入門UNIXシェルプログラミング―シェルの基礎から学ぶUNIXの世界 | ブルース・ブリン, Bruce Blinn, 山下 哲典 |本 | 通販 | Amazonを読んで、UNIXシェルスクリプトの基本文法を学びましたので、メモ書き程度にまとめておきます。
CやJava、PHPなどのALGOL系言語と似たような部分や、Rubyに似たような部分もあって混乱しそうなので、まとめておきたくなりました。
まだ読み終わってないので、随時追記していきたいと思います。また、誤りがある恐れがあります。
前提:シェルスクリプトは、シェルコマンドの集合である
当然ですが、シェルスクリプトは、シェルコマンドの集合です。そのため下記のような違いがあります。
- 半角スペースの扱いが通常の言語と多少違います。(例えばスペースがあれば別のパラメータとして認識されてしまいます。)
- 途中で失敗すると停止します
- シェルによって動作が異なる
作成と実行
普通のテキストファイルにコマンドを書き連ねて、.sh
などの拡張子で保存します。たとえば、/home/user
というディレクトリに、test.sh
という名前で保存したとすると、
$ pwd #=> 現在のディレクトリを表示するコマンド
/home/user
$ sh ./test.sh #=> シェルを用いて、現在のディレクトリのtest.shを実行
などで実行することが出来ます。
宣言
HTMLやXMLなどで当該ファイルがどのようなフォーマットか宣言することで正確に読み取れるようにすることがありますが、シェルスクリプトでも似たようなことがあります。
ファイルの先頭に
#!/bin/sh
と記載するのがそれです。ここでは、デフォルトのシェル(Bシェル)を指定しています。上述したように、シェル毎にコマンドの動作が異なりますので、この宣言をすることで実行するシェルを指定し、間違いなく動くようにしています。(なお、Ubuntuなどでは、/bin/sh
がBashに置き換えられているのに注意。またBashもバージョンにより動作が異なるので注意。)
コメント
コメントは、#
から行の終わりまでになります。
#!/bin/sh
# ここがコメント
echo "test" # 行の途中からでもコメント可能
改行かセミコロンか
改行でコマンドの終わりを表します。コマンドなのでEnterキーで実行されるわけです。
他方でプログラむ言語のように、;
で区切ることも可能です。
なお、\
(バックスラッシュ)で行を続けて複数行のコマンドを実行できます。
$ echo Hello \
> World. #=> バックスラッシュで終わらせると続きを聞いてくれる
Hello World.
つまり、改行コードをエスケープしているということですね。
セミコロンは、下記のような感じです。
$ echo Hello; echo World.
Hello
World.
エスケープ
上述しましたが、エスケープは\
で行います。
$ FOO=bar #=>後述しますが、FOOという名前の変数にbarという文字列を格納しました
$ echo $FOO #=>変数を指定するには`$`(ドルマーク)を利用します。
bar #=>(使用時はPHPと同じですが、PHPと違い、変数の定義時に`$`は不要です。)
$ echo \$FOO
$FOO #=>ドルマーク部分がエスケープされ「ただのドルマーク」となり、
#=>変数では無くなります。
'
(シングルクオーテーション)と"
(ダブルクオーテーション)
クオーテーションで囲うとそこが一単位として扱われます。
$ ls abc xyz #=>abcとxyzという2つのファイルに対してコマンドが実行される
abc xyz
$ ls "abc xyz" #=>「abc xyz」という名前の1ファイルに対してコマンドが実行される
abc xyz
シングルクオーテーションとダブルクオーテーションの違いは、PHPやRuby(の式展開)と似ていて、中に入れた変数が展開されるか否かという違いがあります。
$ FOO=bar
$ echo $FOO
bar
$ echo "$FOO"
bar #=>ダブルクオーテーションで囲うと変数が展開される
$ echo '$FOO'
$FOO #=>シングルクオーテーションで囲うとそのままその文字列が扱われる
バッククオート `
(逆アポストロフィとも)
シングルクオート('
)とは違います。見た目が似てますし、フォントの種類やサイズによっては見分けがつかないかもしれないのでご注意ください。日本語配列キーボードだと@キー
をshift押しながら押すと出てきます。
このバッククオート `
(逆アポストロフィとも)は、囲んだコマンドが実行され、その場所に結果が書き込まれます。
$ echo "Today is `date`"
Today is 2016年 10月18日 火曜日 22時36分18秒 JST #=>私の手元のMacの結果
また、エスケープとダブルクオーテーションとを組み合わせて下記のような表現も可能です。
$ echo "abc \`echo def \` ghi"
abc `echo def` ghi
testコマンド
test
というコマンドがあります。これは、条件式の真偽を判定するものです。
使用法は、test 条件式
となります。
$ test "a" = "a"
#=> 何も表示されませんが、「真」が返ります
これだけではあまり意味が無く、if
などと組み合わせて利用することになります。
#!/bin/sh
if test "a" = "a" #=> if文の使い方は後述
then
echo "a文字列とa文字列は等しい"
fi #=> if文を閉じるのは`fi`です。(ifの逆さまです)
なお、testコマンドは、以下の省略式も可能です。
#!/bin/sh
if [ "a" = "a" ]
then
echo "a文字列とa文字列は等しい"
fi
なお、then
の前で改行しているのは意味があります。もし改行しないのであれば、以下のようにしなければなりません。
#!/bin/sh
if [ "a" = "a" ]; then
echo "a文字列とa文字列は等しい"
fi
これは、[ ] の中身が、testコマンドで、そこで1回コマンドの実行を終わらせないといけないからということかと思います。
その他testコマンドの用例は、下記を参照
testコマンド参考リンク
真と偽について
シェルスクリプトでは、**0は、真(true)**であり、**0以外は、偽(false)**になります。
多くのプログラミング言語とは真逆ですので、ご注意ください。
コマンドのバックグランド起動(&
)
&
をつけることで、コマンドをバックグラウンドで起動できます。
$ sleep 60 & #=> 60秒停止するコマンド
[1]8012
$ #=> 次のコマンドを受け付けてくれる
バックグラウンドで起動しているアプリについては、job
コマンドで見ることが出来ます。
$ sleep 60 & #=> 60秒停止するコマンド
[1]8012
$ jobs
[1]+ Running sleep 60 &
wcコマンド
wc
コマンドは、標準入力から渡された文字数をカウントするものです。word countだと思います。
$ wc(Enter) #=> wcコマンドを単体で実行すると、入力画面になる
abc(Enter) #=> abcとここでは入力
(Ctrl + d) #=> ctrlを押しながらdキーでwcコマンドを終了
1 1 4 #=> 1行1単語4文字(バイト)(改行コード含む)
パイプ(|
)
パイプ(|
)は、あるコマンドの実行結果を他のコマンドに渡すことが出来る文法となります。
上記wc
コマンドをパイプを利用して書くと、
$ echo abc | wc
1 1 4
のようにすることが出来ます。
これだけだとあまり意味があるようには思えませんが、このパイプや後述のリダイレクトのようなシンプルな仕組みが、小さいアプリケーションを組み合わせて使うというCUI、ひいてはUNIX的な文化・発展を生みました。
AND (&&
) / OR(||
)
AND (&&
)
他のプログラミング言語と同じような利用法です。
#!/bin/sh
if [ "a" = "a" ] && [ "b" = "b" ]; then
echo "aはaだし、bはbです。"
fi
上記スクリプトを実行すれば、echo "aはaだし、bはbです。"
とコンソールに表示されます。
&&
の前の式が偽であれば、後の式は評価されず、全体を偽として進みます。
逆に&&
の前の式が真であれば、後の式も評価され、真であれば全体を真として進みます。
(※ここらへんの表現は説明として正確では無いかもしれませんが、わかりやすさを重視しています。)
OR(||
)
#!/bin/sh
if [ "a" = "a" ] || [ "b" = "b" ]; then
echo "aはaと言えるか、もしくはbはbと言えるかです。"
fi
上記スクリプトを実行すれば、echo "aはaと言えるか、もしくはbはbと言えるかです。"
とコンソールに表示されます。
||
の前の式が真であれば、後の式は評価されず、全体を真として進みます。
逆に||
の前の式が偽であれば、後の式が評価され、後の式が真であれば全体を真として進み、偽であれば、全体を偽として進みます。
(※ここらへんの表現は説明として正確では無いかもしれませんが、わかりやすさを重視しています。)
if文 ( if then elif else fi )
if構文です。他のプログラミング言語と似ていますがやや違います。
#!/bin/sh
if [ "a" = "b" ]; then #=> thenの前に改行かセミコロンが必須
echo "aとbはイコール"
elif [ "c" = "d" ]; then #=> else if / elsif(Ruby) と同等
echo "cとdはイコール"
else
echo "aとbがイコールなわけないし、cとdもイコールなわけない"
fi
入れ子(ネスト)も可能です。
#!/bin/sh
if [ "a" = "a"]; then
if[ "b" = "b" ]; then
echo "aはaで、bはbです。"
fi
fi
for文
for
は、これも他の言語と同じく繰り返し構文になります。
#!/bin/sh
for i in a b c d
do
echo $i
done
上記スクリプトの実行結果は、
$ sh ./(上記スクリプト)
a
b
c
d
となります。(その他 break/continueも可能。後で追記予定)
while文
while
は、渡した条件が真である限り処理を繰り返す構文となります。
#!/bin/sh
a=1
while [ $a -lt 3 ] # aの値が3より小さいか(**l**ess **t**han)
do
echo $a
a=`expr $a + 1` # a+1を実行し、その結果を再度aに格納
done
case文
case
は、他の言語だとswitch
とかに相当します。ifによる分岐が多数になる場合などに代わりに利用したりします。
string=abc
case $string in
ABC) echo "string is ABC" ;;
abc) echo "string is abc" ;;
xyz) echo "string is xyz" ;;
*) echo "string is ?" ;; #=> ワイルドカードなども利用でき、いずれにも該当しなかったら、ここが適用される
esac #=>caseの逆
上記スクリプトを実行すると string is abc
と返ってきます。
以下、追記予定は、
- グルーピング
- 変数
- null/undefibed/空
- idコマンド
- sedコマンド
- awkコマンド