大量のrpmファイルからインストールされていない依存関係にあるrpmファイルを抽出するbashスクリプト


## 経緯
yumを使用できないサーバにrpmでパッケージをインストールする作業が最近増えてきた。
OSのインストールディスク(/Packagesディレクトリ)にある大量のrpmファイルから必要なファイルを抽出し、rpmコマンドでインストールするという作業が多い。

rpmを使用したインストールは、毎回依存関係に苦しめられる。これのせいでヘタすると1日がかりになってしまうこともある。
yumだと5分もかからない作業なので、アホらしくなってくる。

今後も何回か発生しそうな作業なので、bashスクリプトを作成して作業時間の短縮を試みる。

*[2015/12/27 追記]
iwaim@github様より、yumコマンドでもOSのインストールディスクからインストールできることを教えていただきました。
※下記コメント参照。
こちらの方もご覧ください。*

## 内容
作成するスクリプトの動作は以下の通り。

1. 検索用辞書作成
    事前に検索用の辞書ファイルを作成する。
    大量のrpmファイルから「パッケージ名」「含まれているファイル名」を抽出し、ファイルに出力する。

1. インストールされていない依存関係にあるrpmファイル名抽出
	インストールするrpmファイル名と検索用辞書を元に、インストールされていない依存関係にあるrpmファイル名を抽出する。

1. rpmインストールコマンド文字列生成
    インストールされていない依存関係にあるrpmファイルを含めたインストールコマンド文字列を生成する。

    ``` bash:インストールコマンド文字列のフォーマット
    rpm -ivh <rpmファイル名> [<rpmファイル名2>...]
    ```
        
    例）

    ``` bash:
    rpm -ivh xorg-x11-utils-7.5-6.el6.x86_64.rpm libX11-1.5.0-4.el6.x86_64.rpm ...
    ```

## スクリプト
OSのバージョンは以下の通り。

``` text:
Red Hat Enterprise Linux Server release 6.4 (Santiago)
```

### 検索用辞書作成

``` bash:
# ----------------------------------------------------------------------------------------------
# Name     : create_rpmlist
# Purpose  : rpm依存ファイル検索で使用する辞書を作成する。
# Usage    : create_rpmlist
# Return   : 
#   _DEPENDENCY_INFO_FILE変数で指定したファイルに辞書を出力する。
#   辞書のフォーマットは以下の通り。(空白区切り)
#   "パッケージ名" "rpmファイル名" "rpmファイルに含まれるファイル名"
#     例)
#       …（省略）…
#       gcalctool gcalctool-5.28.2-3.el6.x86_64.rpm /usr/share/omf/gcalctool/gcalctool-zh_HK.omf
#       gcalctool gcalctool-5.28.2-3.el6.x86_64.rpm /usr/share/omf/gcalctool/gcalctool-zh_TW.omf
#       gcc gcc-4.4.7-3.el6.x86_64.rpm /usr/bin/c89
#       gcc gcc-4.4.7-3.el6.x86_64.rpm /usr/bin/cc
#       …（省略）…
# Example  : create_rpmlist /tmp/_rpmlist.txt
# Remarks1 :
#   _DEPENDENCY_INFO_FILE変数を必ず設定すること。
#   例) _DEPENDENCY_INFO_FILE=/tmp/_rpmlist.txt
# Remarks2 : i686の情報は格納しない。
# Remarks3 : 
#   rpmファイル名からパッケージ名を取得する処理について。
#   "rpm -qip"を使用し詳細情報を取得後、"Name        : "で始まる行の3フィールド目を取得する。
#   実行例）
#     # rpm -qip gcc-4.4.7-3.el6.x86_64.rpm
#     …（省略）…
#     Name        : gcc                          Relocations: (not relocatable)
#     Version     : 4.4.7                             Vendor: Red Hat, Inc.
#     Release     : 3.el6                         Build Date: 2012年10月19日 02時33分21秒
#     …（省略）…
# ----------------------------------------------------------------------------------------------
function create_rpmlist(){
  for fname in *.rpm ; do
    pname=$(rpm -qip "${fname}" 2>&1 | grep 'Name        :' | sed -e 's/  */ /g'  | cut -d ' ' -f3)
    for line in $(rpm -qpl "${fname}"); do
      echo "$pname $fname $line"
    done
  done 2> /dev/null | grep -v '.i686.' > "${_DEPENDENCY_INFO_FILE}"   # 今回のマシンはi686ではないので除外する。 
  echo "ListFile creation completion!"
}

```

### インストールされていない依存関係にあるrpmファイル名抽出

