ラァンフォーヱヴアー

出先・常駐先用コマンドリスト。2020年からリモートワークなので常駐なくなったけど。その他思ったことメモ。

シェルスクリプトのファイルを分割せずに済む方法をまとめてみた(Linux)

はじめに

シェルスクリプトで困るのが入力に癖のあるコマンド。
別のプロンプトを起動して操作する形式だったり、対話形式が必須だったり、ファイル指定形式でないと受け付けなかったり。
(SQL*Plusなどが典型的。sqlplus user/password @hogehoge.sqlみたいな)

そのようなコマンドを含むシェルスクリプトを1ファイルにまとめる方法をまとめてみた。多くの環境で動くと思うけど一応Bash念頭。

あるコマンドの標準出力を別の標準入力にする場合

これはご存知の皆様も多いかと。まとめて示す。

  • パイプ(pipe): あるコマンドの次にパイプライン(|←コレ)を置き、続けてコマンドを置くと前のコマンドの標準出力を後のコマンドの標準入力に渡すことができる。
    書き方としてはこんな感じ。
outputcommand | inputcommand

具体例としては

$ echo -e "hoge\nhuga" | grep "hoge"
hoge
  • リダイレクト (redirect): あるコマンドの次に小なり記号(<←コレ)を置き、続けてファイルを置くと後のファイルを前のコマンドの標準入力に渡すことができる。 書き方としてはこんな感じ。
command < file

具体例としては

$ cat test.txt
hoge
huga
$ grep hoge < test.txt
hoge

別プロンプトがシェルからの入力を受け付ける場合(SQL*Plusなど)

ヒアドキュメント(Here document)を使う。リダイレクトの複数行版みたいなもの。
「<<ある文字列」から「ある文字列」の間までの改行を含む文字列を標準入力に流し込むことができる。「ある文字列」はなんでもいい。

これが威力を発揮するのは例えばSQL*Plusみたいな「制御のためにOSのシェルではないプロンプトを起動する」ケース。
例えばOracleDBの起動シェルスクリプトを作成する場合、以下のようにstartdb.sh、startdb.sqlの2ファイルを用意する必要がある。
ただ数行レベルかつDBを起動するという目的が同じスクリプトをわざわざ分けたくないと考える。

% cat startdb.sh
#!/bin/sh
sqlplus / as sysdba
exit 0

% cat startdb.sql
startup
quit

よってヒアドキュメントを使用する。具体例を以下に示す。ここでは「ある文字列」をHEREとしている。先に説明した通りどんな文字列でもいい。

% cat startdb1.sh
#!/bin/sh
sqlplus / as sysdba  <<HERE
startup
quit
HERE
exit 0

これで1ファイルのままstartdb.sh、startdb.sql分の働きをすることができる。
なおヒアドキュメントの詳細は以下のとおり。

  • <<HERE: 文字列の解釈はシェルに順ずる。つまりヒアドキュメント内に$hogeという文字列があればそれはシェル関数であり、hoge関数内の文字列に置き換わる。
  • <<'HERE': 文字列をそのまま扱う。つまりヒアドキュメント内に$hogeという文字列があればそのまま$hogeという文字列として渡される。
  • <<(タブ文字)HERE: 行頭のタブ文字は無視する。インデントをしている場合に適する。

対話形式コマンドを自動入力する場合

商用ソフト等でまれにある対話形式が必須で、かつパイプやリダイレクト(ヒアドキュメント)を受け付けないようなコマンドを自動化したいケース。
コマンドexpectコマンドを使う。メジャーなdistroで入ってるイメージないので追加インストールが必要になるかも。
使い方は説明長くなりそうなので省略する。気が向いたら追記するかも。

echoコマンドの結果をファイルとして扱う(プロセス置換)

ややマニアックな方法?かも。あるコマンドの結果をファイルとして扱う方法。
ちょっとややこしいので具体例を示す。

$ cat read_test.sh
#!/bin/sh

while read line
do
  echo $line
done < $1

exit 0
$ cat file.txt
aaa
bbb
ccc
$ ./read_test.sh file.txt
aaa
bbb
ccc
$ ./read_test.sh <(echo -e "aaa\nbbb\nccc")
aaa
bbb
ccc
$

説明を加える。
ファイルを読み込むコマンド(read_test.sh)を呼び出す処理をしたい(./read_test.sh file.txt)が、 そのためにわざわざ別ファイル(file.txt)をつくりたくないとする。
その場合はファイルの中身に相当する処理をechoコマンド結果として実行している。

さいごに

これまでシェルスクリプトをX年書いてきてぶつかったパターンはだいたいこんな感じ。
特にラスト2つは結構調べた記憶があるので今後の備忘録。