変数展開
記述 | 説明 | 例 | ||
${parameter} | parameter値に置換される。 |
|
||
${parameter:-word} | parameterがunsetまたはnullの時、wordに置換される。 |
|
||
${parameter:+word} | parameterがunsetまたはnullの時、置換されず、そうでなければparameterの値に置換される。 |
|
||
${parameter:=word} | parameterがunsetまたはnullの時、wordの値がparameterに代入される。 |
|
||
${parameter:?word} | parameterがnullまたはunsetの時、wordの値が 標準エラー出力とshellに出力される。インタラク ティブでないshellは終了する。そうでければ、parameterの値に置換される。 |
|
||
${parameter:offset} ${parameter:offset:length} |
parameter文字列をoffsetからlength数までの 部分文字列を返す。 |
|
||
${!prefix*} ${!prefix@} |
名前がprefixで始まる変数名を展開する。区切り 文字はIFSの値が使われる。 |
|
||
${!name[@]} ${!name[*]} |
nameが配列の場合、keyのリストに展開される。 nameが配列でない場合、nameがsetされていれば 0、でなければnullに展開される。 |
|
||
${#parameter} | parameterの文字列長。 |
|
||
${parameter#word} ${parameter##word} |
パス名の前方一致によるパターン展開。#は、 shortest maching、##は、longest maching。 |
|
||
${parameter%word} ${parameter%%word} |
パス名の後方一致によるパターン展開。%は、 shortest maching、%%は、longest maching。 |
|
||
${parameter/pattern/string} | パラメータ置換。 |
|
フロー制御
ループ構造
until
条件が真になるまで繰り返し実行される。
1 2 3 4 5 6 7 8 9 10 |
n=5 t=1 until [ $n -eq 0 ]; do let t=$n*$t let n=$n-1 done echo "5! = $t" ### output ### 5! = 120 |
while
条件が真の間繰り返し実行される。
1 2 3 4 5 |
$ cat server.txt web1 web2 web3 db1 |
以下では、server.txtから1行ずつ読み込み、文字列に”web”が含まれている場合に出力する。
1 2 3 4 5 6 7 8 |
while read line && [[ "$line" =~ "web" ]]; do echo $line done < sample.txt ### output ### web1 web2 web3 |
for
馴染みのあるfor文。他の高級言語の構文と同様に書ける。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# for ((expr1; expr2; expr3)); do commands; done for ((i=0; i < 10; i++)); do echo $i done ((i++)) # increment echo $i ### output ### 0 1 2 3 4 5 6 7 8 9 11 |
拡張for文、foreach文のようにも書ける。
1 2 3 4 5 6 7 8 9 |
for word in $(cat server.txt); do echo $word done ### output ### web1 web2 web3 db1 |
条件構造
if
基本的な条件分岐。elsif
でもなく、else if
でもなく、elif
に注意。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
if [ $# -eq 0 ]; then echo "Usage: if.sh <arg>" exit elif [ $# -eq 1 ]; then echo "args[0] = $1" else echo "args = $@" fi ### output ### $ bash if.sh Usage: script.sh <arg> $ bash if.sh 1 args[0] = 1 $ bash if.sh 1 2 3 args = 1 2 3 |
case
他の高級言語でいうとswitch文に当たる。比較部分は、(.+)
と(
で囲っても良い。以下は、起動時に渡された引数をParseするシンプルな例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#!/usr/bin/env bash # # case word in [ [(] pattern [| pattern]…) command-list ;;]… esac # while [ $# -gt 0 ]; do case "$1" in --help) echo "Usage: case.sh [OPTIONS]" exit ;; (-v) # () でも良い echo "Version 1.0" ;; --verbose) VERBOSE=1 echo "VERBOSE -> on" ;; -o|--output) shift OUTPUT="$1" echo "outpuf -> $OUTPUT" ;; -*) echo "invalid option" 2>&1 exit 1 ;; esac shift done ### output ### $ bash case.sh -v --verbose --output /tmp Version 1.0 VERBOSE -> on outpuf -> /tmp |
最新のbashではコマンドの区切り文字に、;;
、;&
、;;&
が用意されておりそれぞれ解釈が異なるので注意。;;
は、command-listの実行を終え次の句の評価へ移る。;&
は、fall throughの動きになる。;;&
は、引き続き後続するwordのマッチを試みる。実際の動きは以下のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#!/usr/bin/env bash # # case word in [ [(] pattern [| pattern]…) command-list ;;]… esac # while [ $# -gt 0 ]; do case "$1" in --help) echo "Usage: case.sh [OPTIONS]" exit ;; (-v) echo "Version 1.0" ;& --verbose) VERBOSE=1 echo "VERBOSE -> on" shift ;;& -o|--output) shift OUTPUT="$1" echo "outpuf -> $OUTPUT" ;; --ver*) echo "ver" ;; -*) echo "invalid option" 2>&1 exit 1 ;; esac shift done ### output ### $ bash case.sh -v Version 1.0 VERBOSE -> on $ bash case.sh --verbose VERBOSE -> on ver |
select
シンプルな選択リストを表示して標準入力から値を受け取ることができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#!/usr/bin/env bash # # select name [in words …]; do commands; done # ANSWER= select ans in yes no; do ANSWER=$ans break; done echo "Your Answer is '$ANSWER'" ### output ### bash select.sh 1) yes 2) no #? 1 Your Answer is 'yes' |
in words
が省略されている場合は、$@
が指定されたかのように振る舞う。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
echo "Which do you like ?" select ans; do ANSWER=$ans break; done echo "Your Answer is '$ANSWER'" ### output ### Which do you like ? 1) apple 2) orange #? 1 Your Answer is 'apple' |
((..))、$((..))
算術式。let
と等しい。statusは、式の評価の結果が0
の場合は1
を、そうでない場合は0
を返す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
i=0 while [ $i -lt 5 ]; do echo $i ((i++)) done echo "# status test" ((0)) ; echo "((0)) returns $?" ((1)) ; echo "((1)) returns $?" ### output ### 0 1 2 3 4 # status test ((0)) returns 1 ((1)) returns 0 |
[[..]]
拡張正規表現やPattern Matchingによる条件評価が可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env bash STRING="This is a pen. This is a dog. This is a cat." # Matching with regexp [[ $STRING =~ [[:space:]]*a[[:space:]](pen) ]] && { # マッチした文字列はBASH_REMATCH変数で参照可能 echo ${BASH_REMATCH[0]} echo ${BASH_REMATCH[1]} } # Pattern Matching as if the extglob shell option were enabled STRING="pendogcat" echo '[[ $STRING = ?(pen*) ]] ; echo $?' [[ $STRING = ?(pen*) ]] ; echo $? ### output ### a pen pen [[ $STRING = ?(pen*) ]] ; echo $? 0 |
[..], test
条件の評価を行なう。
1 2 3 |
if [ -f ~/.bashrc ] ; then . ~/.bashrc fi |
グルーピングコマンド
コマンドをグループ化して実行できる。
( list )
サブシェルで実行される。以下の例では、サブシェルでカレントディレクトリを一時的に変更し、また元のカレントディレクトリで後続の処理を実行している。サブシェルで定義した変数は、元のコンテキストでは無効となっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
echo $PWD ( cd .. echo $PWD VARS="subshell" ) echo $VARS echo $PWD ### output ### $ pwd /Users/guest/workspace/bash $ bash group.sh /Users/guest/workspace/bash /Users/guest/workspace /Users/guest/workspace/bash |
{ list; }
現在のシェルのコンテキストで評価される。リストのコマンド実行前後でカレントディレクトリは変更されている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
echo $PWD { cd .. echo $PWD VARS="same context" } echo $VARS echo $PWD ### output ### $ bash group2.sh /Users/guest/workspace/bash /Users/guest/workspace same context /Users/guest/workspace |
また、以下のようにリダイレクトと組み合わせるとログファイルへの出力などを一箇所にまとめることができる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ echo "==> `date`" if [[ "hoge" =~ "foo" ]]; then echo "INFO - match" else echo "ERROR - not match" fi } >> out.log ### output ### $ bash group3.sh $ cat out.log ==> 2016年 7月 8日 金曜日 20時56分54秒 JST ERROR - not match |
Coprocesses
サブシェルで非同期に実行する仕組み。サブシェルとはcoproc実行時に生成されるファイルディスクリプタを経由してやりとりすることができる。
NAME
は、commandのPIDやファイルディスクリプタを参照するための変数に使われる識別名。指定しない場合は、デフォルトでCOPROC
という名前が使われる。サブシェルのPIDは、NAME_PID
で参照できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# # coproc [NAME] command [redirections] # do_loop() { i=0 while test $i -lt 10; do echo "loop $i" read line <&0 echo "reply from parent -> $line" ((i++)) sleep 0.5 done } coproc do_loop echo "waiting until coprocess is done." while read line; do echo $line echo "ok" >&${COPROC[1]} done <&${COPROC[0]} wait $COPROC_PID echo "done" ### output ### $ bash coproc.sh waiting until coprocess is done. loop 0 reply from parent -> ok loop 1 reply from parent -> ok loop 2 reply from parent -> ok loop 3 reply from parent -> ok loop 4 reply from parent -> ok loop 5 reply from parent -> ok loop 6 reply from parent -> ok loop 7 reply from parent -> ok loop 8 reply from parent -> ok loop 9 reply from parent -> ok done |
関数
1 2 3 |
name () compound-command [ redirections ] or function name [()] compound-command [ redirections ] |
コマンドをグループ化して名前で一連のコマンド群を呼び出すことができる。以下ような特徴がある。
unset -f name
で関数定義を削除することができる。- 関数のbodyを囲む
{ }
では、前後にブランクや改行での区切りが必要。 - 関数への引数は$
NUMBER
で参照できる(引数は1から) DEBUG
やRETURN
トラップは関数に継承されないため、もし設定を引き継ぎたい場合は、set -o functrace
、関数内でDEBUG
やRETURN
のトラップを指定する、declare -t
で宣言するのいずれかの方法を用いる必要がある。- FUNCNEST変数でネスト数を指定することができる。ネスト回数を超えて関数を呼び出しした場合、関数の実行は中断される。
return 数値
で関数の終了ステータスを指定できる。指定がない場合は、関数の最終コマンドの実行結果が関数の終了ステータスとなる。local varname
でvarname
は関数ローカルの変数となる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#!/usr/bin/env bash function hello() { # 関数ローカル変数 local la="$1" # ネスト数をカウント ((NEST_COUNT++)) echo "FUNCNAME: $FUNCNAME" echo "FUNCNEST: $FUNCNEST" echo " NEST_COUNT: $NEST_COUNT" echo "\$0: $0" echo "Hello, \$la: Hello, $la" # 再帰 hello # 終了ステータスは1 return 1 } FUNCNEST=2 NEST_COUNT=0 # This function definition overwrides existed one. # hello() { # echo "hello function was overwride" # } hello "haikikyou" # 修了ステータス echo "\$?: $?" ### output ### $ bash func.sh FUNCNAME: hello FUNCNEST: 2 NEST_COUNT: 1 $0: func.sh Hello, $la: Hello, haikikyou FUNCNAME: hello FUNCNEST: 2 NEST_COUNT: 2 $0: func.sh Hello, $la: Hello, func.sh: 行 12: hello: maximum function nesting level exceeded (2) $?: 1 |
trap DEBUGと-o functraceを使って以下のようなデバッグもできる(以下のサンプルは簡単かつ稚拙な例、bashdbの方が高度で実用的である)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#!/usr/bin/env bash # _lines=() _i=0 _file=$1 _steps=0 # ステップごとに呼ばれる debugger() { local lineno let lineno=$1+1 (( $_steps > 0 )) && echo "line $lineno: ${_lines[$1]}" (( _steps++ )) } # デバッグ対象ソースを配列に保存 while read; do _lines[$_i]=$REPLY ((_i++)) done < $_file set -o functrace trap 'debugger $(( $LINENO-1 ))' DEBUG source $_file # 実行 |
1 2 3 4 5 6 7 8 |
sample() { local i=0 while test $i -lt 5; do echo "i = $i" let i=i+1 done } sample |
これを実行すると以下のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
bash trap.sh sample.sh line 8: sample line 1: sample() { line 2: local i=0 line 3: while test $i -lt 5; do line 4: echo "i = $i" i = 0 line 5: let i=i+1 line 3: while test $i -lt 5; do line 4: echo "i = $i" i = 1 line 5: let i=i+1 line 3: while test $i -lt 5; do line 4: echo "i = $i" i = 2 line 5: let i=i+1 line 3: while test $i -lt 5; do line 4: echo "i = $i" i = 3 line 5: let i=i+1 line 3: while test $i -lt 5; do line 4: echo "i = $i" i = 4 line 5: let i=i+1 line 3: while test $i -lt 5; do |
条件式
[[
、test
、[
で使われる。
式 | 説明 |
---|---|
-a file | fileが存在すればTrue |
-b file | fileが存在しブロックスペシャルファイルの場合はTrue |
-c file | fileが存在しキャラクタスペシャルファイルの場合はTrue |
-d file | fileが存在しディレクトリの場合はTrue |
-e file | fileが存在すればTrue |
-f file | fileが存在し通常ファイルの場合はTrue |
-g file | fileが存在しSet Group IDがセットされている場合はTrue |
-h file | fileが存在しシンボリックリンクの場合はTrue |
-k file | fileが存在しスティッキービットがOnの場合はTrue |
-p file | fileが存在し名前付きPIPEの場合はTrue |
-r file | fileが存在し読み取り可能の場合はTrue |
-s file | fileが存在しファイルサイズが0以上の場合はTrue |
-t fd | ファイルディクリプタ(fd)がopenされており、ターミナルを指している場合はTrue |
-u file | fileが存在しSet User Idがセットされている場合はTrue |
-w file | fileが存在し書き込み可能な場合はTrue |
-x file | fileが存在し実行可能な場合はTrue |
-G file | fileが存在し有効なグループIDに属する場合はTrue |
-L file | fileが存在しシンボリックリンクの場合はTrue |
-N file | fileが存在し最後に読み取りされてから修正された場合はTrue |
-O file | fileが存在し有効なユーザーIDに属する場合はTrue |
-S file | fileが存在しソケットファイルの場合はTrue |
file1 -ef file2 | file1とfile2が同じデバイスファイルでinode番号が同じ場合はTrue |
file1 -nt file2 | file2がfile2より最終更新日が新しい、または、file1が存在しfile2が存在しない場合はTrue |
file1 -ot file2 | file2がfile2より古い、または、file2が存在しfile1が存在しない場合はTrue |
-o optname | optnameが有効な場合はTrue |
-v varname | varnameの変数がセットされている場合はTrue |
-R varname | varnameの変数がセットされかつ名前参照の場合はTrue |
-z string | stringの長さが0の場合はTrue |
-n string | stringの長さが0でない場合はTrue |
string1 == string2 string1 = string2 |
string1とstring2が等しい場合はTrue |
string1 != string2 | string1とstring2が等しくない場合はTrue |
string1 < string2 | string1が辞書並びでstring2より前に来る場合はTrue |
string1 > string2 | string2が辞書並びでstring1より前に来る場合はTrue |
arg1 OP arg2 | 以下数値比較で用いる。
|
参考リンク
- man bash
- https://www.gnu.org/software/bash/manual/bashref.html
- http://qiita.com/kiida/items/3beb1bf718cdc2f0798a
- http://wiki.bash-hackers.org/start
オライリージャパン
売り上げランキング: 243,409