``` bash:
# ----------------------------------------------------------------------------------------------
# Name     : search_dependent
# Purpose  : インストールされていない依存しているrpmファイルを検索する。
# Usage    : search_dependent <rpmfile> [<rpmfile2> ...]
# Return   : インストールされていない依存しているrpmファイル名のリスト（空白区切り）
# Example  : 
#   search_dependent gcc-4.4.7-3.el6.x86_64.rpm libstdc++-devel-4.4.7-3.el6.x86_64.rpm
#     ↓結果例
#   cloog-ppl-0.15.7-1.2.el6.x86_64.rpm cpp-4.4.7-3.el6.x86_64.rpm glibc-devel-2.12-1.107.el6.x86_64.rpm
# Remarks1 : 
#   _DEPENDENCY_INFO_FILE変数を必ず設定すること。
#   例) _DEPENDENCY_INFO_FILE=/tmp/_rpmlist.txt
# Remarks2 : 事前に辞書を作成すること。(create_rpmlistを使用)
# Remarks3 : 
#   依存性確認の処理について
#   "rpm -ivh --test 〜"を使用して依存を確認する。依存する「パッケージ名」または
#   「rpmに含まれるファイル名」が出力されるが、結果だけではどちらか判断できない。
#   結果例)
#     gcc = 4.4.7-3.el6 is needed by gcc-c++-4.4.7-3.el6.x86_64               ★パッケージ名
#     libmpfr.so.1()(64bit) is needed by gcc-c++-4.4.7-3.el6.x86_64           ★rpmに含まれるファイル名
#     libstdc++-devel = 4.4.7-3.el6 is needed by gcc-c++-4.4.7-3.el6.x86_64   ★パッケージ名
#   rpm実行時にエラーが出るだけで大事にはいたらないので、複雑な処理は避けたい。
#   以下を仕様とする。
#   - 調査対象の文字列を、まず「パッケージ名」として検索する。存在しない場合は
#     「rpmに含まれるファイル名」として検索する。
#   - 「rpmに含まれるファイル名」にも関わらずパッケージ名でヒットしてしまう可能性が
#     あるが、仕様とする。
# ----------------------------------------------------------------------------------------------
function search_dependent(){
  local rpmfiles="$@"
  local keywords=$(LANG=C rpm -ivh --test ${rpmfiles} 2>&1 | grep ' is needed by ' | sed -e 's/^\s*//g' -e 's/(.*)//g' | cut -d ' ' -f1)
  local pnames=$(for keyword in ${keywords}; do
    # 「パッケージ名」よりrpmファイル名を抽出
    pnames=($(cat "${_DEPENDENCY_INFO_FILE}"  | awk '{ if($1 == "'"${keyword}"'") print $2; }' ))
    if [ "${#pnames[@]}" -eq 0 ]; then
      # 「rpmに含まれるファイル名」よりrpmファイル名を抽出
      pnames=($(cat "${_DEPENDENCY_INFO_FILE}"  | awk '{ if($3 ~ /'"${keyword}"'/) print $2; }'))
    fi
    echo "${pnames}"
  done | sort | uniq )
  echo ${pnames}  # 改行を消す
}
```

### rpmインストールコマンド文字列生成

``` bash:
# ----------------------------------------------------------------------------------------------
# Name     : create_rpminstallcmd
# Purpose  : rpmインストールコマンド文字列作成
# Usage    : create_rpminstallcmd <rpmfile> [<rpmfile2> ...]
# Return   : rpmインストールコマンド文字列
# Example  : 
#   create_rpminstallcmd gcc-c++-4.4.7-3.el6.x86_64.rpm
#     ↓結果例
#   rpm -ivh gcc-4.4.7-3.el6.x86_64.rpm libstdc++-devel-4.4.7-3.el6.x86_64.rpm …（省略）… glibc-devel-2.12-1.107.el6.x86_64.rpm
# Remarks1 : 
#   _DEPENDENCY_INFO_FILE変数を必ず設定すること。
#   例) _DEPENDENCY_INFO_FILE=/tmp/_rpmlist.txt
# Remarks2 : 事前に辞書を作成すること。(create_rpmlistを使用)
# ----------------------------------------------------------------------------------------------
function create_rpminstallcmd(){
  local rpmfiles="$@"
  local depfiles=''

  # "30"は依存ファイルの依存を調査する回数。
  # "while :"を使用した場合無限ループになる可能性があるため、forで制限を設定した。
  # 依存が30層を上回ることはまずないだろう。
  for (( i=0; i<30; ++i )) 
  do
    depfiles=$(search_dependent "${rpmfiles}")
    [ "${#depfiles}" -eq 0 ] && break
    rpmfiles="${rpmfiles} ${depfiles}"
  done
  echo "rpm -ivh ${rpmfiles}"
} 
```

