本日のお話は「Linuxコマンド・シェルスクリプトを体現せよ!」ついての考察です。

時代の傍観者、宴会部長、マカ―の生き残り、生きた化石のいしたんがお届けする、ラジオラジオ番組「テックチューン」の時間です。 テックチューンは最近話題のテクノロジーや私の身の回りで起こったこと、興味があることなどをお話する番組です。 特に目標もなく無気力に続けていきたいと思いますので、お付き合いいただけますと幸いです。

はじめに

プログラマーたるものLinuxのコマンドを使ったことはあると思いますが、シェルスクリプトを組んだことがる人はどれぐらいいますでしょうか。シェルスクリプトといっても、ただの一連のバッチ処理ではなく、分岐やループがあってファイル処理を行ったりするものです。

コマンド

$ ls
$ cat
$ rm
$ cd
など対話型でシェルの状態を変更したりファイルを処理するものです。
手で入力してその場で処理結果が分かるものになっています。
複数の処理を行う場合はその都度コマンドを入力したり、ワイルドカードを使用して複数のファイルを指定します。
  • その場で処理を実行させることができる。
  • 結果に応じて次の処理を検討できる。

シェルスクリプト

コマンドに対して、コマンドを一連の処理にしたものをシェルスクリプトと呼び、いつも同じ作業をする場合はスクリプト化した方が便利です。
  • 同じことを自動実行できる。
  • 意外と出来なさそうなことがコマンドレベルでできる。
  • その為のコマンドが用意されている。
  • 夜間に行いたいことなどをスクリプト化

なぜシェルスクリプトを使うのか

シェルスクリプトを使用する最大のメリットとして、他に余計なプログラミング言語などをインストールしなくても良い点です。
処理を自動化したい場合に手っ取り早く思いつくのが、PHPやPerlでプログラムを書いた方が、ファイルの入出力や文字列の切り出し、正規表現などのパターンマッチングをするのに細かく対応できるため便利そうです。しかも自分が慣れている言語だとなおさらです。
ただ、それが限られた環境だとどうでしょうか。例えば踏み台サーバーで処理を実行したい場合や、本番環境で処理を実行しなければならない場合などです。
踏み台サーバーは、踏み台にしか使わない為、awsのmicroインスタンスでディスク容量も小さい構成となっています。ここにPHPをインストールするのは容量的にも過酷ですし、セキュリティホールの原因にもなりかねません。
また、本番環境などでは新しいモジュールをインストールする際に手順書作成やリリース判定などが必要な場合もあります。
こうなるとシェルスクリプトで実装した方がよさそうです。
全ての処理をシェルスクリプトでやった方がいいというわけではなく、シェルスクリプトを作れれば仕事の幅が広がりますよ、という話しです。

シェルスクリプトの基礎

ではシェルスクリプトを使いこなすにはどのような要素が必要かを見ていきます。
if, for, while ができる。
シェルスクリプトにもプログラミングの制御構造があります。

if文

if 文では変数の比較だけではなく、ファイルの存在や種別の判定もできます。
変数の比較
if [ $TARGET -eq $SOURCE ]; then
#あっているときの処理
else
#違っているときの処理
fi
ファイルの有無
if [ -e /tmp/local.txt ]; then
#ファイルがある場合
fi

for 文

for i in $LIST
do
#ループ処理したい内容
done
$LISTに入っているものを1つずつ処理します。
また、0~10までループする場合は下記の様になります。
(これだと10も含まれる)
for i in  $(seq 0 10)
do
#ループ処理したい内容
done
while文
条件が成立している間だけ実行する
FLAG=1
while [ $FLAG -eq 1 ];
do
#ループしたい内容
done
シェルスクリプトの応用
さらにシェルマスターを目指してシェルスクリプトを極めていくには下記のコマンドの使い方を把握するともっと便利なことができます。

文字の置換

文字を置換したい場合は sed というコマンドで文字列を置き換えることができます。
sed -e ‘s/探す文字列/置き換えたい文字列/g’ file.txt

特定のカラムを取り出す

ログファイルやTSVの特定の項目だけ抜き出したい場合に便利なコマンドが awk です。
いつも aws とタイプミスしがち。
httpd のログからパスだけ抜き出したい場合は下記の通り
awk ‘{ print $7 }’ /var/log/httpd/access_log
変数名は $1 から始まります。
デフォルトのセパレータは’ ‘なのですが、-F オプションでセパレータを変更することができます。

同じコマンドを何回も実行する

ls | xargs -L 1 echo
1行ずつ書かれている物をコマンドに渡すためのコマンドです。
ディレクトリ内にファイルが1万個ある場合に、ファイルを全部消したいときに rm * としたいのですが、コマンドバッファが一杯になって受け付けてくれないのです。
そうした場合に ls | xargs -L 1 rm とやると、1行ずつファイル名を rm に渡してくれます。

シェルでも関数を定義できる

シェルスクリプトで同じ処理やサブルーチン的な処理を実行したい場合に、今まではもう一つシェルスクリプトを作って実行していたのですが、シェルスクリプトでも関数を定義できるという事で早速使ってみました。

今までのボク

for i in  $(seq 0 9)
do
process.sh $i $TARGET $SOURCE
done

これからのボク

function process() {
echo $1  #1番目の引数
echo $2  #2番目の引数
echo $3  #3番目の引数
}
for i in  $(seq 0 9)
do
process $i $TARGET $SOURCE
done
ここまでできればシェルマスターになれそうです。
あとは、 echo -n や sed -n ‘3p’ や grep を使っていろいろ処理すれば、/var/log/httpd/access_log から縦軸にURL一覧、横軸に日付のアクセス集計のファイルを作ることもできてしまいます。
(実際に作った)

シェルスクリプトの落とし穴

シェルスクリプトの実行時パラメータとして引数を10個以上渡したときのシェル内での変数名は ${10} にしないと参照できない
シェルスクリプトの変数は $TARGET と書いてもいいが、正式には ${TARGET} と書くようです。
その為、$10 と書いてしまうと、$1 なのか $10 なのかが分からなくなってしまい、$1 と解釈されるようです。
シェル変数は ${変数名} の癖をつけた方が良いと思います。

実行ディレクトリ

cron から実行した場合などによく起きるのですが、シェルスクリプトのディレクトリを指定せずに実行すると / にログが出力されたりしてビックリすることがあります。
シェルスクリプトが置いてある場所で実行したい場合は下記の様にシェルスクリプトが置かれているディレクトリを取得して移動するのをスクリプトの先頭に書いておいた方が良いと思います。
cd $(dirname $0)

環境変数とパス

これも cron の話しなのですが root で cron を実行すると、最低限の環境変数しか設定されておらず、パスも /bin と /usr/bin にしか通っていなかったりして、痛い目を見ることがあります。
作業していたユーザーと同じような環境変数にするには、シェルスクリプトの先頭で /etc/profile を読み込んでおくと良いでしょう。
source /etc/profile

Mac のシェルスクリプト

macOS のシェルが今までの bash から zsh に変わってしまいました。(Catalina から)
そのため、Linux ように書いたスクリプトはそのままでは動作しないようです。
設定で bash を使用するようにできるので切り替えてしまった方が良いでしょう。さすが UNIX です。

おわりに

シェルスクリプトも使い倒してみると、いろいろ出来そうなことが分かりました。
最低限の環境でも処理を自動化する事ができるので、とても便利です。
シェルスクリプトは意外とシステム運用分野では使われているので習得しておいて損はないでしょう。

投稿者 Yoshiteru Ishida