シェルスクリプト
シェル
コマンドの入力を受け付けそのコマンドを実行し、入力したユーザに対しその結果を返す役割がある
シェルスクリプト
繰り返し行なう処理を自動化するための手段
コマンドに条件分岐や繰り返しなど制御機能を加え実行すること
たとえば
# tar cvzf 120626-etc.tar.gz /etc
# tar cvzf 120626-home.tar.gz /home
# scp 120626-etc.tar.gz root@backup.local.example.com:~/backup
# scp 120626-home.tar.gz root@backup.local.example.com:~/backup
このようなコマンドがあるとすると、
これを実行のたびに入力するのは面倒で、さらに1つのコマンドの処理が終わるまで次のコマンドを実行できなかったり、そもそも実行する際に端末の前に人手が必要である
この一連処理をsystembackup.shという名前のファイルを作成して書き込み、system-backup.shを実行することでファイルに書き込んだコマンドを順に実行できる
# !/bin/bash
tar cvzf 120626-etc.tar.gz /etc
tar cvzf 120626-home.tar.gz /home
scp 120626-etc.tar.gz root@backup.local.example.com:~/backup
scp 120626-home.tar.gzroot@backup.local.example.com:~/backup
プログラムの要素
- 順次実行
- 条件分岐
- 繰り返し
- サブルーチン
このようにプログラミングには、どの種類のプログラムでもいくつかの重要な構成要素がある
シェルスクリプトの作成
$vi lsdate.sh
まずはlsdate.shというファイルを作る
# !/bin/bash
ls
date
lsdate.shというファイルに、lsとdateコマンドを実行するシェルスクリプトを記述
シェルの指定
ファイルの1行目には、利用するシェルの種類とそのコマンド位置を記述する
シェルには数種類あり、今回はbashを使用
1行目に利用するシェルを指定、2行目以降に実行するコマンドを1行ずつ入力
パーミッションの変更
作成したシェルスクリプトを実行するには、パーミッション(アクセス権)を変更してファイルの実行権限をつける必要がある
$ ls -l lsdate.sh
-rw-rw-r--. 1 tooyama tooyama 21 Jun 6 09:51 lsdate.sh
lsコマンドでファイルのアクセス権限を確認している
$ chmod u+x lsdate.sh
$ ls -l lsdate.sh
-rwxrw-r--. 1 tooyama tooyama 21 Jun 6 09:51 lsdate.sh
実行権限を付与するためにchmodコマンドを使う
これで所有者に実行権限が与えられた
$ ./lsdate.sh
Desktop diff2 ls-l-output ls-usr-bin
touched-file
cat-output hosts.bak ls-l-output-second lsdate.sh
uniq-sample
diff1 hosts.new2 ls-output score (lsの実行結果)
Fri Jun 6 09:55:05 JST 2008 (dateの実行結果)
実行権限をシェルスクリプトにつけたらシェルスクリプトを実行する
**./**というのはパス指定で、「カレントディレクトリにある(lsdate.shを実行せよ)」という意味
lsやcpを実行するときは、パスが通っているので、パス指定は必要ないが、今回は、カレントディレクトリにある特定のシェルスクリプトを実行させるために、パス指定を実行
lsdate.shの中に記述された、lsコマンドとdateコマンドが順に実行された
コメント
プログラム上に書く注釈
シェルの場合、**#**で始まる行がコメント
プログラムの実行時はコメントは無視される
$ vi lsdate.sh
# !/bin/bash
ls
# date
lsdate.shを編集して、dateコマンドをコメントアウト
$./lsdate.sh
Desktop diff2 ls-l-output ls-usr-bin
touched-file
cat-output hosts.bak ls-l-output-second lsdate.sh
uniq-sample
diff1 hosts.new2 ls-output score(lsの実行結果)
dateコマンドの出力がなくなった
このようにコメントは記述したプログラムがどういった処理をするのかを記述したり、一時的に特定の処理を無効化(コメントアウト)する場合に利用される
echoコマンド
echo [オプション] 文字列
引数で与えた文字列を標準出力に出力する
オプション | 意味 |
---|---|
-n | 通常の出力は改行されるが、このオプションがあると改行されない |
変数
変数とは、いわゆる「ハコ」の役割をし、中に数値や文字列が入る
変数に「=」を使って数値や文字列を代入し、参照は「$」をつけて行う
$ abc=123
$ echo $abc
123
(abcの内容を表示する)
シェル関数abcに値を設定し、に123を代入している
$ abc[0]=123
$ abc[1]=456
$ echo ${abc[0]}
123
(abc[0]の内容を表示する)
$ index=1
$ echo ${abc[$index]}
456
(abc[1]の内容を変数を使って表示する)
シェル変数と環境変数
-
シェル変数
実行しているシェル内部でのみ有効 -
環境変数
シェル内で実行されたコマンド内(外部)でも有効
$ export abc
(シェル変数abcを環境変数abcにする)
$ export xyz=234
(環境変数xyzを作成し234を代入する)
exportコマンドを使い環境変数を作成している
1つ目のコマンドではabcという環境変数を作成していて、abcには値を代入していない
2つ目のコマンドではxyzという環境変数を作成していて、xyzには234を代入している
$ cat BBB.sh
# !/bin/bash
xxx=123 #シェル変数xxxに123を代入する
export yyy=234 #環境変数yyyに234を代入する
echo xxx=$xxx in BBB.sh #変数xxxの値を表示する
echo yyy=$yyy in BBB.sh #変数yyyの値を表示する
./CCC.sh #CCC.shを実行する
$ cat CCC.sh
# !/bin/bash
echo xxx=$xxx in CCC.sh #変数xxxの値を表示する
echo yyy=$yyy in CCC.sh #変数yyyの値を表示する
$./BBB.sh
xxx=123 in BBB.sh
yyy=234 in BBB.sh
xxx= in CCC.sh
yyy=234 in CCC.sh
$
シェル変数は引き継がれないので、シェルスクリプトCCC.shの中でxxxの値は表示されない
一方で、yyyは環境変数なのでCCC.shまで引き継がれるため、値が表示される
readコマンド
read 変数名
標準入力からデータを読み込み、すでに変数にデータが入っていた場合、新しいデータに上書きされる
$ echo $abc
123
$ read abc
aaabbbccc
$ echo $abc
aaabbbccc
1行目でシェル変数abcの中身が表示される
3行目で文字列を入力
5行目でシェル変数abcの中身が入れ替わっている
シェル変数
シェル変数の一覧を表示する場合は、setコマンドを利用
削除する場合は、unsetを利用
$ set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:
hostcomplete:interactive_comments:login_shell:progcomp:promptvars:so
urcepath
BASH_ALIASES=()
abc=aaabbbccc
$ set | grep ^abc
abc=aaabbbccc
$ unset abc
$ set | grep ^abc
8行目でabcで始まるシェル変数のみ確認
9行目でシェル変数abcを削除
10行目でabcで始まるシェル変数のみ確認し、削除されていることを確認
環境変数
環境変数の一覧を表示する場合は、envコマンドを利用
登録済みの環境変数を削除するときは、unsetコマンドを利用
$ env
ABC=999999
HOSTNAME=host1.alpha.jp
TERM=xterm
SHELL=/bin/bash
HISTSIZE=1000
(略)
$ env | grep ^ABC
ABC=999999
$ unset ABC
$ env | grep ^abc
$
8行目でABCで始まる環境変数のみ確認
9行目で環境変数ABCを削除
10行目でABCで始まる環境変数のみ確認し、削除されていることを確認
引用符
シェルスクリプトにおいて、文字列を引用符で囲むことが可能であり、使用される引用符により
囲まれた文字列の処理が異なる
- '(シングルクォート)
参照用の「$」付きの変数がある場合、「$」も文字列として認識されるため、変数は展開されない - "(ダブルクォート)
「$」付き変数は展開された文字列になる - `(バッククォート)
バッククォートで囲まれた文字列はコマンドとして解釈され、このとき「$」付きの変数があれば、それを展開した上でコマンドが実行される
$ ABC=123
$ echo 'Value of ABC is $ABC.'
Value of ABC is $ABC.
$ echo "Value of ABC is $ABC."
Value of ABC is 123.
$ XYZ=`date`;
$ echo "It is $XYZ now."
It is Thu Mar 20 06:08:14 JST 2017 now.
$ echo "Itis`date`now."
It is Thu Mar 20 06:08:14 JST 2017 now.
2行目では$ABCが文字として認識されそのまま表示された
3行目では$ABCが変数として認識され、内容である123が展開された
5行目では$XYZにdateコマンドの実行結果が入っている
6行目ではダブルクォートで全体の文字列を囲み、バッククォートで囲んだ
dateコマンドを挿入して、変数XYZへの代入を省略
引数
シェルスクリプトは、実行時にオプションを引数として参照することが可能
引数は$1,$2…など$の後に引数の番号を指定することで参照できる
$ cat args.sh
# !/bin/bash
echo '$1:' $1;
echo '$2:' $2;
echo '$3:' $3;
echo '$0:' $0;
echo '$#:' $#;
$ ./args.sh aaa bbb ccc
$1: aaa
$2: bbb
$3: ccc
$0: ./args.sh
$#: 3
$1-$3は引数、$0は実行コマンド名、$#は、引数の数を示している
shift文
引数の順序をずらす
shiftを実行すると、$2が$1に、$3が$2に・・・になる
$ cat argsshift.sh
# !/bin/bash
echo '$1:' $1;
echo '$2:' $2;
echo '$3:' $3;
shift
echo '$1:' $1;
echo '$2:' $2;
作成したスクリプトに対して、変数を渡すと
$ ./argsshift.sh aaa bbb ccc
$1: aaa
$2: bbb
$3: ccc
$1: bbb
$2: ccc
shiftを実行すると
4行目で$1がbbbに変わった
5行目で$2がcccに変わった
エスケープシーケンス
$ABC=123
$ echo "Value of ABC is "$ABC"."
Value of ABC is 123.
$ echo "Value of ABC is \"$ABC\"."
Value of ABC is "123".
2行目では$ABCを""で囲ったが、""が表示されない
3行目では表示したい"の直前に**「\」**を付けることで"を表示できる
$ echo 'Value of ABC is "$ABC".'
Value of ABC is "123".
$ echo "Value of ABC is\$ABC."
Value of ABC is $ABC.
1行目で文字列全体を'(シングルクォート)で囲むことでも"(ダブルクォート)を表示できる
2行目でダブルクォートで囲んだ文字列内で「$」をそのまま表示したい場合にも直前の「\」で可能
\(バックスラッシュ)はエスケープ文字と呼ばれ、
- 特別な文字で直後の1文字の扱いを変更する
- 使用する引用符との組み合わせで、文字の扱いを変えたい場合に有効
- 改行コードにも有効で\(バックスラッシュ)を行末に付与することで、文字列の途中で折り返す
ことができる
\(バックスラッシュ)による改行はシェルスクリプト内でも使うことが可能
エスケープシーケンス
次に続く1文字とセットで特別な意味を持つ文字列とする
- \t(タブ)
- \n(改行)
- \ooo(oは数字で8進数表記の文字)
などがある
$ echo -e "I am a cat. \nAs yet I have no name\041"
I am a cat.
As yet I have no name!
\nは改行に変わり、\041は!になった
※-eはエスケープした文字を解釈するechoオプション
sourceコマンド
bashなどのシェルの内部コマンドで、指定されたファイルを読み込んでシェル環境を設定する
ファイル内容はシェルコマンドと解釈して実行
一般的な用途として、シェルの環境設定ファイルである".bashrc"や".bash_profile"などを設定変更後、ログインしなおさずに設定を現在のシェル上で有効にする場合に使われる
$ cat set.sh
# !/bin/bash
abc=xyz
echo $abc
シェルスクリプト「set.sh」を用意する
1行目でset.shの内容確認
$ echo $abc
$abcをechoコマンドで出力
$abcには何も格納されてないため、何も表示されない
$ ./set.sh
xyz
set.shスクリプトを実行
スクリプト内で設定された変数abcの値がechoで出力された
set.shにはecho文が記述されているため、その値が出力される
$ echo $abc
$abcをechoコマンドで出力
変数abcへの値の設定はスクリプト内でしか有効でないので、何も表示されない
$ source set.sh
xyz
sourceコマンドでset.shを読み込む
スクリプト内で設定された変数abcの値がechoで出力された
$ echo $abc
xyz
sourceコマンドで読み込んだことにより、set.shの終了後も変数abcに値が格納されたまま
条件分岐
if文
if 条件式1 then ... elif 条件式2 ... else ... fi
elif...の部分とelse...の部分は省略可能
elifは、別の条件(条件式2)で判断したい場合に利用する
elseは条件が全てあてはまらなかった場合、実行される
fiで終了
文字列比較を行う演算子
演算子 | 比較内容 |
---|---|
a==b | aとbが等しければ真 |
a!=b | aとbが等しくなければ真 |
数値比較を行う演算子
演算子 | 比較内容 |
---|---|
a -eq b | aとbが等しい(equalto)ければ真 |
a -ne b | aとbが等しくなければ(notequalto)真 |
a -ge b | aがb以上(greaterthanorequalto)であれば真 |
a -le b | aがb以下(lessthanorequalto)であれば真 |
a -gt b | aがbより大きい(greaterthan)値であれば真 |
a -lt b | aがb未満(lessthan)であれば真 |
ファイル属性の確認
if test -d パス ; then.....
-dの部分がファイル属性確認の演算子
-dはディレクトリであるかの判定を行なうので、このif文全体で、「パスがディレクトリであれば真の値を返す」という条件式
:ファイル属性の確認を行なう演算子
演算子 | 内容 |
---|---|
-f ファイル名 | 通常ファイルなら真 |
-d ファイル名 | ディレクトリなら真 |
-e ファイル名 | ファイルが存在すれば真 |
-L ファイル名 | シンボリックリンクなら真 |
-r ファイル名 | 読み取り可能ファイルなら真 |
-w ファイル名 | 書き込み可能ファイルなら真 |
-x ファイル名 | ファイルが存在して、実行権限があれば真 |
-s ファイル名 | サイズが0より大きければ真 |
if[条件節]; then...(if test 条件節 ; then....)
testコマンドは[]を使って記述することも可能
複数の条件を重ねる
条件分岐の場合、複数の条件を重ねることができます。条件Aと条件Bが同時に成立している必要があるときは、「条件A”かつ”条件Bが成立」ということで、論理積(=AND)が用いられます。同じく「条件A”もしくは”条件Bが成立」の場合は、論理和(=OR)が用いられます。シェルスクリプトにおいて、論理積・論理和の書き方は2通り存在します。
論理積
条件A”かつ”条件B”かつ”条件Cが成立
[条件A -a 条件B -a条件C ]....
[条件A] && [条件B] && [条件C]...
2通りあり[]の使い方が違う
論理和
条件A”もしくは”条件B"もしくは”条件Cが成立
[条件A -o 条件B -o 条件C ]....
[条件A] || [条件B] || [条件C]...
一対多の条件分岐
シェルスクリプトでは、case文が用意されており、一対多の分岐が記述できるようになっている
case 変数 in
値A)
処理1;;
値B)
処理2;;
esac
変数の値が値Aのとき、処理1を実行
最後はcase文の逆からの記述であるesacで終わる
値はパイプ記号(|)で区切って複数指定可能
$ cat case.sh
# !/bin/bash
case $1 in
a|A)
echo"引数にaまたはAが入力されました";;
b|B)
echo"引数にbまたはBが入力されました";;
esac
1行目でcase.shを作成して実行
$ ./case.sh a
引数にaまたはAが入力されました
$ ./case.sh B
引数にbまたはBが入力されました
と表示される
また、
$ cat defaultcase.sh
# !/bin/bash
case $1 in
1)
echo"引数に1が入力されました";;
2)
echo"引数に2が入力されました";;
*)
echo"1,2以外が入力されました";;
esac
1行目でdefaultcase.shを作成し実行
どの値にもマッチしなかった場合の処理を記述するには、値に**アスタリスク(*)**を使う
$ ./defaultcase.sh 1
引数に1が入力されました
$ ./defaultcase.sh 2
引数に2が入力されました
$ ./defaultcase.sh 0
1,2以外が入力されました
5行目でどの条件にもマッチしない場合に指定した値*の処理が実行
繰り返し
同じ処理を繰り返し行い、ある条件が成立したときに終了する、という形式
for文
for 変数 in 値のリスト
do
処理
done
値を列挙し、それを対象に処理を繰り返す
値のリストは文字を羅列したもや実行結果
$ for i in a b c d
> do
> echo $i
> done
a
b
c
d
値のリストとして'a b c d'が渡され、まずi=aを行いecho $i、次にi=bを行いecho $iを・・・
という繰り返し
for i in `ls`
結果値のリストとしてコマンドの実行結果を使うこともでき、iにlsの実行結果を代入してループが実行される
while/until文
- while条件が成立している間ループを繰り返し条件が成立しなくなったら終了という処理
- until文は条件が成立してない間ループを繰り返し条件が成立したら処理を終という処理
while 条件式
do
処理
done
until 条件式
do
処理
done
たとえば
$ cat loop.sh
# !/bin/bash
count=1
while [ $count -le 10 ]
do
echo "この処理は$count回実行されました"
count=`expr$count+1`
done
のloop.shを作成
count=1で、カウンタ用の変数countに初期値1を設定
while [ $count -le 10 ]で、シェル変数countが10以下の間、処理を繰り返す
$./loop.sh
この処理は1回実行されました
この処理は2回実行されました
この処理は3回実行されました
この処理は4回実行されました
この処理は5回実行されました
この処理は6回実行されました
この処理は7回実行されました
この処理は8回実行されました
この処理は9回実行されました
この処理は10回実行されました
このような実行結果となる
select文
select 変数 in リスト
do
処理
done
たとえば
select name in "apple" "banana" "orange"
do
echo "You selected $name";
done
これを実行すると
1) apple
2) banana
3) orange
$? 1
You selected apple
13を入力すると、do~done内部が実行される
また、「Ctrl」Cで中止できる
繰り返しの制御
breakやcontinueを使い繰り返しを制御することができる
breakは繰り返しを終了して、continueは繰り返しの先頭に戻る役割
$ cat ./sample.sh
while true
do
echo "Continue? (y/n)"
read input
case $input in
n) break
;;
y) continue
;;
*) echo "Please input y or n."
;;
esac
done
これを実行すると
$ ./sample.sh
Continue? (y/n)
y
Continue? (y/n)
a
Please input y or n.
Continue? (y/n)
n
$
3行目でyを入力すると
4行目で繰り返しの先頭に戻る
5行目でy,n以外を入力
6行目でPlease input y or n が表示される
8行目でnを入力すると
9行目では繰り返しをが終了され何も表示されない
サブルーチン
一連の処理をまとめて、再利用できるようにしたものをいう
サブルーチンはシェルスクリプトでは関数と呼ばれている
関数
関数は、引数とよばれるデータを与え、処理をして結果を返すという機能の集まり
function関数名
{
処理
}
または
関数名()
{
処理
}
引数は、実行時に関数名に続けて記述
引数は、関数の内部で$1,$2で参照することができる
return文
return 変数名
シェルスクリプトの関数で、結果を返すときに実行
関数内での処理はそこで終了し、変数が関数の呼び出し元に返される
デバッグ
作成したプログラムが動作しない場合はどこに問題があるのか調べる必要がある
これをデバッグという
多くのプログラム言語には、デバッグを支援するツールが用意されており、シェルスクリプトにも、スクリプトをデバッグモード
で動かすためのコマンドが用意されている
shコマンド
sh自身はシェルを起動するコマンドで、-xオプションを付けて引数にシェルスクリプトを指定すると、コマンドや変数の中身を表示しながらスクリプトを実行
$ sh -x ./sample.sh
+ true
+ echo 'Continue? (y/n)'
Continue? (y/n)
+ read input
y
(yを入力)
+ case $input in
+ continue
+ true
+ echo 'Continue? (y/n)'
Continue? (y/n)
+ read input
n
(nを入力)
+ case $input in
+ break
参考書籍
『Linux標準教科書 v3.0.2』 LPI-Japan 2019年