## 実行

``` bash:
# OSインストールディスクマウント
su -
mount -r -t iso9660 /dev/cdrom /media

# rpmファイルが置いてあるディレクトリに移動
cd /media/Packages

# 一時ファイルの設定
_DEPENDENCY_INFO_FILE=/tmp/_rpmlist.txt

# 検索用辞書ファイルを作成
create_rpmlist 

# rpmインストールコマンド作成スクリプト実行１
create_rpminstallcmd unzip-6.0-1.el6.x86_64.rpm
        # rpm -ivh unzip-6.0-1.el6.x86_64.rpm

# rpmインストールコマンド作成スクリプト実行２
create_rpminstallcmd xorg-x11-utils-7.5-6.el6.x86_64.rpm
        # rpm -ivh xorg-x11-utils-7.5-6.el6.x86_64.rpm libX11-1.5.0-4.el6.x86_64.rpm libXext-1.3.1-2.el6.x86_64.rpm libXi-1.6.1-3.el6.x86_64.rpm libXinerama-1.1.2-2.el6.x86_64.rpm libXrender-0.9.7-2.el6.x86_64.rpm libXtst-1.2.1-2.el6.x86_64.rpm libXv-1.0.7-2.el6.x86_64.rpm libXxf86dga-1.1.3-2.el6.x86_64.rpm libXxf86misc-1.0.3-4.el6.x86_64.rpm libXxf86vm-1.1.2-2.el6.x86_64.rpm libdmx-1.1.2-2.el6.x86_64.rpm libxcb-1.8.1-1.el6.x86_64.rpm libX11-common-1.5.0-4.el6.noarch.rpm libXau-1.0.6-4.el6.x86_64.rpm

# 複数のrpmインストールコマンドを生成し、まとめて実行。
(
create_rpminstallcmd unzip-6.0-1.el6.x86_64.rpm 
create_rpminstallcmd gcc-4.4.7-3.el6.x86_64.rpm
create_rpminstallcmd gcc-c++-4.4.7-3.el6.x86_64.rpm
create_rpminstallcmd binutils-2.20.51.0.2-5.36.el6.x86_64.rpm
) > /tmp/temp.sh
sh /tmp/temp.sh

```


## メモ
### rpmファイル名から詳細情報を取得する。
**"rpm -qip 〜"**を使用する。

▼コマンド

``` bash:
rpm -qip gcc-4.4.7-3.el6.x86_64.rpm
```

▼ 結果例

``` text:
警告: gcc-4.4.7-3.el6.x86_64.rpm: ヘッダ V3 RSA/SHA256 Signature, key ID fd431d51: NOKEY
Name        : gcc                          Relocations: (not relocatable)
Version     : 4.4.7                             Vendor: Red Hat, Inc.
Release     : 3.el6                         Build Date: 2012年10月19日 02時33分21秒
Install Date: (not installed)               Build Host: x86-001.build.bos.redhat.com
Group       : Development/Languages         Source RPM: gcc-4.4.7-3.el6.src.rpm
Size        : 19404986                         License: GPLv3+ and GPLv3+ with exceptions and GPLv2+ with exceptions
Signature   : RSA/8, 2012年12月05日 18時55分12秒, Key ID 199e2f91fd431d51
Packager    : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
URL         : http://gcc.gnu.org
Summary     : Various compilers (C, C++, Objective-C, Java, ...)
Description :
The gcc package contains the GNU Compiler Collection version 4.4.
You'll need this package in order to compile C code.
```

### rpmコマンドでインストールせずに依存性を確認する。
**"rpm -ivh --test 〜"**を使用する。

▼コマンド

``` bash:
rpm -ivh --test gcc-4.4.7-3.el6.x86_64.rpm
```

▼ 結果例
※「依存性の欠如」には、「パッケージ名」と「rpmに含まれるファイル名」が表示されている。その他に種類があるのかどうかは不明。

``` text:
警告: gcc-4.4.7-3.el6.x86_64.rpm: ヘッダ V3 RSA/SHA256 Signature, key ID fd431d51: NOKEY
エラー: 依存性の欠如:
	cloog-ppl >= 0.15 は gcc-4.4.7-3.el6.x86_64 に必要とされています
	cpp = 4.4.7-3.el6 は gcc-4.4.7-3.el6.x86_64 に必要とされています
	glibc-devel >= 2.2.90-12 は gcc-4.4.7-3.el6.x86_64 に必要とされています
	libgomp = 4.4.7-3.el6 は gcc-4.4.7-3.el6.x86_64 に必要とされています
	libgomp.so.1()(64bit) は gcc-4.4.7-3.el6.x86_64 に必要とされています
```