2014年4月14日月曜日

シェルスクリプト初心者の私がまず覚えたこと9つ

今年の4月にシェルスクリプトを書く機会がありました。今までシェルを使った経験は多少あったものの、シェルスクリプトを書くのは初めてでした。シェルスクリプトを書くにあたり、まず抑えたことを紹介します。

  1. 1行目のおまじない
  2. 変数の使い方
  3. 関数の使い方
  4. 制御文
  5. バッククォート
  6. 終了ステータス
  7. 設定ファイル
  8. 文字列操作
  9. 文字列のエスケープ
それぞれについて以下に記載します。
1行目のおまじない
シェルスクリプトの戦闘には、インタプリタを指定する#!で始まる行を書きます。
#!/bin/sh

変数の使い方
変数を定義するには、変数名=値と書きます。=の前後に空白を含めてはいけません。参照するときは変数名の先頭に$をつけます。
VARIABLE=VALUE
echo $VARIABLE
ただし、この書き方ではスコープが広くなってしまいます。グローバル変数は原則的に避けるべきなので、「初期化時以外は書き換えない」というルールを決めました。
書き換えが必要な変数は、次に紹介する関数の内部に定義しました。

関数の使い方
関数が定義できれば、プログラムの構造化ができます。関数は次のように定義します。
funcName ()
{
    someCommand1
}
localコマンドを使えば、関数でのみ参照できるローカル変数を定義できます。
local localVar=1

制御文
制御分は4種類押さえました。if/case/for/whileです。
if文は条件によって処理を分岐するときに使用します。
if [ someCommand ]
then
    echo TRUE
else
    echo FALSE
fi

case文はマッチングによって処理を分岐するのに適しています。マッチングを突き詰めると大変です。とりあえず"ワイルドカードが使える"程度の認識です。
case $someVar in
    case1) case1Command ;;
    case2) case2Command ;;
    *) defaultCommand ;;
esac

forとwhileはどちらも繰り返しです。
forは特に、引数を列挙するのに適しています。
for var
do
    someCommand $var
done

一方whileは、条件によってループを継続するかどうか判定する処理に適しています。
while [ someCondition ]
do
    someCommand
done

バッククォート
バッククォート(`)でコマンドを囲むと、その部分が実行時にコマンドの標準出力と置き換えられます。
つまり、
echo Hello
と、
echo `echo Hello`
は、ほぼ同じです。

終了ステータス
一般的に終了ステータスは0が正常、それ以外が異常となっています。
直前に実行したコマンドの終了ステータスは、?変数にセットされます。
someCommand
if [ $? == 0 ]
then
    echo SUCCESS
else
    echo FAIL
fi
終了ステータスを返すには、exitコマンドを使用します。
exit 0
関数から返すには、returnコマンドを使用します。
return 0
設定ファイル
設定ファイルを扱う機能は見つけられませんでした。
代わりとして、変数を定義するスクリプト(A)と、処理を記述するスクリプト(B)に分割し、BからAを読み込むことで、設定ファイル相当の機能を実現しました。
#!/bin/sh
# A.sh
GREETING="Hello World"
#!/bin/sh
# B.sh
. ./A.sh
echo $GREETING

文字列操作
文字列の結合は、普通に並べるだけです。
GREETING=Hello
GREETING=$GREETING" World"
文字列の切り出しには、sedコマンドを使用します。
echo $GREETING | sed -e "s/Hello //"

文字列のエスケープ
文字列をエスケープするには、\、'、"を使います。詳しく説明するのはたいへんなので、ここでは例を示すのみとします。
$ VAR=variable; echo "$VAR"
variable
$ VAR=variable; echo '$VAR'
$VAR
$ VAR=variable; echo \$VAR
$VAR

以上を踏まえれば、少なくとも動くものは書けると思います。
しかし。
書いてみて思いましたが、シェルスクリプトは使いにくいと思いました。例えば、次のプログラムは常にSUCCESSが出力されます。
funcReturn2 ()
{
    return 2
}
funcReturn2
if [ $? == 0 ] || [ $? == 1 ]
then
    echo SUCCESS
else
    echo FAIL
fi
こういう結果になる理由については、ご自身で調べてみてください。

参考
Man page of BASH

0 件のコメント:

コメントを投稿