シェルプログラムの先頭には#!/bin/sh
を書きます。
シェルプログラムのファイルには実行権限(xフラグ)を与える。
#
はコメントとして扱われます。
#!/bin/sh
#
# comment
# comment comment
echo "Hello World. # comment ";
また、上記の様にダブルクォーテーションで囲むと、 echoコマンドの引数として扱われるため、コメントとしては取り扱われません。
./comment
Hello World. # comment
コマンドは改行することで1つのコマンドとして区切られます。
改行を無視する場合、は\
を利用する
echo Hello \
then else> World.
Hello World.
\
の前にスペースをいくら入力しても、1つのスペースに置き換えられる点に注意が必要です。
echo Hello \
then else> world.
Hello world.
ワイルドカード | 説明 |
---|---|
* | 文字列全部 |
? | 一文字 |
[...] | [ ]の中に含まれる文字のどれか1つ |
[!...] | [ ]に含まれない文字 |
例 | |
---|---|
*abc | 〜abcの様に、abcで終わるファイル。abc というファイル名でもOK |
*abc* | ファイルの中にabc が含まれる文字列。 |
[a-z]* | a からz までの文字のどれかで始まるファイル。 |
[-a-z]* | - かa からz で始まるどれかで始まるファイル |
a-zA-Z]* | アルファベットの大文字か小文字で始まるファイル |
*[0-9]* | ファイル名の中に数字が含まれるファイル |
[!0-9]* | 数字では始まらないファイル |
?? | 2文字のファイル |
??* | 2文字以上のファイル |
abc/* | abc というディレクトリ下の全ファイル |
.
で始まるファイルを隠しファイルといいます。
隠しファイルはワイルドカードを利用しても除外されます。
ls -a
. .Xdefaults .exrc ZZZ dir_a xyz
.. .cshrc ABC def ghi
ls
ABC ZZZ def dir_a ghi xyz
ls .*
.Xdefaults .cshrc .exrc
ls .*rc
.cshrc .exrc
\
を使うワイルドカードカード等のシェルにとって特別な文字は、メタキャラクタと呼ぶ。
;
、&
、(
、)
、|
、~
、<
、>
、?
、*
、[
、
]
、$
、'
、"
、バッククォート
、{
、}
、改行
、タブ
、スペース
1文字をクォートするにはバックスラッシュを使います。
echo abc\\def
abc\def
シングルクォートで囲まれた文字列はすべて普通の文字となる。
ダブルクォーテーションはほとんどすべての文字をエスケープしますが、
$
、バッククオート、\
という3つの特殊文字はエスケープしません。
$
を利用した場合に変数名で展開される$
の場合はエスケープされるコマンドの引数がシェルに解釈されないようにしたいときに利用する。
シェルがそれを解釈してもよいかどうかを考えて利用する。
echo *
ABC ZZZ def dir_a ghi xyz
echo "*"
*
囲んだ文字列の中で、変数の置き換えやコマンド置き換えした結果を使いたいときは、 ダブルクォートを使う。
FILE=testfile
echo "Cannot remove $FILE"
Cannot remove testfile
echo "Today is `date`"
Today is 2021年 2月22日 月曜日 22時34分07秒 JST
シングルクォートはそのままの文字として使いたい場合です。
シングルクォートはダブルクォートをエスケープでき、
ダブルクォートはシングルクォートをエスケープします。
echo "'abc'"
'abc'
echo '"abc"'
"abc"
バックスラッシュをエスケープするには、 シングルクォートでもバックスラッシュでも可能です。
シングルクォートの中で変数を展開したい場合は、以下のように書く
'Part1'"Part2"'Part3'
バッククォートはその中に書かれたコマンドを実行し、その結果をその位置に書き込みます。
echo "Today is `date`"
Today is 2021年 2月22日 月曜日 22時45分15秒 JST
バッククォートをバックスラッシュでエスケープすることにより、 コマンド置き換えのネストも可能です。
STRING=`echo "abc \`echo def\` ghi"`
echo $STRING
abc def ghi
最初にecho def
の部分が置き換えられ、その後echo "abc def ghi"
が実行されます。
以下が同じ意味のコマンドです。
TMPSTR=`echo def`
STRING=`echo "abc $TMPSTR ghi"`
echo $STRING
abc def ghi
コマンドは慣例として、正しく終了したときは0、失敗したときは、1が終了時にセットされ、 エクジットステータスと言う。
exit status | 実行結果 | 正誤 |
---|---|---|
0 | 成功 | true |
1 | 失敗 | false |
またexit statusはコマンドを実行した直後に$?
という変数に代入される。
echo $?
0
コマンドは通常改行でコマンド行となりますが、その他にもコマンドの区切りとするものがあります。
これらをまとめてコマンドセパレータと呼びます。
コマンドセパレータ | 説明 |
---|---|
改行 | 1つのコマンドの区切り |
; | 1つのコマンドの区切り |
| | 出力された結果を次のコマンドの入力にする |
& | バックグラウンドで実行させる |
|| | OR制御演算子 |
&& | AND制御演算子 |
セミコロンでコマンドを区切ると、左から順に実行されます。
cat file1;cat file2;cat file3
abc
def
ghi
セミコロンを使うと、複数行にまたがる記述を1行にまとめることができます。
左から右に流していくという意味で、パイプライン処理といいます。
|
の右側で実行したコマンドの結果を、|
の右側のコマンドの入力として処理する働きがあります。
echo abc | wc
1 1 4
cat file2 file1 | sort | more
abc
def
パイプライン上にあるコマンドはそれぞれ別のプロセスとして動作しています。
一番右に書かれたコマンドの終了コードが、そのパイプライン全体の終了コードとなります。
command1 | command2
を書き直すと、
comamnd1 > tmpfile; command2 < tmpfile
となります。
command1
の結果をtmpfile
に書き込み、その後tmpfile
をcommand2
の入力とします。
アンパサンド(&)をコマンドの後ろにつけると、そのコマンドはバックグラウンドで実行されます。
make &
command1 || command2
command1はすぐに実行されますが、command2は実行結果が0でない(trueではない)場合に限って実行されます。
command1 && command2
command1の実行結果が0(true)の場合に右側のコマンドが実行されます。
mkdir directory && cp file directory
丸括弧()
や中括弧{}
を使ってコマンドをグルーピングすることで、
複数のコマンドをあたかも一つのものであるかのように実行させることができます。
()
によるグルーピング丸括弧で囲まれたコマンドは現在の動作しているシェルとは別にサブシェルのもとで動作することになります。
今のシェルが新しくシェルを動作させ、その中で動作するものです。
今のシェル(親)はサブシェル(子)が終了するまで次の処理には移行できません。
丸括弧を使うのは、現行の状態を変えたくは無いが、変えた状態で何かをやらなくてはならないときです。
(cd $HOME/makedir; make)
{}
によるグルーピング中括弧を使って
{ command1; command2; ....; }
の様に、グルーピングすることもできます。
中括弧で囲むと、現行のシェルの中で実行されます。
中括弧の前後にはスペースが必要です。
また、中括弧の中の最後のコマンドはセミコロンが必要です。
改行すればセミコロンやスペースが不要となります。
{
command1
command2
}
中括弧を使うのは、それぞれのコマンドの結果をひとまとめにしたいようなときです。
{ date; make; } > make.list
makeコマンドの出力結果の前に、その日の日付を挿入する例です。
中括弧はネストさせることもできますし、バックグラウンドで走らせることもできます。
バックグラウンドで走らせた場合は、サブシェルで実行されます。
if commandlilst
then
command
else
command
fi
command-listの結果が0の場合はthen、
そうでない場合は、elseのcommandが実行されます。
fiでif文を閉じます。
#!/bin/sh
if test -f file1
then
echo "The file exists."
else
echo "The file does not exist."
fi
else文はなくてもかまいません。
ifと同じ行にthenを書くこともできます。
#!/bin/sh
if test -f file1; then
echo "The file exists."
fi
また、testコマンドは、鉤括弧で代用可能です。
#!/bin/sh
if [ -f file1 ]; then
echo "The file exists."
fi
条件を増やす場合は、elif
文を利用します。
if condition1
then
command
elif condition2
then
command
elif condition3
then
command
else
command
fi
for variable in word-list
do
command
.....
done
word-list
の部分には、スペースで区切って並べます。
#!/bin/sh
for i in a b c d
do
echo $i
done
./for
a
b
c
d
while command-list
do
command
.....
done
command-listの部分が0ではなくなった場合に処理を抜けます。
#!/bin/sh
a=1
while test $a -lt 3
do
echo $a
a=`expr $a + 1`
done
実行結果は以下となります。
./while
1
2
コロン(:)で無限ループの設定を行い、 何らかの条件でループを抜けるというやり方もできます。
コロンは、何も処理せず、0を終了コードとして返すコマンドです。
while :
do
if ...
then
break
fi
done
case string in
pattern1) command-list ;;
pattern2) command-list ;;
pattern3) command-list ;;
....
esac
stringのところにある値が、それぞれのpatternという条件に合うかどうか調べていき 条件があった場合にその後ろにあるcommand-listに並べられたコマンドが実行されます。
;;
と書いたところまでが処理の対象となります。
caseの終わりはesac
という文字です。
#!/bin/bash
STRING=abc
case "$STRING" in
ABC) echo "STRING is ABC" ;;
abc) echo "STRING is abc" ;;
xyz) echo "STRING is xyz" ;;
esac
./case
STRING is abc
patternの条件を設定するときには、ワイルドカードが利用できます。
パイプを使って複数の条件を設定することも可能です。
#!/bin/sh
case $1 in
abc ) echo "これはabcです" ;;
def | ghi ) echo "これは、defかghiかのどちらかです" ;;
abcd* ) echo "これはabcdで始まる文字列です" ;;
[Yy]* ) echo "Yかyで始まる文字列です(yes、noの判定で使える)" ;;
"[Yy]" ) echo "Yかyである" ;;
"[Yy]*" ) echo "Y*あるいはy*という文字列である" ;;
"[Nn]"* ) echo "Nかnで始まる文字列です" ;;
[a-z]*[AB] ) echo "小文字で始まりAかBで終わる文字列" ;;
\* ) echo "アスタリスク(*)です" ;;
\$ ) echo '$です' ;;
"!" ) echo "!です" ;;
'&' ) echo "&です" ;;
\? ) echo "?です" ;;
? ) echo "なにか1文字の文字です" ;;
"" ) echo "何も無い(null文字や変数が未セットの場合)" ;;
'""' ) echo "ダブルクォーテーションが2つ並んでいる状態" ;;
\'\' ) echo "シングルクォートが2つ並んでいる状態" ;;
[!Nn]* ) echo "Nでもnでも始まらない文字列です" ;;
* ) echo "今までの条件に合致しない残り全部" ;;
esac
testコマンドは、ある条件を判定し、その条件が正しい場合には真(0の値)を返し、 誤っている場合には偽(0以外の値)を返します。
if test -r file1
then
echo "The file exists and I can read it.
fi
if [ -r file1 ]
then
echo "The file exists and I can read it.
fi
while [ expression ]
do
....
done
改行コードではなくセミコロンを使う事によって、複数のコマンドを1行に書くことができます。
if command-list; then command; ...; fi
メリットとして、見やすくなるということもありますが、
コマンドによっては1行にまとめてやらないと期待通りに動作しないものもあります。 (リモートシェル、Makefile)