コマンド実行結果やテキストファイルから特定情報を取得するシェルスクリプト


## はじめに

コマンドの標準出力の結果や、テキストファイルから特定情報を取得するシェルスクリプトを作りました。

- シェルスクリプト①
    - `apt list --installed`コマンドの実行結果から特定情報を抜き出す
- シェルスクリプト②
    - テキストファイルを読み取り特定情報を抜き出す

## 1.実行環境

手っ取り早いLinux環境がラズパイだったので、それを使いました。

```console
$ cat /etc/os-release
PRETTY_NAME="Raspbian GNU/Linux 10 (buster)"
NAME="Raspbian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"
$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:        10
Codename:       buster
$ uname -a
Linux raspberrypi 5.10.63-v7+ #1459 SMP Wed Oct 6 16:41:10 BST 2021 armv7l GNU/Linux
```

## 2.シェルスクリプト①

`apt list --installed`コマンドの実行結果から、パッケージ名とバージョンを抜き出すスクリプトです。取得した情報をcsv出力します。

### 2-1.シェルスクリプト①のソース

```shell
#!/bin/bash

function getInfoFromAptList(){
    while read LINE
    do
        if [[ ! "$LINE" =~ ^[^\x01-\x7E] ]]; then
            list=(${LINE//[ |,]/ })
            #echo "list ${list[@]}"
            #echo "list[0] ${list[0]%/*}"
            #echo "list[3] ${list[2]#*:}"
            echo "${list[0]%/*} , ${list[2]#*:}"
        fi
    done < <($1)
}

getInfoFromAptList "apt list --installed"
```

### 2-2.シェルスクリプト①の実行結果

シェルスクリプト①実行で出力されたcsvファイル

```console:出力されたcsvファイルの一部抜粋
python3.7-venv , 3.7.3-2+deb10u3
python3.7 , 3.7.3-2+deb10u3
python3 , 3.7.3-1
python , 2.7.16-1
qpdfview-djvu-plugin , 0.4.17~beta1+git20180709-rpt1
qpdfview-ps-plugin , 0.4.17~beta1+git20180709-rpt1
qpdfview-translations , 0.4.17~beta1+git20180709-rpt1
qpdfview , 0.4.17~beta1+git20180709-rpt1
qt5-gtk-platformtheme , 5.11.3+dfsg1-1+rpi1+deb10u4
qt5-style-plugins , 5.0.0+git23.g335dbec-2+rpt1
qt5ct , 0.37-1+rpt1
qttranslations5-l10n , 5.11.3-2
raspberrypi-artwork , 20150921
raspberrypi-bootloader , 1.20211007-2~buster
raspberrypi-kernel , 1.20211007-2~buster
raspberrypi-net-mods , 1.3.0
raspberrypi-sys-mods , 20211005
raspberrypi-ui-mods , 1.20210706
raspbian-archive-keyring , 20120528.2
raspi-config , 20210212
raspi-copies-and-fills , 0.13
raspi-gpio , 0.20191001
raspinfo , 20190624-1
rc-gui , 1.46
read-edid , 3.0.2-1
readline-common , 7.0-5
realvnc-vnc-server , 6.7.2.42622
```

### 2-3.補足

スクリプトの説明をちょっとだけ

---

```shell
if [[ ! "$LINE" =~ ^[^\x01-\x7E] ]]; then
```

全角文字列から始まる行を除外しています。
`[^\x01-\x7E]` が全角文字列を示す正規表現(らしい)です。
これは、出力されたメッセージからパッケージ,バージョン情報を表示しない行を除外するための処理です。
具体的には、`apt list --installed`コマンド実行時に `一覧表示... 完了` というメッセージが表示される行を除外します。

---

```shell
function getInfoFromAptList(){
    while read LINE
    do
        echo "${LINE}"
    done < <($1)
}

getInfoFromAptList "apt list --installed"
```

`apt list --installed` コマンドの実行結果の1行ごとに処理するループです。処理する行の文字列は変数 `LINE` に入っています。

