- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- Bash へ行く。
- 1 (2018-03-18 (日) 20:49:33)
- 2 (2018-03-20 (火) 09:06:21)
- 3 (2019-01-06 (日) 09:24:42)
- 4 (2019-01-12 (土) 21:48:28)
- 5 (2019-01-31 (木) 07:20:51)
- 6 (2019-02-01 (金) 09:43:35)
- 7 (2019-02-02 (土) 09:46:16)
- 8 (2019-02-03 (日) 02:20:06)
- 9 (2019-02-03 (日) 05:36:51)
- 10 (2019-02-04 (月) 05:00:39)
- 11 (2019-02-07 (木) 08:53:49)
- 12 (2019-02-10 (日) 00:28:39)
- 13 (2019-02-10 (日) 03:20:26)
- 14 (2019-02-10 (日) 08:34:09)
- 15 (2019-02-25 (月) 04:44:27)
- 16 (2019-02-25 (月) 08:11:51)
- 17 (2019-02-26 (火) 08:06:29)
- 18 (2019-02-27 (水) 10:02:53)
- 19 (2019-02-28 (木) 09:38:29)
- 20 (2019-03-05 (火) 09:54:13)
- 21 (2019-03-06 (水) 09:17:25)
- 22 (2019-03-07 (木) 07:41:27)
- 23 (2019-03-09 (土) 09:17:38)
- 24 (2019-03-09 (土) 23:41:50)
- 25 (2019-03-10 (日) 07:31:44)
- 26 (2019-03-10 (日) 10:33:46)
- 27 (2019-03-10 (日) 19:16:58)
- 28 (2019-03-17 (日) 07:32:30)
- 29 (2019-03-17 (日) 19:35:17)
- 30 (2019-03-18 (月) 01:12:00)
- 31 (2019-03-22 (金) 07:12:56)
- 32 (2019-03-23 (土) 22:12:31)
- 33 (2019-03-24 (日) 09:14:12)
- 34 (2019-03-26 (火) 08:55:27)
- 35 (2019-03-27 (水) 09:31:09)
- 36 (2019-03-28 (木) 06:30:44)
- 37 (2019-03-29 (金) 09:01:02)
- 38 (2019-03-30 (土) 09:06:01)
- 39 (2019-03-30 (土) 20:22:20)
- 40 (2019-04-01 (月) 08:03:58)
- 41 (2019-04-04 (木) 08:48:41)
- 42 (2019-04-06 (土) 21:07:53)
- 43 (2019-04-11 (木) 06:27:12)
- 44 (2019-05-01 (水) 22:54:09)
- 45 (2019-05-02 (木) 07:50:54)
- 46 (2019-05-02 (木) 19:19:20)
- 47 (2019-05-04 (土) 06:04:16)
- 48 (2019-05-04 (土) 07:35:38)
- 49 (2019-05-06 (月) 07:23:05)
- 50 (2019-07-25 (木) 07:54:44)
- 51 (2020-01-31 (金) 21:32:23)
Bashマニュアルから(自分が)頻繁に使用するであろう機能をピックアップしてまとめる。
- コマンド
- 関数
- 変数
- 展開
- ブレース展開
- チルダ展開
- パラメータ展開
- ${parameter}
- ${parameter:-word}
- ${parameter:=word}
- ${parameter:?word}
- ${parameter:+word}
- ${parameter:offset}、${parameter:offset:length}
- ${!prefix*}、${!prefix@}
- ${!name[*]}、${!name[@]}
- ${#parameter}
- ${parameter#word}、${parameter##word}
- ${parameter%word}、${parameter%%word}
- ${parameter/pattern/string}
- ${parameter^pattern}、${parameter^^pattern}、${parameter,pattern}、${parameter,,pattern}
- コマンド置換
- 算術展開
- プロセス置換
- 単語分割
- ファイル名展開
- リダイレクション
- 参考リンク
コマンド †
ループ †
until †
until test-commands; do consequent-commands; done
条件が成り立つまで繰り返す。
# --- commands --- rm "job.done" { sleep 3 && touch job.done; } & retry=0 until [ -f "job.done" ] do sleep 1 ((retry+=1)) if [ $retry -gt 5 ]; then echo "exceeded retry max:5." exit 1 fi done echo "success" # --- output --- success
while †
while test-commands; do consequent-commands; done
条件が成り立つ間繰り返す。
# --- commands --- touch "job.lock" { sleep 3 && rm job.lock; } & retry=0 while [ -f "job.lock" ] do sleep 1 ((retry+=1)) if [ $retry -gt 5 ]; then echo "exceeded retry max:5." exit 1 fi done echo "job done" # --- output --- job done
for †
for name [ [in [words …] ] ; ] do commands; done for (( expr1 ; expr2 ; expr3 )) ; do commands ; done
wordsの数繰り返す。*1
は、他のプログラミング言語同様の構文。
expr1が最初に評価され、expr2がゼロになるまで、commandsを繰り返し評価する。expr2がゼロでない場合は、expr3が評価される。
# --- commands --- for w in {localhost,192.168.3.4}; do echo "$w" done # --- output --- localhost 192.168.3.4 # --- commands --- for ((i=0; i < 5;i++)); do echo $i done # --- output --- 0 1 2 3 4
参考 https://www.gnu.org/software/bash/manual/bash.html#Looping-Constructs
条件 †
if †
if test-commands; then consequent-commands; [elif more-test-commands; then more-consequents;] [else alternate-consequents;] fi
ifの条件式には、コマンドやBashの条件式(-aや-fなど)、算術式などを指定することができる。
# --- commands --- # Arithmetic expression (( 0 )); echo $? (( 1 )); echo $? #1 ls job.done if ( cd . && test -f job.done ); then echo "job.done found" fi #2 i=0 (( i >= 0 )) ; echo $? if (( i >= 0 )); then echo "i is more than 0" fi #3 i=0 (( (i >= 0) && (i < 1) )) ; echo $? !(( (i >= 0) && (i < 1) )) ; echo $? if !(( (i >= 0) && (i < 1) )); then echo "i is not (i >= 0) && (i < 1)" else echo "i is (i >= 0) && (i < 1)" fi #4 log="FATAL: unexpected error" if [[ "$log" =~ ^FATAL ]]; then echo "Error detected!" fi # --- output --- # Arithmetic expression 1 0 #1 job.done job.done found #2 0 i is more than 0 #3 0 1 i is (i >= 0) && (i < 1) #4 Error detected!
case †
case word in [ [(] pattern [| pattern]…) command-list ;;]… esac
- switch case文のようなコマンド。
- ;; で条件区切りとなる。;&、;;& という指定もある。;& は、フォールスルーする。;;& は、次にマッチする条件があるまで辿り、マッチすればcommand-listを実行する。
- patternは、パターンマッチングのルールによって処理される。
- | で区切れば、ORでpatternを複数書くことができる。
- どのpatternにもマッチしない場合は、終了ステータス0となり、マッチすれば、command-listの終了ステータスとなる。
# --- commands --- do_case() { echo "cond: $cond" case "$cond" in start) echo "match start" ;; s*p) echo "match s*p" ;; {a,b,c}) echo "match {a,b,c}" ;; [0-5]*|[6-9]+) echo "match [0-5]*|[6-9]+" ;; (~) echo "match ~" ;& (fallback) echo "match fallback" ;; ($USER) echo "match \$USER" ;;& (/$USER) echo "match /\$USER" ;; ($USER*) echo "match \$USER*" ;; *) # default echo "cond does not match anything" ;; esac } do_case2() { case $cond in not) test "1" = "0" ;; esac echo "status: $?" } #1 cond="start" do_case #2 cond="stop" do_case #3 cond="a b c" do_case cond={a,b,c} do_case #4 cond="123" do_case cond="678" do_case #5 cond="unknown" do_case #6 cond="$HOME" do_case #7 cond="$USER" do_case #8 cond="not" do_case2 cond="notmatch" do_case2 # --- output --- #1 cond: start match start #2 cond: stop match s*p #3 cond: a b c cond does not match anything cond: {a,b,c} match {a,b,c} #4 cond: 123 match [0-5]*|[6-9]+ cond: 678 cond does not match anything #5 cond: unknown cond does not match anything #6 cond: /home/guest match ~ match fallback #7 cond: guest match $USER match $USER* #8 status: 1 status: 0
select †
- メニューを生成するコマンド。複数のリストから選択させたい場合に便利である。
select name [in words …]; do commands; done
選択肢を選んだ場合、itemに値が入る。REPLYには、readした値が入る。
select item in dog apple orange; do echo you picked $item \($REPLY\) break; done # ----- output ----- 1) dog 2) apple 3) orange #? 1 you picked dog (1)
選択肢にない番号を選んだ場合は、nullがセットされる
select item in dog apple orange; do echo you picked $item \($REPLY\) break done # ----- output ----- 1) dog 2) apple 3) orange #? 4 you picked (4)
何も入力しなかった場合は、再び選択を求められる
select item in dog apple orange; do echo you picked $item \($REPLY\) break done # ----- output ----- 1) dog 2) apple 3) orange #? 1) dog 2) apple 3) orange #?
変数PS3に値を設定すると、選択時のメッセージをカスタマイズすることができる。
PS3="Please select a package you will install: " menus=(package1 package2 package3) select item in ${menus[@]}; do test -z "$item" && { echo "invalid package, please select again" continue } echo "ok, install $item ($REPLY)" break done # ----- output ----- 1) package1 2) package2 3) package3 Please select a package you will install: hoge invalid package, please select again Please select a package you will install: 1 ok, install package1 (1)
((expression)) †
(( expression )) let "expression"
expressionの結果がゼロでない場合、0が返る。そうでない場合は、1。true
、false
を指定しても結果は非ゼロである。
以下一通りの算術演算の結果である。
i=0; (( ++i )) ; echo $? #=> 0 i=1; (( --i )) ; echo $? #=> 1 echo $(( 0 )) #=> 0 (( 0 )) ; echo $? #=> 1 echo $(( 1 )) #=> 1 (( 1 )) ; echo $? #=> 0 echo $(( 1 - 1 )) #=> 0 (( 1 - 1 )) ; echo $? #=> 1 echo $(( 0 + 1 )) #=> 1 (( 0 + 1 )) ; echo $? #=> 0 echo $(( 1**2 )) #=> 1 (( 1**2 )) ; echo $? #=> 0 echo $(( 1 * 0 )) #=> 0 (( 1 * 0 )) ; echo $? #=> 1 echo $(( 0 / 1 )) #=> 0 (( 0 / 1 )) ; echo $? #=> 1 echo $(( 3 % 3 )) #=> 0 (( 3 % 3 )) ; echo $? #=> 1 echo $(( 3 % 1 )) #=> 0 (( 3 % 1 )) ; echo $? #=> 1 echo $(( 2 >> 1 )) #=> 1 (( 2 >> 1 )) ; echo $? #=> 0 echo $(( 1 << 1 )) #=> 2 (( 1 << 1 )) ; echo $? #=> 0 echo $(( 1 >= 0 )) #=> 1 (( 1 >= 0 )) ; echo $? #=> 0 echo $(( 0 <= 0 )) #=> 1 (( 0 <= 0 )) ; echo $? #=> 0 echo $(( 1 > 0 )) #=> 1 (( 1 > 0 )) ; echo $? #=> 0 echo $(( 0 < 1 )) #=> 1 (( 0 < 1 )) ; echo $? #=> 0 echo $(( 1 == 1 )) #=> 1 (( 1 == 1 )) ; echo $? #=> 0 echo $(( 1 != 0 )) #=> 1 (( 1 != 0 )) ; echo $? #=> 0 echo $(( 1 & 3 )) #=> 1 (( 1 & 3 )) ; echo $? #=> 0 echo $(( 1 & 0 )) #=> 0 (( 1 & 0 )) ; echo $? #=> 1 echo $(( 1 ^ 0 )) #=> 1 (( 1 ^ 0 )) ; echo $? #=> 0 echo $(( 1 ^ 1 )) #=> 0 (( 1 ^ 1 )) ; echo $? #=> 1 echo $(( 0 | 1 )) #=> 1 (( 0 | 1 )) ; echo $? #=> 0 echo $(( 1 | 1 )) #=> 1 (( 1 | 1 )) ; echo $? #=> 0 echo $(( 1 >= 0 && 1 <= 2 )) #=> 1 (( 1 >= 0 && 1 <= 2 )) ; echo $? #=> 0 echo $(( 0 >= 1 || 1 <= 2 )) #=> 1 (( 0 >= 1 || 1 <= 2 )) ; echo $? #=> 0 echo $(( 1 ? 0 : 1 )) #=> 0 (( 1 ? 0 : 1 )) ; echo $? #=> 1 echo $(( 1, 0 )) #=> 0 (( 1, 0 )) ; echo $? #=> 1 echo $(( 1, 1 )) #=> 1 (( 1, 1 )) ; echo $? #=> 0 echo $(( true )) #=> 0 (( true )) ; echo $? #=> 1 echo $(( false )) #=> 0 (( false )) ; echo $? #=> 1
[[ expression ]] †
[[ expression ]]
- [[ は組み込みコマンドである。[ と振る舞いが異なることに注意。
- [[ と ]]; の間のexpressionでは展開の扱いが異なる。
- 単語分割、ファイル名展開は行われない。
- チルダ展開、パラメータ・変数展開、算出式展開、コマンド置換、プロセス置換、クオート除去は作用する。
- >、< は、現在のlocaleで作用する。
- ==、!= は、右辺がパターンマーチングのルールで判定される。
extglob
オプションが有効な場合、= は、 == と等しい。nocasematch
オプションが有効な場合、アルファベットの文字大小を無視する。終了ステータスは、== でマッチ、!= でマッチしないを満たす場合は0、そう出ない場合は1。 - =~ は、右辺の文字列をPOSIX拡張正規表現として動作する。正規表現構文が正しくない場合、終了ステータスは2となる。グルーピングにマッチした文字列は、
BASH_REMATCH
配列に保存される。
# --- commands --- touch file_ab line="ls file_*" #1 [[ $($line) =~ (.+)_(a)b ]] echo $? echo "${BASH_REMATCH[@]}" #2 [[ "$line" =~ (.+)_(a)b ]] echo $? echo "${BASH_REMATCH[@]}" #3 [[ $(cat <(ls file_*) ) =~ (.+)_(a)b ]] echo $? echo "${BASH_REMATCH[@]}" #4 echo ~ [[ ~ =~ /home/([^/]+) ]] echo $? echo "${BASH_REMATCH[@]}" #5 pattern='/home/([^/]+)' [[ ~ =~ $pattern ]] echo $? echo "${BASH_REMATCH[@]}" #6 - ""すると、#5とは異なるので注意 pattern='/home/([^/]+)' [[ ~ =~ "$pattern" ]] echo $? echo "${BASH_REMATCH[@]}" : [[ '/home/([^/]+)' =~ "$pattern" ]] echo $? echo "${BASH_REMATCH[@]}" #7 pattern='\.' [[ . =~ $pattern ]] [[ . =~ \. ]] echo $? echo "${BASH_REMATCH[@]}" [[ . =~ "$pattern" ]] [[ . =~ '\.' ]] echo $? echo "${BASH_REMATCH[@]}" [[ "\." =~ '\.' ]] echo $? echo "${BASH_REMATCH[@]}" # --- output --- #1 0 file_ab file a #2 1 #3 0 file_ab file a #4 /home/guest 0 /home/guest guest #5 0 /home/guest guest #6 - ""すると、#5とは異なるので注意 1 0 /home/([^/]+) #7 0 . 1 0 \.
( expression ) †
expressionの値を返す。演算子の優先順位を変えたい時とか。
# --- commands --- i=-1 #1 if (( i > 0 && i < 100 || i < 0 )); then echo "#1 true" else echo "#1 false" fi #2 if (( i > 0 && ( i < 100 || i < 0 ) )); then echo "#2 true" else echo "#2 false" fi # --- output --- #1 #1 true #2 #2 false
! expression †
expressionがtrueならばfalseを返す。
# --- commands --- #1 if (( 1 > 0 )); then echo "true" else echo "false" fi #2 if (( !(1 > 0) )); then echo "true" else echo "false" fi # --- output --- #1 true #2 false
expression1 && expression2、expression1 || expression2 †
expression1で全体の真、偽が決まる場合は、expression2を評価しない。
- expression1 && expression2
- expression1がfalse、expression2は評価しない、結果はfalse
- expression1 || expression2
- expression1がtrue、expression2は評価しない、結果はtrue
(( 1 > 0 )) && { echo "expr2"; } #=> expr2 (( 1 < 0 )) && { echo "expr2"; } #=> (( 1 > 0 )) || { echo "expr2"; } #=> (( 1 < 0 )) || { echo "expr2"; } #=> expr2
参考 https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs
グルーピング †
( list ) †
サブシェルで実行される。変数の割り当ても親シェルには影響しない。
親シェルのPWDはそのままで、一時的にcdして何らかの処理を実行するといった時にも使える。
# --- commands --- PARAM=10 ( PARAM=20 ; echo $PARAM ) echo $PARAM mkdir -p subdir pwd ( cd subdir; pwd; ) pwd # --- output --- 20 10 /home/guest/workspace/bash /home/guest/workspace/bash/subdir /home/guest/workspace/bash
{ list; } †
現在のコンテキストで実行される。}の後にリダイレクトを指定すると、グルーピングされたコマンドの結果をリダイレクト先にまとめて送ることができる。
# --- commands --- PARAM=10 { PARAM=20 ; echo $PARAM ; } echo $PARAM { echo "foo" echo "bar" } > out.txt cat out.txt mkdir -p subdir pwd { cd subdir; pwd; } pwd # --- output --- 20 20 foo bar /home/guest/workspace/bash /home/guest/workspace/bash/subdir /home/guest/workspace/bash/subdir
参考 https://www.gnu.org/software/bash/manual/bash.html#Command-Grouping
Coprocesses †
coproc [NAME] command [redirections]
- サブシェルで非同期にコマンド実行することができる。&を使ったバックグラウンド実行と似ている。coprocで実行されたシェルと実行元のシェル間にはstdinとstdoutのパイプが作られる。pipeを呼び、forkしている。
- コマンドのstdoutは NAME[0]、stdinは NAME[1]
# --- commands --- coproc MY { read line echo "$line, guest!" sleep 1 echo "hello" sleep 1 } echo ${MY[@]} echo "hello" >&${MY[1]} cat - <&${MY[0]} wait # --- output --- 63 60 hello, guest! hello
GNU Parallel †
コマンドを並列実行するための機能。Bashにビルドインされているものではない。
https://www.gnu.org/software/parallel/
並列実行であれば、xargs -Pもカジュアルに実行できるグレートな方法である。
https://linuxjm.osdn.jp/html/GNU_findutils/man1/xargs.1.html
関数 †
name () compound-command [ redirections ] function name [()] compound-command [ redirections ]
- 関数の終了ステータスは、関数内の最後のコマンドの終了ステータスとなる。return [終了ステータス]で関数を抜ける。終了ステータスが指定されない場合は、returnの前にコマンドの終了ステータスとなる。
- 引数は、
$1〜
の様な位置パラメータや$@
、$*
で参照可能である。関数を終了すると、これらの値は関数実行前の状態にリストアされる(関数内でshiftしたりしても、関数を抜けると呼び出し前の状態である)。 FUNCNAME
は、実行中の関数名が入っている。FUNCNAMEは、配列でネストした関数スタックを取得できる。unset -f
で定義済み関数を削除できる。- local指定で、関数ローカルな変数定義ができる。グローバルと同様な名前であっても、local指定していれば関数内ではlocalで指定した値を使用でき、グルーバルを汚染しない。変数の可視性は動的スコープ。
- declare -f、typeset -fで関数定義を確認できる。-Fオプションの場合は名前のみを表示する。
# --- commands --- :> "$PWD/output" hello() { echo "hello func" } # functionがある場合は()は省略可 function hello_with_redirections { echo "$@" } &>> "$PWD/output" shortfunc() { echo "$FUNCNAME"; } shortfunc2() { echo "this is ok"; } # shortfunc3() { echo "this is error, ; is required" } #1 hello #2 hello_with_redirections "hello redirections" #3 cat "$PWD/output" #4 shortfunc #5 shortfunc2 # shortfunc3 # --- output --- #1 hello func #2 #3 hello redirections #4 shortfunc #5 this is ok
関数定義を削除してみる。
# --- commands --- function myfunc() { echo $FUNCNAME } # 関数定義の確認 #1 type myfunc ; echo $? #2 type -t myfunc ; echo $? #3 myfunc # 関数定義を削除 #4 unset -f myfunc ; echo $? #5 type myfunc ; echo $? # --- output --- #1 myfunc は関数です myfunc () { echo $FUNCNAME } 0 #2 function 0 #3 myfunc #4 0 #5 type: myfunc: 見つかりません 1
関数の呼び出しスタックを見る。トップレベルの関数名はmainとなる。
# --- commands --- f0() { echo ${FUNCNAME[@]} } f1() { f0 } f2() { f1 } #1 f2 # --- output --- #1 f0 f1 f2 main
変数の可視性について確認する。以下の例を見た方が理解できる。一連の関数の呼び出しにおいて、呼び出し元(caller)が変数の定義をした場合、呼び出される側では、呼び出し側で上書きされた値が見える。
# --- commands --- function f0() { var="change to f0" echo "6) After change: In $FUNCNAME, var=$var" } function f1() { echo "1) In $FUNCNAME, var=$var" local var="f1" echo "2) Define local: In $FUNCNAME, var=$var" unset var echo "3) After unset: In $FUNCNAME, var=$var" var="change to f1" echo "4) After change: In $FUNCNAME, var=$var" } function f2() { local var="f2" f1 echo "5) In $FUNCNAME, var=$var" f0 } function f3() { var="change to f3" echo "8) After change: In $FUNCNAME, var=$var" } var="global" f2 echo "7) In $FUNCNAME, var=$var" f3 echo "9) In $FUNCNAME, var=$var" # --- output --- 1) In f1, var=f2 2) Define local: In f1, var=f1 3) After unset: In f1, var= 4) After change: In f1, var=change to f1 5) In f2, var=f2 6) After change: In f0, var=change to f0 7) In , var=global 8) After change: In f3, var=change to f3 9) In , var=change to f3
関数定義の確認
# --- commands --- function my1() { : } function my2() { : } typeset -f typeset -F # --- output --- my1 () { : } my2 () { : } declare -f my1 declare -f my2
変数 †
name=[value]
- valueが指定されない場合は、null
- チルダ展開、変数展開、コマンド置換、算術展開、クオートremovalが行われる。単語分割、ファイル名展開は行われない。
- 整数が渡される場合は、数値として評価される
+= †
+=
は、valueへの追加、配列の場合は要素追加する。数値が設定されていても変数属性がintegerとなっていない場合は、単なる文字列結合となるので注意。。
変数が配列の場合は、+=で要素を追加すると末尾に追加される。
b=1 b+=1 echo $b #=> 11 # integerとして定義 declare -i a a=1 a+=1 echo $a #=> 2 # 配列として定義 declare -a arr arr=(1 2) arr+=(1) echo ${arr[*]} #=> 1 2 1 arr+=1 # この場合は、arr[0]の値に1が追加される echo ${arr[*]} #=> 11 2 1 # 連想配列として定義 declare -A map map=(["a"]=1 ["b"]=2) map+=(["c"]=5) echo ${map[@]} #=> 1 2 5 echo ${!map[@]} #=> a b c map["e"]=6 echo ${!map[@]} #=> a b c e echo ${map[@]} #=> 1 2 5 6
参照変数 †
declare -n
で参照変数を定義することができる。unset -n で参照を削除する。-nがない場合は、参照先の変数がunsetされる。配列変数は、変数参照指定ができないが、要素には可能である。
# --- commands --- my() { local -n ref=$1 ref=${ref^^t} } var="test" # 変数名を渡し関数内で操作 my var #1 echo $var declare -n ref2=var declare -n ref3=var #2 -nオプションありでunset declare -p ref2 unset -n ref2 declare -p ref2 #3 -nオプションなしでunset declare -p var declare -p ref3 unset ref3 declare -p ref3 declare -p var # --- output --- #1 TesT #2 -nオプションありでunset declare -n ref2="var" declare -- ref2 #3 -nオプションなしでunset declare -- var="TesT" declare -n ref3="var" declare -n ref3="var" declare: var: 見つかりません
for文で制御変数を参照設定して操作することも可能である。
# --- commands --- a="echo a > out" b="echo b >> out" c="echo c >> out" d="cat out" declare -n ref for ref in {a,b,c,d}; do # a b c dの変数に設定されているコマンド文字列を実行 eval $ref done # --- output --- a b c
位置パラメータ †
- $N、${N}で引数で渡される値を取得できる。Nがシングルな数値でない場合はブレース{}がいる。
- $0は、実行されたファイル名を示す。
- setやshiftでset、unsetできる。
- 関数実行時には、一時的に位置パラメータは置き換えられる。
- $#は位置パラメータの数を示す。
- $- は、setのオプションを示す。
# --- commands --- # file: vars2.sh arg_n=66001 function f() { echo $0 local v= i # 1000ずつ飛ばして表示してみる for i in $(seq 1 1000 $arg_n); do v="\${$i}" printf "# $v = %s " "$(eval echo $v)" done } function f2() { echo "in f2: len(args) = $#" while [ $# -gt 0 ]; do echo $1 shift done } #1 echo $0 #2 f $(seq 1 $arg_n | xargs printf 'A%d ') #3 set a b c d # 位置パラメータに値を設定 while [ $# -gt 0 ]; do echo $1 shift 2 # 1つ飛ばしで done #4 f2 a b c d #5 echo $- # --- output --- #1 vars2.sh #2 vars2.sh # ${1} = A1 # ${1001} = A1001 # ${2001} = A2001 # ${3001} = A3001 # ${4001} = A4001 # ${5001} = A5001 # ${6001} = A6001 # ${7001} = A7001 # ${8001} = A8001 # ${9001} = A9001 # ${10001} = A10001 # ${11001} = A11001 # ${12001} = A12001 # ${13001} = A13001 # ${14001} = A14001 # ${15001} = A15001 # ${16001} = A16001 # ${17001} = A17001 # ${18001} = A18001 # ${19001} = A19001 # ${20001} = A20001 # ${21001} = A21001 # ${22001} = A22001 # ${23001} = A23001 # ${24001} = A24001 # ${25001} = A25001 # ${26001} = A26001 # ${27001} = A27001 # ${28001} = A28001 # ${29001} = A29001 # ${30001} = A30001 # ${31001} = A31001 # ${32001} = A32001 # ${33001} = A33001 # ${34001} = A34001 # ${35001} = A35001 # ${36001} = A36001 # ${37001} = A37001 # ${38001} = A38001 # ${39001} = A39001 # ${40001} = A40001 # ${41001} = A41001 # ${42001} = A42001 # ${43001} = A43001 # ${44001} = A44001 # ${45001} = A45001 # ${46001} = A46001 # ${47001} = A47001 # ${48001} = A48001 # ${49001} = A49001 # ${50001} = A50001 # ${51001} = A51001 # ${52001} = A52001 # ${53001} = A53001 # ${54001} = A54001 # ${55001} = A55001 # ${56001} = A56001 # ${57001} = A57001 # ${58001} = A58001 # ${59001} = A59001 # ${60001} = A60001 # ${61001} = A61001 # ${62001} = A62001 # ${63001} = A63001 # ${64001} = A64001 # ${65001} = A65001 # ${66001} = A66001 #3 a c #4 in f2: len(args) = 4 a b c d #5 hB
特殊パラメータ †
$*
位置パラメータに展開される。ダブルクォートで囲まれていない場合、個々のwordに展開される。そのコンテキストでは、単語分割とパス名展開が行われる。ダブルクオート内で囲まれている場合は、個々のパラメータをIFS変数に指定されている最初の文字で連結された1つの単語に展開される。
単語分割されるコンテキストでは、各位置パラメータは個々のwordに展開される。ダブルクオートなしの場合、wordは単語分割される。単語分割が行われないコンテキストでは、各wordをスペースで区切った1つの文字列に展開される。
"$@"が1つの文字列内で展開されるとき、最初のパラメータは前の文字列にジョインされ、最後のパラメータは、後の文字にジョインされる。
"$*"は、"$1c$2..."に等しい。cはIFSに指定されている先頭の文字。IFSがunsetされる場合はスペースで区切られる。IFSがnullならば、区切り文字なしで結合される。
# --- commands --- function f() { echo "$1" echo "$2" echo "$3" echo "in f(): args= $*" # $1 $2 ... f2 $* # "$1" "$2" ... f2 "$*" # "s $1" ... "$4 z" f2 "s $* z" } function f2() { echo "in f2(): args= $*" echo "num: $#" echo "$1" echo "$2" echo "$3" echo "$4" } function f3() { echo "in f3()" # $1 $2 ... $* } function f4() { echo "in f4()" # "$1" "$2" ... "$*" } function f5() { echo "in f5()" # "$10$2" ... ( IFS=0 && echo "$*" ) # "$1$2" ... ( IFS= && echo "$*" ) } f a b c d f3 "echo hello" f4 "echo hello" f5 "echo" "hello" # --- output --- a b c in f(): args= a b c d in f2(): args= a b c d num: 4 a b c d in f2(): args= a b c d num: 1 a b c d in f2(): args= s a b c d z num: 1 s a b c d z in f3() hello in f4() template.sh: 行 71: echo hello: コマンドが見つかりません in f5() echo0hello echohello
$@
位置パラメータに展開される。単語分割されるコンテキストでは、各位置パラメータは個々のwordに展開される。ダブルクオートなしの場合、wordは単語分割される。単語分割が行われないコンテキストでは、各wordをスペースで区切った1つの文字列に展開される。
"$@"が1つの文字列内で展開されるとき、最初のパラメータは前の文字列にジョインされ、最後のパラメータは、後の文字にジョインされる。
"$@"は、"$1" "$2"に等しい。位置パラメータが無しの場合、"$@"や$@は何も無し。
# --- commands --- function f() { echo "$1" echo "$2" echo "$3" echo "in f(): args= $@" # $1 $2 ... f2 $@ # "$1" "$2" ... f2 "$@" # "s $1" ... "$4 z" f2 "s $@ z" } function f2() { echo "in f2(): args= $@" echo "num: $#" echo "$1" echo "$2" echo "$3" echo "$4" } function f3() { # $1 $2 ... $@ } function f4() { # "$1" "$2" ... "$@" } f a b c d f3 "echo hello" f4 "echo hello" # --- output --- a b c in f(): args= a b c d in f2(): args= a b c d num: 4 a b c d in f2(): args= a b c d num: 4 a b c d in f2(): args= s a b c d z num: 4 s a b c d z hello echo hello: コマンドが見つかりません
$#
位置パラメータの数に展開される。配列などは、${#array[@]}で要素数を取得できる。
# file: test.sh echo $@ echo $# ar=(1 2 3) echo ${#ar[@]} # --- output --- $ ./test.sh 1 2 3 1 2 3 3 3
$?
foregroundで実行される最近のコマンドの終了ステータスに展開される。
# --- commands --- function f_ret_0() { [ 1 -ne 1 ] # result should not be 0 } function f2_ret_0() { [ 1 -ne 1 ] echo "f2" # result should be 0 } function f3_ret_0() { case "$1" in -h) echo "option -h" ;; esac # result should be 0 } function f4_ret_not_0() { case "$1" in -h) echo "option -h" [ 1 -ne 1 ] ;; esac # result should not be 0 } #1 (( 1 == 1 )) echo $? #2: function f ends with exit status not 0 f_ret_0 echo $? #3: function f2 ends with exit status 0 f2_ret_0 echo $? #4: function f3 ends with exit status 0 f3_ret_0 -p echo $? #4: function f4 ends with exit status not 0 f4_ret_not_0 -h echo $? # --- output --- #1 0 #2: function f ends with exit status not 0 1 #3: function f2 ends with exit status 0 f2 0 #4: function f3 ends with exit status 0 0 #4: function f4 ends with exit status not 0 option -h 1
$-
setコマンドやシェル自身によって設定される現在のオプションフラグに展開される。
set -x echo $- set +x echo $- # ----- output ----- + echo hxB hxB + set +x hB
対話シェルで実行した場合 (GNU bash, バージョン 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)
$ echo $- himBH
$$
プロセスIDに展開される。サブシェルの中では、呼び出し元のプロセスIDに展開される。
echo $BASHPID #=> 42071 echo $$ #=> 42071 bash -c 'echo $BASHPID; echo $$' #=> 42079 42079 ( echo $$ ) #=> 42071
$!
バックグラウンドで実行された直近のジョブのプロセスIDに展開される。
$ sleep 30 & [1] 15816 $ echo $! 15816 $ echo "hello" hello $ echo $! 15816 $ sleep 30 & [2] 15870 $ echo $! 15870 $ jobs -l [1]- 15816 Running sleep 30 & [2]+ 15870 Running sleep 30 &
例:nohupと組み合わせて使うケース
#!/usr/bin/env bash pidfile=job.pids nohup bash -c 'i=0; while ((i<10)); do echo job1-$i; ((i++)); done' > stdout-1 2> stderr-1 & echo $! > job.pids nohup bash -c 'i=0; while ((i<10)); do echo job2-$i; ((i++)); done' > stdout-2 2> stderr-2 & echo $! >> job.pids
$0
シェルスクリプト名に展開される。コマンドファイルで呼び出された場合は、ファイル名に展開される。-cオプションで呼び出された場合は、-c string arguments...となる場合の最初の引数が入る。それ以外では、Bashを呼び出すのに使用されたファイルの名前が入る。
# hello.sh echo $0 # --------- ./hello.sh #=> ./hello.sh bash hello.sh #=> hello.sh
bash -c 'echo $0' #=> bash bash -c 'echo $0' hello #=> hello
$ echo $0 bash $ /bin/bash $ echo $0 /bin/bash $ ln -s /bin/bash hogehoge $ ./hogehoge $ echo $0 ./hogehoge
$_
最後に実行されたコマンドの最後の引数に展開される。
# --- commands --- function f() { echo "${1:-func}" } echo '#!/bin/bash echo $_ ' > com.sh chmod +x com.sh #1 ls -G > /dev/null echo $_ #2 bash -c "/bin/echo" echo $_ #3 bash -c "/bin/echo hello" echo $_ #4 f echo $_ #5 f func_arg echo $_ #6 ./com.sh echo $_ #7 ./com.sh hello echo $_ # --- output --- #1 -G #2 /bin/echo #3 hello /bin/echo hello #4 func f #5 func_arg func_arg #6 ./com.sh ./com.sh #7 ./com.sh hello
_ をシェルの変数名として設定することはできない、特殊パラメータとして予約されているためである。
$ _=hoge $ echo $_
配列の操作 †
name[subscript]=value declare -a name declare -a name[subscript] 注:subscriptは無視される name=(value1 value2 … )
配列の主な操作は下例のサンプルの通り。
# --- commands --- #1: 代入 arr= arr[0]=1 echo ${a[@]} arr[5]=1 echo ${arr[@]} echo ${#arr[@]} #2: 宣言 declare -a arr arr[0]=1 echo ${arr[@]} #3: 初期化 arr=(1 2 3 4) echo ${arr[@]} #4: 繰り返し arr=(1 2 3 4) for item in ${arr[@]}; do echo $item done #5: 削除 arr=(1 2 3 4) while (( ${#arr[@]} > 0 )); do echo ${arr[0]} unset arr[0] arr=(${arr[@]}) done #6: まとめて削除 arr=(1 2 3 4) declare -p arr echo ${arr[*]} unset arr declare -p arr #7: まとめて削除 arr=(1 2 3 4) declare -p arr unset arr[*] declare -p arr #8: インデックスで繰り返し arr=(1 2 3 4) for i in ${!arr[*]}; echo ${arr[$i]} done # --- output --- #1: 代入 1 1 2 #2: 宣言 1 #3: 初期化 1 2 3 4 #4: 繰り返し 1 2 3 4 #5: 削除 1 2 3 4 #6: まとめて削除 declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4") 1 2 3 4 declare -- arr #7: まとめて削除 declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4") declare -- arr #8: インデックスで繰り返し template.sh: eval: 行 83: 予期しないトークン `echo' 周辺に構文エラーがあります template.sh: eval: 行 83: ` echo ${arr[$i]}' [2019-03-06 22:35:36] t-moriyasu@ToruMoriyasu-no-iMac ~/workspace/bash $ bash vars7.sh # --- commands --- #1: 代入 arr= arr[0]=1 echo ${a[@]} arr[5]=1 echo ${arr[@]} echo ${#arr[@]} #2: 宣言 declare -a arr arr[0]=1 echo ${arr[@]} #3: 初期化 arr=(1 2 3 4) echo ${arr[@]} #4: 繰り返し arr=(1 2 3 4) for item in ${arr[@]}; do echo $item done #5: 削除 arr=(1 2 3 4) while (( ${#arr[@]} > 0 )); do echo ${arr[0]} unset arr[0] arr=(${arr[@]}) done #6: まとめて削除 arr=(1 2 3 4) declare -p arr echo ${arr[*]} unset arr declare -p arr #7: まとめて削除 arr=(1 2 3 4) declare -p arr unset arr[*] declare -p arr #8: インデックスで繰り返し arr=(1 2 3 4) for i in ${!arr[*]}; do # iは、0、1 ... echo ${arr[$i]} done # --- output --- #1: 代入 1 1 2 #2: 宣言 1 #3: 初期化 1 2 3 4 #4: 繰り返し 1 2 3 4 #5: 削除 1 2 3 4 #6: まとめて削除 declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4") 1 2 3 4 declare -- arr #7: まとめて削除 declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4") declare -- arr #8: インデックスで繰り返し 1 2 3 4
連想配列の操作 †
declare -A name
# --- commands --- #1: 宣言 declare -A map map=([apple]=120 [orange]=100 [grape]=150) declare -p map #2: 参照 echo ${map[apple]} #3: 繰り返し for key in ${!map[@]}; do printf "%s = %s\n" "$key" "${map[$key]}" done #4: 繰り返し2 for value in ${map[@]}; do printf "%s\n" "$value" done #5: 削除 unset map[apple] declare -p map #6: 全削除 unset map declare -p map # --- output --- #1: 宣言 declare -A map=([grape]="150" [apple]="120" [orange]="100" ) #2: 参照 120 #3: 繰り返し grape = 150 apple = 120 orange = 100 #4: 繰り返し2 150 120 100 #5: 削除 declare -A map=([grape]="150" [orange]="100" ) #6: 全削除 declare -- map
展開 †
展開の種類には以下がある。展開の順序を知っておくことで、最終的にどのような結果が得られるのか迷わずに済むだろう。
以下の順序で展開が行われる。(ただし、二重引用符や単一引用符で囲まれる場合は、いくつかの展開がスキップされたりするがここでは割愛する)
- ブレース展開
{a,b} {x..y..incr}
- チルダ展開 、[ プロセス置換 ]
~ ~- ~+ [<(list) or >(list)]
- パラメータと変数展開
${param} etc
- コマンド置換
$(( expression ))
- 算術式展開
$(command) or `command`
- 単語の分割
word word split by $IFS
- パス名展開
. * ? etc
全ての展開後に、クオートの除去( \ ' " )が行われる。コマンド検索が行われ、コマンド実行という流れとなる。
参考 https://www.gnu.org/software/bash/manual/bash.html#Shell-Expansions
ブレース展開 †
{x..y[..incr]}
- {} を使って行なう。ブレース展開を使うと、値の組み合わせのパターン生成に便利な手法。
- 他の展開よりも先に行われるので、変数展開を期待して埋め込んでも意図した結果にならないだろう。
- { や , はバックスラッシュでエスケープできる。
param=10 #=> echo `seq 1 10` #=> 1 2 3 4 5 6 7 8 9 10 echo {1..10..1} #=> 1 2 3 4 5 6 7 8 9 10 echo {1..10..3} #=> 1 4 7 10 echo {1..10} #=> 1 2 3 4 5 6 7 8 9 10 echo {10..1..-1} #=> 10 9 8 7 6 5 4 3 2 1 echo {10..-1} #=> 10 9 8 7 6 5 4 3 2 1 0 -1 echo {10..10..-1} #=> 10 echo {1..$param} #=> {1..10} eval echo {1..$param} #=> 1 2 3 4 5 6 7 8 9 10 echo test{1,} #=> test1 test echo test{1,2,3} #=> test1 test2 test3 echo test{,} #=> test test echo test{{,}} #=> test{} test{} echo {postgresql,pg_hba}.conf #=> postgresql.conf pg_hba.conf
参考 https://www.gnu.org/software/bash/manual/bash.html#Brace-Expansion
チルダ展開 †
や ~-、~+ といった文字列の展開である。
~ | $HOME |
~<user> | ユーザ<user>の$HOME |
~+ | $PWD |
~- | ${OLDPWD:'~-'} |
~N、~+N | dirs +N |
~-N | dirs -N |
実際の動きを以下例で確認してみる。
pwd #=> /home/guest/workspace/bash echo $PWD #=> /home/guest/workspace/bash echo $OLDPWD #=> /home/guest/workspace # ~, ~user echo ~ #=> /home/guest echo ~guest #=> /home/guest echo ~unknownuser #=> ~unknownuser # dirs echo ~+ #=> /home/guest/workspace/bash echo ~- #=> /home/guest/workspace unset OLDPWD #=> echo ~- #=> ~- dirs -l #=> /home/guest/workspace/bash pushd /tmp #=> /tmp ~/workspace/bash echo ~1 #=> /home/guest/workspace/bash echo ~+1 #=> /home/guest/workspace/bash echo ~-1 #=> /tmp
参考 https://www.gnu.org/software/bash/manual/bash.html#Tilde-Expansion
パラメータ展開 †
${parameter} †
parameterの値に置換される。
#!/usr/bin/env bash PARAM="parameter" echo $PARAM #=> parameter
${parameter:-word} †
parameter が設定されていないか空文字列であれば、 wordを展開したものに置換される。そうでなければ、 parameter の値に置換される。
変数のデフォルト値を設定したい場合などによく使う。
echo $USER #=> "moritetu" # ${parameter:-word} SSH_USER="${USER:-"guest"}" echo "$SSH_USER" #=> "moritetu" SSH_OPTS="${OPTIONS:-""}" echo "$SSH_OPTS" #=> ""
${parameter:=word} †
parameterが設定されていないか空文字列であれば、 wordを展開したものがparameter に代入される。その後、parameter の値への置換が行われる。
# ${parameter:=word} # ex1 ${COMMAND:="date"} #=> 2019年 1月30日 水曜日 22時10分22秒 JST # ex2 : ${PARAM:="param1"} echo $PARAM #=> param1
${parameter:?word} †
- parameterが空文字列または設定されていない場合、word を展開したものが標準エラー出力に出力される。
- wordがなければ パラメータが空文字列または設定されていないことを示すメッセージが標準エラー出力に出力される。
- 対話的シェルでなければ、 シェルは終了する。
- parameterに空文字列以外が設定されていれば、 parameter 値への置換が行われる。
# ${parameter:?word} # ex1 ${not_defined_param:?} #=> not_defined_param: パラメータが null または設定されていません # シェルは終了する # ex2 ${not_defined_param2:?"parameter is not defined"} #=> not_defined_param2: parameter is not defined # シェルは終了する # ex3 defined_param2="defined_param2" echo ${defined_param2:?"parameter is not defined"} #=> defined_param2
${parameter:+word} †
parameter が空文字列または設定されていなければ、空文字列に置換される。そうでなければ word を展開したものに置換される。${parameter:-word} の逆。
# ${parameter:+word} echo ${parameter:+"word"} #=> "" parameter="defined" echo ${parameter:+"word"} #=> word
${parameter:offset}、${parameter:offset:length} †
- 部分文字列の展開を行なう。
- length指定がある場合は、parameter を展開したものから最大 length 文字を取り出す。length指定がない場合は、指定したインデックスから末尾までの文字を取り出す。
- 配列の場合は、指定したインデックスからlengthで指定される要素を取り出す。
# ${parameter:offset} parameter="012345" #=> array=(0 1 2 3 4 5) #=> echo ${parameter:2} #=> 2345 echo ${parameter:-1} #=> 012345 echo ${parameter:-2} #=> 012345 echo ${array[@]:-2} #=> 0 1 2 3 4 5 echo ${array[@]:2} #=> 2 3 4 5 echo ${parameter:2:2} #=> 23 echo ${parameter:2:-1} #=> 234 echo ${array[@]:2:1} #=> 2 echo ${array[@]:2:-1} #=> parameter.sh: 行 8: -1: substring expression < 0
${!prefix*}、${!prefix@} †
prefix で始まる全ての変数の名前に展開して、 IFS 特殊変数の最初の文字によって区切る。
# ${!prefix*} parameter_1="p1" #=> parameter_2="p2" #=> echo ${!param*} #=> parameter_1 parameter_2 (IFS="_|" ; echo ${!param*}) #=> parameter 1 parameter 2 # ${!prefix@} parameter_1="p1" #=> parameter_2="p2" #=> echo ${!param@} #=> parameter_1 parameter_2 for p in "${!param@}"; do eval echo \$$p; done #=> p1 p2
${!name[*]}、${!name[@]} †
- 配列のキーのリストに展開される。ハッシュの場合は、キーを取り出すことができる。
- 配列の場合は、インデックスとなる。*と@の違いは、ダブルクォートで囲まれた場合の展開方法が異なる。
array=(a b c d e f) #=> # ${!name[*]} echo ${!array[*]} #=> 0 1 2 3 4 5 # ${!name[@]} echo ${!array[@]} #=> 0 1 2 3 4 5 # ハッシュ定義 declare -g -A map #=> map=(["a"]=0 ["b"]=1 ["c"]=2 ["d"]=3 ["e"]=4 ["f"]=5) #=> echo ${!map[*]} #=> a b c d e f echo ${map[@]} #=> 0 1 2 3 4 5 echo ${!map[@]} #=> a b c d e f
${#parameter} †
パラメータの長さを示す。文字列の場合は文字列の長さ、配列の場合は要素数。
# ${$parameter} strings="12345" #=> echo ${#strings} #=> 5 strings="あいう" #=> echo ${#strings} #=> 3 array=(0 1 2 3 4 5) #=> echo ${#array} #=> 1 echo ${#array[@]} #=> 6
${parameter#word}、${parameter##word} †
- parameterに対しwordで前方一致するパターンを取り除いた結果を返す。#は最短一致、##は最長一致のパターン。
- parameterが@や*の配列変数の場合、全ての要素に対して順番に適用される。
parameter="test.sh.j2" #=> array=(index.html.erb hello.html.erb) #=> # ${parameter#word} echo ${parameter#*.} #=> sh.j2 echo ${array[@]#*.} #=> html.erb html.erb # ${parameter##word} echo ${parameter##*.} #=> j2 echo ${array[@]##*.} #=> erb erb
${parameter%word}、${parameter%%word} †
- parameterに対しwordで後方一致するパターンを取り除いた結果を返す。#は最短一致、##は最長一致のパターン。
- parameterが@や*の配列変数の場合、全ての要素に対して順番に適用される。
parameter="test.sh.j2" #=> array=(index.html.erb hello.html.erb) #=> # ${parameter%word} echo ${parameter%.*} #=> test.sh echo ${array[@]%.*} #=> index.html hello.html # ${parameter%%word} echo ${parameter%%.*} #=> test echo ${array[@]%%.*} #=> index hello
${parameter/pattern/string} †
- parameterのpatternの最長一致する部分をstringに置換する。
- / で始まる場合は、patternにマッチした全てが置換される。
- # で始まる場合は、parameterを展開した値の先頭にマッチ。
- %で始まる場合は、parameterを展開した値の末尾にマッチ。
- parameterが、@や*の場合は、全ての要素に適用される。
parameter="# this is a comment line." #=> array=("# this is a comment line." "# this is not a comment line.") #=> # ${parameter/pattern/string} echo ${parameter/i/I} #=> # thIs is a comment line. echo ${parameter//i/I} #=> # thIs Is a comment lIne. echo ${array[*]//i/I} #=> # thIs Is a comment lIne. # thIs Is not a comment lIne. echo ${parameter/##/#=>} #=> #=> this is a comment line. echo ${parameter/## this/This} #=> This is a comment line. echo ${parameter[@]/## this/This} #=> This is a comment line. This is not a comment line. echo ${parameter/%./\!} #=> # this is a comment line! echo ${parameter/%e/\!} #=> # this is a comment line. echo ${parameter[@]/%./!} #=> # this is a comment line! # this is not a comment line!
${parameter^pattern}、${parameter^^pattern}、${parameter,pattern}、${parameter,,pattern} †
- parameterに含まれるアルファベットの大文字小文字を変換する。
- ^ は、patternにマッチした小文字を大文字に変換し、, は、大文字を小文字に変換する。これらは、最初にマッチした部分のみ置換する。
- ^^ と ,, は、マッチした全ての文字を置換する。
- parameterが、@や* の場合は、全ての要素に適用される。
parameter="hellO, WOrld" #=> array=("hellO, WOrld" "hellO, WOrld2") #=> # ${parameter^pattern} echo ${parameter^[lh]} #=> HellO, WOrld echo ${array[@]^l} #=> hellO, WOrld hellO, WOrld2 # ${parameter^^pattern} echo ${parameter^^l} #=> heLLO, WOrLd echo ${array[@]^^l} #=> heLLO, WOrLd heLLO, WOrLd2 # ${parameter,pattern} echo ${parameter,O} #=> hellO, WOrld echo ${array[@],,O} #=> hello, World hello, World2 # ${parameter,,pattern} echo ${parameter,,O} #=> hello, World echo ${array[@],,O} #=> hello, World hello, World2
参考 https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
コマンド置換 †
$(command) or `command`
コマンド置換は、サブシェルでの実行結果に展開される。$(cat file)は、$(<file)と等しい。$(<file)の方が高速である。ネストすることも可能である。
``は古いスタイルの記述方法。$()の方がバックスラッシュとか気にしなくても良い。ダブルクォートで囲まれたコマンド置換では、単語分割やファイル名展開は行われない。
# --- commands --- #1: Capture stdout stdout=$(echo "hello") echo $stdout #2: Capture stderr stderr=$(echo "hello" >&2) echo $stderr #3: Capture stdout, stderr std=$(echo "stdout"; echo "stderr" >&2) echo $std #4: Read from stdin echo "pwd" > pwd.txt $(<pwd.txt) #5: Split data into array :> num.txt for i in $(seq 1 10); do echo "$i col1 col2" >> num.txt done OLDIFS="$IFS" IFS=" " lines=( $(<num.txt) ) IFS="$OLDIFS" for l in "${lines[@]}"; do echo $l done #6: Nest echo $(echo $(pwd)) echo `echo \`pwd\`` # --- output --- #1: Capture stdout hello #2: Capture stderr hello #3: Capture stdout, stderr stderr stdout #4: Read from stdin /home/guest/workspace/bash #5: Split data into array 1 col1 col2 2 col1 col2 3 col1 col2 4 col1 col2 5 col1 col2 6 col1 col2 7 col1 col2 8 col1 col2 9 col1 col2 10 col1 col2 #6: Nest /home/guest/workspace/bash /home/guest/workspace/bash
算術展開 †
$(( expression ))
全体が、ダブルクォートで囲まれていても展開される。
パラメータ展開、コマンド置換、クォート除去は行われる。結果は算術式として評価される。算術展開はネスト可能。
# +++++++++++++++ Commands +++++++++++++++ #1: 計算 echo $(( 1 + 2 )) echo "$(( 100 * 20 ))" # [Output] > #1: 計算 3 2000 # +++++++++++++++ Commands +++++++++++++++ #2: 変数を使った場合 # Shell VariablesはOK、パラメータ$はなくても良い i=10 echo $(( i + 9 )) echo $(( $i + 9 )) echo $(( "$i" + 9 )) # [Output] > #2: 変数を使った場合 19 19 19 # +++++++++++++++ Commands +++++++++++++++ #3: 関数を使ってみるがダメ counter() { echo 1 } echo $(( counter * 10 )) # [Output] > #3: 関数を使ってみるがダメ 0 # +++++++++++++++ Commands +++++++++++++++ #4: ネスト echo $(( $(( 1 + 10 )) * 10 )) echo $(( "$(( 1 + 10 ))" * 10 )) # [Output] > #4: ネスト 110 110
関連 算術式
プロセス置換 †
>(list)
は、ファイルへの出力がlistへの入力となる。<(list)
は、listの出力がファイルへの入力となる。
サンプル lsの結果で*.shファイルのみを出力
サンプル a.txtとb.txtをdiffの入力ファイルとして比較
サンプル
参考
単語分割 †
シェルは、ダブルクォートで囲まれてない、パラメータ展開、コマンド置換、算術展開の結果をスキャンし単語分割する。
IFSパラメータにセットされている各文字をフィールド区切り文字として単語に分割する。
# +++++++++++++++ Commands +++++++++++++++ #1: <space>, <tab>, <newline>を含む文字列 a="This is at test." array=( $a ) for i in {0..3}; do echo ${array[$i]} done # [Output] > #1: <space>, <tab>, <newline>を含む文字列 This is at test. # +++++++++++++++ Commands +++++++++++++++ #2: IFSをunsetする # <space>, <tab>, <newline>が作用する unset IFS a="This is at test." array=( $a ) for i in {0..3}; do echo ${array[$i]} done # [Output] > #2: IFSをunsetする This is at test. # +++++++++++++++ Commands +++++++++++++++ #3: IFSを別の区切り文字にする IFS='|_' a="This is at test.|foo_bar_|_1" array=( $a ) for i in {0..5}; do echo ${array[$i]} done # [Output] > #3: IFSを別の区切り文字にする This is at test. foo bar 1 # +++++++++++++++ Commands +++++++++++++++ #4: IFSがnullの場合は分割されない IFS= a="This is at test.|foo_bar_|_1" array=( $a ) for i in {0..1}; do echo ${array[$i]} done # [Output] > #4: IFSがnullの場合は分割されない This is at test.|foo_bar_|_1 # +++++++++++++++ Commands +++++++++++++++ #5: クォートで囲まれた動作 function myfunc() { echo "args: $@" echo "len : $#" } #5:1 a= myfunc $a myfunc "$a" #5:1 a="1 2" myfunc $a myfunc "$a" # [Output] > #5: クォートで囲まれた動作 #5:1 args: len : 0 args: len : 1 #5:1 args: 1 2 len : 2 args: 1 2 len : 1
ファイル名展開 †
単語分割の後、-fオプションが設定されていなければ、*
、?
、[
の各文字がスキャンされる。これらの文字の1つが出現した場合、パターンとして認識し、パターンにマッチするアルファベット順に整列されたファイルリストに置換される。マッチするファイルが見つからず、nullglob
オプションが無効の場合、単語はそのまま残される。nullglob
オプションが設定されており、マッチするファイルが見つからない場合、単語は削除される。failglob
オプションが設定されており、マッチするファイルが見つからない場合、エラーメッセージが表示されコマンドは実行されない。nocaseglob
オプションが有効な場合、マッチングはアルファベットの大小文字に関わらず実行される。
ファイル名展開にパターンが使われるとき、dotglob
オプションが設定されていないならば、ファイル名の先頭または /
の直後の.
はマッチする。.
、..
は、dotglobオプションに関わらずマッチする。
GLOBIGNORE変数が設定されている場合、その変数に指定されるパターンにマッチするファイルはリストから除かれる。GLOBIGNOREが設定されている場合、doglobオプションが有効であるのと同じ効果をもつ。
# +++++++++++++++ Commands +++++++++++++++ #1: File matching tree -a filenames/ #1-1: [...] ls filenames/[WQ]ORLD.txt #1-2: ? ls filenames/?ORLD.txt #1-3: * ls filenames/*.txt # [Output] > #1: File matching filenames/ ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt └── hello.txt 0 directories, 4 files #1-1: [...] filenames/QORLD.txt filenames/WORLD.txt #1-2: ? filenames/QORLD.txt filenames/WORLD.txt #1-3: * filenames/QORLD.txt filenames/WORLD.txt filenames/hello.txt # +++++++++++++++ Commands +++++++++++++++ #2: dotglob tree -a filenames/ #2-1: dotglob off ls filenames/*.txt #2-2: dotglob on shopt -s dotglob ls filenames/*.txt # [Output] > #2: dotglob filenames/ ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt └── hello.txt 0 directories, 4 files #2-1: dotglob off filenames/QORLD.txt filenames/WORLD.txt filenames/hello.txt #2-2: dotglob on filenames/.helloworld.txt filenames/QORLD.txt filenames/WORLD.txt filenames/hello.txt # +++++++++++++++ Commands +++++++++++++++ #3: nocaseglob tree -a filenames/ #3-1: nocaseglob off ls filenames/[wq]orld.txt #3-2: nocaseglob on shopt -s nocaseglob ls filenames/[wq]orld.txt # [Output] > #3: nocaseglob filenames/ ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt └── hello.txt 0 directories, 4 files #3-1: nocaseglob off ls: filenames/[wq]orld.txt: No such file or directory #3-2: nocaseglob on filenames/QORLD.txt filenames/WORLD.txt # +++++++++++++++ Commands +++++++++++++++ #4: nullglob cd filenames tree -a . #4-1: nullglob off ls [wq]orld.txt #4-2: nullglob on shopt -s nullglob ls [wq]orld.txt # [Output] > #4: nullglob . ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt └── hello.txt 0 directories, 4 files #4-1: nullglob off ls: [wq]orld.txt: No such file or directory #4-2: nullglob on QORLD.txt WORLD.txt hello.txt # +++++++++++++++ Commands +++++++++++++++ #5: failglob cd filenames tree -a . #5-1: failglob off ls [wq]orld.txt #5-2: failglob on shopt -s failglob ls [wq]orld.txt # [Output] > #5: failglob . ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt └── hello.txt 0 directories, 4 files #5-1: failglob off ls: [wq]orld.txt: No such file or directory #5-2: failglob on template.sh: 行 43: 一致しません: [wq]orld.txt # +++++++++++++++ Commands +++++++++++++++ #6: GLOBIGNORE cd filenames ls * #6-1: set GLOBIGNORE="*o.txt" GLOBIGNORE="*o.txt" ls * #6-2: set GLOBIGNORE="*o.txt:.*" # .で始まるファイルを除外したい GLOBIGNORE="*o.txt:.*" ls * #6-3: unset GLOBIGNORE unset GLOBIGNORE ls * # [Output] > #6: GLOBIGNORE QORLD.txt WORLD.txt hello.txt #6-1: set GLOBIGNORE=*o.txt .helloworld.txt QORLD.txt WORLD.txt #6-2: set GLOBIGNORE=*o.txt:.* QORLD.txt WORLD.txt #6-3: unset GLOBIGNORE QORLD.txt WORLD.txt hello.txt
パターンマッチング †
* †
nullを含む任意の文字列。globstar
オプションがonの時、**
はサブディレクトリにもマッチする。
$ tree -a filenames/ filenames/ ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt ├── hello.txt └── subidr ├── hello.txt └── sub2dir └── hello.txt 2 directories, 6 files $ ls ./filenames/**/*.txt ./filenames/subidr/hello.txt $ shopt -s globstar $ ls ./filenames/**/*.txt ./filenames/QORLD.txt ./filenames/subidr/hello.txt ./filenames/WORLD.txt ./filenames/subidr/sub2dir/hello.txt ./filenames/hello.txt
? †
任意の1文字
$ tree -a filenames/ filenames/ ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt ├── hello.txt └── subidr ├── hello.txt └── sub2dir └── hello.txt 2 directories, 6 files $ ls filenames/?ello.txt filenames/hello.txt
[…] †
[ ]で囲まれる文字のいずれかにマッチする。ハイフンで区切られた文字ペアは範囲を示す。
例:[a-d] -> [abcd]
[!
、[^
はnot matchとなる。
$ tree -a filenames/ filenames/ ├── .helloworld.txt ├── QORLD.txt ├── WORLD.txt ├── hello.txt └── subidr ├── hello.txt └── sub2dir └── hello.txt 2 directories, 6 files $ ls filenames/[^h]*.txt filenames/QORLD.txt filenames/WORLD.txt $ ls filenames/[!h]*.txt filenames/QORLD.txt filenames/WORLD.txt
文字クラス
以下で、POSIX標準で定義されている文字クラスが使用可能。
[:class:] class are: alnum alpha ascii blank cntrl digit graph lower print punct space upper word xdigit
word
は、文字、数値、_にマッチする。
$ tree filenames/class/ filenames/class/ └── hoge_foo.txt 0 directories, 1 file $ ls filenames/class/hoge[[:word:]]foo.txt filenames/class/hoge_foo.txt $ ls filenames/class/hoge[[:ascii:]]foo.txt filenames/class/hoge_foo.txt $ ls filenames/class/hoge[[:alpha:]]foo.txt ls: filenames/class/hoge[[:alpha:]]foo.txt: No such file or directory
等価クラス([=c=])
cと等価なキャラクタにマッチする。
[=e=]は、 è、 é、 ê、 ë などにもマッチする。
照合シンボル([.symbol.])
照合シンボルにマッチする。
$ ls a á à â å ä ã ª anaconda-ks.cfg file_e.txt file_é.txt original-ks.cfg $ ls | grep "[[=a=]]" a á à â å ä ã ª anaconda-ks.cfg original-ks.cfg $ ls | grep "a" a anaconda-ks.cfg original-ks.cfg
参考 http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_02_04
リダイレクション †
execを使った入出力のリダイレクト †
execコマンドでリダイレクトだけを指定すると、シェル自身のファイル記述子とデバイスの対応を変更することができる。
例 execを使った入力のリダイレクト †
$ ls Hello* Hello Hello|World.txt $ ls Hello[[.vertical-line.]]World.txt Hello|World.txt $ ls stdout-* stdout-1 stdout-2 $ ls stdout[[.dash.]][12] stdout-1 stdout-2
例 execを使った出力のリダイレクト †
shopt -s nullglob for conf in /etc/*.conf; do echo $conf done shopt -u nullglob
使い道はさておき、こんなのもできる。
shopt -s extglob
特定の式や文の出力をまとめてリダイレクトしたい場合は、ブレースを使った複合コマンドでも処理できる。
例 複合コマンドの出力をファイルにリダイレクトする
# +++++++++++++++ Commands +++++++++++++++ #1: ?(pattern-list) tree . #1-1: case: extglob off #ls foo?(_*).txt #1-2: case: extglob on shopt -s extglob ls foo?(_*).txt # ***** <[Output]> ***** #1: ?(pattern-list) . ├── bar.txt ├── foo.txt └── foo_bar.txt 0 directories, 3 files #1-1: case: extglob off 予期しないトークン `(' 周辺に構文エラーがあります `ls foo?(_*).txt' #1-2: case: extglob on foo.txt foo_bar.txt