はじめに
シェルスクリプトについて、学んだことを備忘録としてまとめる。
shにするかbashにするか
- shの特徴
- 長い歴史を持つ反面,現在では設計が古い
- 過去のシェルスクリプトの多くがsh向けに書かれている
- Debian系ディストリビューションではshをdashとシェルで実装
- Red Hat系ディストリビューションはshをbashの別名として実装(shとbashに実体はRed Hat系では同じ)
- bashの特徴
- shよりもシェルスクリプトを書く上で便利な機能が用意されている
- 現代のLinuxでは,bashはほぼデフォルトでインストールされている
shよりもbashを検討したほうがいい
理由
-
シェルスクリプトの互換性・移植性が高い
→shはすべてのLinuxに存在するが,OSによって実装の差異がある
-
便利な機能が用意されている
→算術計算する際,shでは外部コマンドexprを使用する必要があるが,bashで外部コマンドなしに計算可能(シェル内部の機能なため,動作も高速)
シェルスクリプトの実行形
ファイルの先頭に#!
で始まる行を追加する必要があるが,これは**shebang(シバン,シェバン)**と呼ばれるもの.
Linuxカーネルが#!
を確認すると,その後ろのコマンドを実行する.
#!/bin/bash
は「このシェルスクリプトは/bin/bashで動かす」という意味.
シバンを書くことにより,/bin/bash
をコマンドラインに書かなくても,ファイル名を指定することにより,シェルスクリプトを実行できる
#!/bin/bash
alias lsalf='ls -alf'
$ lsalf #エイリアス未設定
lsalf: コマンドが見つかりません
$ source ./alias_set.sh #sourceコマンドでエイリアス設定
$ lsalf
arg.sh if-bin.sh getdate.sh homesize.sh tes1.sh .
parameters.sh rootls.sh alias_set.sh 2019-10-02 ..
$ lsalfsh #エイリアスは未設定
lsalfsh: コマンドが見つかりません
$ cat alias_set.sh #lsalfの名前をlsalfshに変更後,catで内容表示
#!/bin/bash
alias lsalfsh='ls -lf' #lsalfshに変更
$ ./alias_set.sh #シェルスクリプトを実行
$ lsalfsh #シェルスクリプト終了後もエイリアスは未設定
lsalfsh: コマンドが見つかりません
これはなぜ起こるのか.
まずsourceコマンドでは,カレントシェルでシェルスクリプトが実行されるため,現在の環境が引き継がれる.
それに対し,ファイル名で実行した場合には,サブシェルでシェルスクリプトが実行されるため,元のシェルとは別物であり,エイリアスの設定は引き継がれない.(環境変数は引き継がれる)
サブシェルとは,現在のシェルから新しく起動される子プロセスのシェルのこと.
$ ./rootls.sh
root directory
合計 483908
drwxr-xr-x 2 root root 4096 9月 30 11:54 bin
drwxr-xr-x 3 root root 4096 9月 30 11:56 boot
drwxrwxr-x 2 root root 4096 9月 30 00:24 cdrom
drwxr-xr-x 18 root root 4020 9月 30 21:54 dev
略
変数名の参照
変数の値を参照するには,変数名の前に$
を付ける
変数の書き方の注意
- 代入時には$を付けない
- =の前後にはスペースを入れない
- 変数名に利用できる文字はアルファベットと数値とアンスコのみ
- {}を使用して変数名の区切りを明示する
- ex) 変数名(filename)+任意の文字列(str)を連結して標準出力する場合
- ダメな例⇒echo filename_str
- 正しい例⇒echo {filename}_str
クウォーティング
クウォーティングで囲まれた文字列は一つの単語として認識される
''
と""
の違いについて
変数展開が有効かどうか
シングルクォート:$
による変数の展開が行われない
ダブルクォート:$
による変数の展開が行われる
コマンド置換
#!/bin/bash
filename=$(date '+%Y-%m-%d')
touch "$filename"
$ ./getdate.sh
$ ls -l
合計 16
-rw-r--r-- 1 gyokoyama gyokoyama 0 10月 2 08:00 2019-10-02
-rwxr-xr-x 1 gyokoyama gyokoyama 60 10月 2 07:59 getdate.sh
-rwxr-xr-x 1 gyokoyama gyokoyama 32 10月 1 08:03 homesize.sh
-rwxr-xr-x 1 gyokoyama gyokoyama 50 10月 1 09:01 rootls.sh
-rwxr-xr-x 1 gyokoyama gyokoyama 20 10月 1 08:15 tes1.sh
2019-10-02というファイル名が追加されている.
位置パラメータ
#!/bin/bash
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
echo "\$5 = $5"
$ ./parameters.sh aaa bb ccccc
$0 = ./parameters.sh
$1 = aaa
$2 = bb
$3 = ccccc
$4 =
$5 =
ワイルドカードの使用
*
:任意の文字列の代わりになる.文字列の長さは0でもよい.
?
:任意の1文字の代わりになる.
[]
:かぎ括弧の中に記述した任意の1文字の代わりになる。連続する文字コードの指定には - 記号が使える。たとえば, [a-c] は [abc] と同じ意味.
$ ls
2019-10-02 getdate.sh homesize.sh parameters.sh rootls.sh tes1.sh
$ ./parameters.sh *
$0 = ./parameters.sh
$1 = 2019-10-02
$2 = getdate.sh
$3 = homesize.sh
$4 = parameters.sh
$5 = rootls.sh
引数の個数
コマンドライン引数に指定された引数の個数は$#
という変数で参照できる.
最後の行にecho "\$# = $#"
を追記.
#!/bin/bash
echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
echo "\$5 = $5"
echo "\$# = $#"
$ ./parameters.sh *
$0 = ./parameters.sh
$1 = 2019-10-02
$2 = getdate.sh
$3 = homesize.sh
$4 = parameters.sh
$5 = rootls.sh
$# = 6
$./parameters.sh aaa bb c
$0 = ./parameters.sh
$1 = aaa
$2 = bb
$3 = c
$4 =
$5 =
$# = 3
引数全体の参照
$@
,$*
を使用して,引数を分割せず全て一括りのようにして参照する.
#!/bin/bash
echo "\$@ = $@"
echo "\$* = $*"
$ ./arg.sh aa bbbb cc
$@ = aa bbbb cc
$* = aa bbbb cc
-
$@
,$*
の違い-
$@
は位置パラメータがそれぞれ1つずつの文字列として展開される(上記の例では,'aa' 'bbbb' 'cc'として展開) -
$*
は引数全体が1つの文字列として展開されている(上記の例では,'aa bbbb cc'として展開)
-
if文の制御構造
#!/bin/bash
if [ "$1" = "bin" ]; then
echo "ok"
else
echo "NG"
fi
$ ./if-bin.sh bin
ok
$ ./if-bin.sh bash
NG
記述の注意点
-
if [ "$1" = "bin" ] then
⇒条件を書いた後に;
を省略するとエラー(then
を改行すればセミコロン不要) -
if ["$1" = "bin"]
⇒[]
の前後に半角スペースがないとエラー
if文の仕組み
シェルスクリプトでは,if文の後ろに置くのは「コマンド」であって,条件式ではない.
[
は単なる括弧ではなく,bashの組み込みコマンド.
[
コマンドは引数に書かれた条件式を判定し,その条件が正しければ0,そうでなければ0以外を返すコマンド.if文では0か0以外かどうかで真偽を判定
if直後の文は終了ステータスの値で真偽を判定するため,[
コマンド以外でも可能.評価演算子等を組み合わせて活用する.
終了ステータス
終了ステータスとはどういうものか.
lsやgrepコマンドなどは,終了時にある整数値を返す.その整数値のことを終了ステータスという.コマンドが正常に終了した場合は0,エラー時は0以外を返す.
exit status = 0
,exit status = 1
とかの整数値が終了ステータスにあたる.