---

```shell
list=(${LINE//[ |,]/ })
```

変数 `LINE`内の文字列の半角空白と `,`(コロン) を半角空白に置換します。
置換した後の変数 `LINE`内の文字列を半角空白で区切り、配列としてlistに格納します。

---

```shell
echo "${list[0]%/*} , ${list[2]#*:}"
```

配列`list[x]`の情報を表示しています。表示する際、不要な文字列を削除しています。

- `${list[0]%/*}`：後方から文字列`/`までを削除
- `${list[2]#*:}`：前方から文字列`:`までを削除

例)
文字列が `openssh-client/testing,now 1:7.9p1-10+deb10u2+rpt1 armhf [インストール済み、自動]` の場合、配列に格納される文字列と正規表現の処理後の文字列は以下となります。

- 文字列
    - `openssh-client/testing,now 1:7.9p1-10+deb10u2+rpt1 armhf [インストール済み、自動]`
- 配列
    - `${list[0]}` : `openssh-client/testing`
    - `${list[2]}` : `1:7.9p1-10+deb10u2+rpt1`
- 正規表現の処理後
    - `${list[0]%/*}`：`openssh-client`
    - `${list[2]#*:}`：`7.9p1-10+deb10u2+rpt1`

---

```
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
```

スクリプトを実行すると上記の警告メッセージが表示されますが今回は無視しました。以下サイトによると`apt`コマンドをスクリプトで使用しない方が良いそうです。

https://wp.tekapo.com/2019/07/15/difference-between-apt-and-apt-get/


## 3.シェルスクリプト②

テキストファイルから特定情報を抜き出しcsvファイルとして出力するシェルスクリプトです。

### 3-1.シェルスクリプト②のソース

```shell
#!/bin/bash

declare -a pInfArray=()
today=`date "+%Y-%m-%d_%H-%M"`

function getInfoFromTxt(){
    while read LINE
    do
        if [[ ! "$LINE" =~ ^Installed ]]; then
            #echo ${LINE}
            list=(${LINE// .*/ })
            #echo "list[0] ${list[0]%.*}"
            #echo "list[1] ${list[1]#*:}"
            pInfArray=("${pInfArray[@]}" "${list[0]%.*},${list[1]#*:}" )
        fi
    done < $1
}

getInfoFromTxt "packagelist.txt"

#i=0
for e in ${pInfArray[@]}; do
    #echo "pInfArray[$i] = ${e}"
    #let i++
    echo "${e}" >> packagelist_${today}.csv
done
```

シェルスクリプト②が読み込むテキストファイル`packagelist.txt`

```text:packagelist.txt
Installed Packages
bind-libs.x86_64                    32:9.8.2-0.17.rc1.el6_4.6              @updates
bind-utils.x86_64                   32:9.8.2-0.17.rc1.el6_4.6              @updates
Available Packages
bind.x86_64                         32:9.8.2-0.23.rc1.el6_5.1              updates
bind-chroot.x86_64                  32:9.8.2-0.23.rc1.el6_5.1              updates
bind-devel.i686                     32:9.8.2-0.23.rc1.el6_5.1              updates
bind-devel.x86_64                   32:9.8.2-0.23.rc1.el6_5.1              updates
```

### 3-2.シェルスクリプト②の実行結果

シェルスクリプト②実行で出力したcsvファイル

```text:packagelist_2021-10-25_06-56.csv
bind-libs,9.8.2-0.17.rc1.el6_4.6
bind-utils,9.8.2-0.17.rc1.el6_4.6
Available,Packages
bind,9.8.2-0.23.rc1.el6_5.1
bind-chroot,9.8.2-0.23.rc1.el6_5.1
bind-devel,9.8.2-0.23.rc1.el6_5.1
bind-devel,9.8.2-0.23.rc1.el6_5.1
```

## おわりに

CentOSでも似たようなシェルスクリプトを書いたのですが記事が長くなったので、それは別途投稿します。