GNU Bashマニュアルを参照しながら、実機での挙動を確認した個人学習メモ。
コマンド †ループ †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の数繰り返す。 # --- 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
# --- 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。 以下一通りの算術演算の結果である。 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 ]]
# --- 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を評価しない。
(( 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 ) †サブシェルで実行される。変数の割り当ても親シェルには影響しない。 # --- 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]
# --- 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 ]
# --- 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]
+= †
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 参照変数 †
# --- 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 位置パラメータ †
# --- 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 展開 †展開の種類には以下がある。展開の順序を知っておくことで、最終的にどのような結果が得られるのか迷わずに済むだろう。
全ての展開後に、クオートの除去( \ ' " )が行われる。コマンド検索が行われ、コマンド実行という流れとなる。 参考 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 チルダ展開 †や ~-、~+ といった文字列の展開である。
実際の動きを以下例で確認してみる。 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} # 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} †
# ${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="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="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="# 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="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 関連 算術式 プロセス置換 †
サンプル 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オプションが設定されていなければ、 ファイル名展開にパターンが使われるとき、 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を含む任意の文字列。 $ 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]
$ 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
$ 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と等価なキャラクタにマッチする。 $ 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 照合シンボル([.symbol.]) 照合シンボルにマッチする。 $ 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 参考
注意ワイルドカード展開時の注意 パターンにマッチしない場合、メタ文字がそのまま展開される。 対処法として、nullglobを設定する方法がある。 shopt -s nullglob for conf in /etc/*.conf; do echo $conf done shopt -u nullglob extglobで有効なパターンマッチング †以下の5つのパターンマッチングは、 shopt -s extglob pattern-listには、 先頭の、?、*、+、@、!、は正規表現式で使用されるメタ記号の意味と同様。@は、指定されるパターンが1つ含まれることを示す。([characters]のようなものか?) ?(pattern-list) †指定されたパターンにマッチするものが0または1つある。 # +++++++++++++++ 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 *(pattern-list) †指定されたパターンにマッチするものが、0以上ある。 # +++++++++++++++ Commands +++++++++++++++ #2: *(pattern-list) shopt -s extglob tree . #2-1: ls *(foo*)bar.txt # ***** <[Output]> ***** #2: *(pattern-list) . ├── bar.txt ├── foo.txt └── foo_bar.txt 0 directories, 3 files #2-1: bar.txt foo_bar.txt +(pattern-list) †指定されたパターンにマッチするものが、1つ以上ある。 # +++++++++++++++ Commands +++++++++++++++ #3: +(pattern-list) shopt -s extglob tree . #3-1: bar.txtはマッチしないはず ls +(foo*)bar.txt # ***** <[Output]> ***** #3: +(pattern-list) . ├── bar.txt ├── foo.txt └── foo_bar.txt 0 directories, 3 files #3-1: bar.txtはマッチしないはず foo_bar.txt @(pattern-list) †指定したパターンにマッチするものが1つある。 # +++++++++++++++ Commands +++++++++++++++ #4: @(pattern-list) shopt -s extglob tree . #4-1: *との違いを確認 ls foo*(_bar).txt #4-2: *との違いを確認 ls foo@(_bar).txt # ***** <[Output]> ***** #4: @(pattern-list) . ├── bar.txt ├── foo.txt └── foo_bar.txt 0 directories, 3 files #4-1: *との違いを確認 foo.txt foo_bar.txt #4-2: *との違いを確認 foo_bar.txt !(pattern-list) †指定したパターンにマッチしない。 # +++++++++++++++ Commands +++++++++++++++ #5: !(pattern-list) shopt -s extglob tree . #5-1: ls foo!(_bar).txt # ***** <[Output]> ***** #5: !(pattern-list) . ├── bar.txt ├── foo.txt └── foo_bar.txt 0 directories, 3 files #5-1: foo.txt リダイレクション †コマンドの入出力先を変更したり操作することができる。リダイレクトは、左から右に表示されている順で処理される。 入力のリダイレクト †[n]<word wordからの入力のために記述子 n がオープンされる。nの指定がない場合は、標準入力になる。 # +++++++++++++++ Commands +++++++++++++++ echo "line1 line2" > filename.out while read line; do echo $line done < filename.out # ***** <[Output]> ***** line1 line2 出力のリダイレクト †[n]>[|]word wordへの出力のため記述子 n を開く。nの指定がない場合は、標準出力となる。wordの評価の結果のファイルが存在しない場合は、新しく作成される。存在していた場合は、サイズ0に切り詰められる。
# +++++++++++++++ [TEST-2 Commands] +++++++++++++++ # ファイルが存在しない場合は新規作成される echo "line1 line2" > filename.out cat filename.out # ***** [TEST-2 Output] ***** line1 line2 # +++++++++++++++ [TEST-3 Commands] +++++++++++++++ # noclobberが有効の場合は上書きできない :>filename.out set -o noclobber echo "line1 line2" > filename.out # ***** [TEST-3 Output] ***** hoge.sh: 行 40: filename.out: 存在するファイルを上書きできません # +++++++++++++++ [TEST-4 Commands] +++++++++++++++ # >| の場合は上書きできる :>filename.out set -o noclobber echo "line1 line2" >| filename.out cat filename.out # ***** [TEST-4 Output] ***** line1 line2 追記型出力のリダイレクト †[n]>>word wordの評価の結果に追記出力するために記述子 nがオープンされる。nの指定がない場合は標準出力となる。ファイルが存在しない場合は新規に作成される。 # +++++++++++++++ [TEST-5 Commands] +++++++++++++++ # >>は追記 # 新規作成 echo "[1]line1" > filename.out cat filename.out # 追記 echo "[2]line2" >> filename.out cat filename.out # 切り詰め echo "[3]line1" > filename.out cat filename.out # ***** [TEST-5 Output] ***** [1]line1 [1]line1 [2]line2 [3]line1 標準出力と標準エラー出力のリダイレクト †&>word >&word 以下と等価 >word 2>&1 # +++++++++++++++ [TEST-6 Commands] +++++++++++++++ # >& word # &> word # >word 2>&1 dump() { echo "stdout" echo "stderr" >&2 } dump > stdout.out 2> stderr.out #1 stdout.out cat stdout.out #2 stderr.out cat stderr.out #3 stdout-stderr.out (&>) dump &> stdout-stderr.out cat stdout-stderr.out #4 stdout-stderr.out (2>&1) dump > stdout-stderr.out 2>&1 cat stdout-stderr.out # ***** [TEST-6 Output] ***** # >& word # &> word # >word 2>&1 #1 stdout.out stdout #2 stderr.out stderr #3 stdout-stderr.out (&>) stdout stderr #4 stdout-stderr.out (2>&1) stdout stderr ヒアドキュメント †[n]<<[-]word here-document delimiter
# +++++++++++++++ [TEST-7 Commands] +++++++++++++++ # ヒアドキュメント <<word param=hello cat <<STRING $param \$param $(pwd) STRING # ***** [TEST-7 Output] ***** # ヒアドキュメント <<word hello $param /home/guest/workspace/bash # +++++++++++++++ [TEST-8 Commands] +++++++++++++++ # ヒアドキュメント <<"word" param=hello cat <<"STRING" $param $(pwd) STRING # ***** [TEST-8 Output] ***** # ヒアドキュメント <<word $param $(pwd) # +++++++++++++++ [TEST-9 Commands] +++++++++++++++ # ヒアドキュメント <<'word' #1: 全体をクォート param=hello cat <<'STRING' $param $(pwd) STRING #2: 一部をクォート param=hello cat <<'STR'ING $param $(pwd) STRING # ***** [TEST-9 Output] ***** # ヒアドキュメント <<'word' #1: 全体をクォート $param $(pwd) #2: 一部をクォート $param $(pwd) # +++++++++++++++ [TEST-10 Commands] +++++++++++++++ # ヒアドキュメント <<-word param=hello cat <<-STRING $param $(pwd) STRING # ***** [TEST-10 Output] ***** # ヒアドキュメント <<-word hello /home/guest/workspace/bash wordは変数でも可能か?(マニュアルにはNo parameter and variable expansion, ... is performed on word とあるが。。)。試すと正しく展開されているようである。 # +++++++++++++++ [TEST-11 Commands] +++++++++++++++ # wordでの変数展開は無効である param=STRING cat <<$(echo $param) $param $(pwd) $(echo $param) echo foo # ***** [TEST-11 Output] ***** # wordでの変数展開は無効である STRING /home/guest/workspace/bash foo ヒアストリング †[n]<<< word wordは、パス名展開、単語分割は行われない。結果は単一の文字列(改行あり)として入力に渡される。 # +++++++++++++++ [TEST-12 Commands] +++++++++++++++ #1 ヒアストリング read first second <<< "Hello World" echo $first echo $second # ***** [TEST-12 Output] ***** #1 ヒアストリング Hello World # +++++++++++++++ [TEST-13 Commands] +++++++++++++++ #2 以下でも同様なことが可能 echo "Hello World" | { read first second echo $first echo $second } # ***** [TEST-13 Output] ***** #2 以下でも同様なことが可能 Hello World # +++++++++++++++ [TEST-14 Commands] +++++++++++++++ #3 変数展開 param1=Hello param2=World read first second <<< "$param1 $param2" echo $first echo $second # ***** [TEST-14 Output] ***** #3 変数展開 Hello World # +++++++++++++++ [TEST-15 Commands] +++++++++++++++ #4 関数で入力を処理、パイプでもできるが・・・ param1=Hello param2=World function my() { cat - } my <<< "$param1 $param2" echo "$param1 $param2" | my # ***** [TEST-15 Output] ***** #4 関数で入力を処理、パイプでもできるが・・・ Hello World Hello World ファイルディスクリプタの複製 †# 入力 [n]<&word # 出力 [n]>&word
標準入力の複製 †lsofコマンドでディスクリプタの使用状況も合わせて確認している。 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ # 入力用のファイルを作成 :> input.txt for i in {1..5}; do echo $i >> input.txt done #lsof -p $$ # 標準入力0を複製し記述子60とする exec 60<&0 lsof -p $$ # 標準入力をinput.txtにする exec < input.txt #lsof -p $$ # input.txtから読み込み for i in {1..5}; do read line && echo $line done # 標準入力を戻し、記述子60を閉じる exec 0<&60- lsof -p $$ # ******** [TEST-1 Output] ******** COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 47422 guest cwd DIR 1,8 2312 32874335 /home/guest/workspace/bash bash 47422 guest txt REG 1,8 945256 12433654 /usr/local/Cellar/bash/4.4.23/bin/bash bash 47422 guest txt REG 1,8 841456 32850597 /usr/lib/dyld bash 47422 guest txt REG 1,8 1175449600 32853756 /private/var/db/dyld/dyld_shared_cache_x86_64 bash 47422 guest 0r REG 1,8 402 36951947 /private/var/folders/jt/gf6q8xn50szb_fbqq1j36yzh0000gn/T/sh-thd.fQezbf bash 47422 guest 1 PIPE 0xcc5656065833aed5 16384 ->0xcc5656065833ae15 bash 47422 guest 2 PIPE 0xcc5656065833ab15 16384 ->0xcc5656065833af95 bash 47422 guest 60r REG 1,8 402 36951947 /private/var/folders/jt/gf6q8xn50szb_fbqq1j36yzh0000gn/T/sh-thd.fQezbf bash 47422 guest 255r REG 1,8 402 36951899 /home/guest/workspace/bash/_exec.sh 1 2 3 4 5 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME bash 47422 guest cwd DIR 1,8 2312 32874335 /home/guest/workspace/bash bash 47422 guest txt REG 1,8 945256 12433654 /usr/local/Cellar/bash/4.4.23/bin/bash bash 47422 guest txt REG 1,8 841456 32850597 /usr/lib/dyld bash 47422 guest txt REG 1,8 1175449600 32853756 /private/var/db/dyld/dyld_shared_cache_x86_64 bash 47422 guest 0r REG 1,8 402 36951947 /private/var/folders/jt/gf6q8xn50szb_fbqq1j36yzh0000gn/T/sh-thd.fQezbf bash 47422 guest 1 PIPE 0xcc5656065833aed5 16384 ->0xcc5656065833ae15 bash 47422 guest 2 PIPE 0xcc5656065833ab15 16384 ->0xcc5656065833af95 bash 47422 guest 255r REG 1,8 402 36951899 /home/guest/workspace/bash/_exec.sh 標準出力の複製 †# ++++++++++++++++++++++ [TEST-2 Commands] ++++++++++++++++++++++ # コマンドキャプチャする # 標準出力とエラー出力を複製する exec 10>&1 11>&2 # 標準出力とエラー出力をそれぞれファイルにリダイレクトする exec > stdout.out 2> stderr.out # 以降の出力は全てファイルに書かれる echo "hogehoge" echo "foofoo" >&2 # 記述子10と11を閉じる exec 1>&10- 2>&11- # stdout.out cat stdout.out # stderr.out cat stderr.out # ******** [TEST-2 Output] ******** hogehoge foofoo その他サンプル †execコマンドでリダイレクトだけを指定すると、シェル自身のファイル記述子とデバイスの対応を変更することができる。 例 execを使った入力のリダイレクト
例 execを使った出力のリダイレクト
使い道はさておき、こんなのもできる。 :> jobs # stdoutを63に複製 exec 63>&1 # 63をプロセス置換でpipeの入力にする exec 63> >( echo "child pid: $BASHPID" while read line; do echo "$(date): received: $line" | tee "$line.log" touch "$line.done" done ) i=0 declare -r MAX_COUNT=10 echo "parent pid: $BASHPID" while read line; do if [ "$line" = "q" ] || [ "$line" = "quit" ]; then break fi # プロセス置換のコマンドに送る echo "$line" >&63 i=1 until [ -f "$line.done" ] || (( i > $MAX_COUNT )); do sleep 1 ((i+=1)) done if (( i>$MAX_COUNT )); then echo "exceeded $MAX_COUNT seconds" fi done < <(tail -f jobs) # jobsファイルから次のジョブを受け取る # stdoutを戻し63を閉じる exec 1>&63- # ---- output ----- parent pid: 86757 child pid: 86772 2020年 1月 1日 水曜日 23時26分25秒 JST: received: job1 2020年 1月 1日 水曜日 23時26分28秒 JST: received: job1 2020年 1月 1日 水曜日 23時26分29秒 JST: received: job1 # ----- 別シェルから ----- echo "job1" > jobs echo "job1" > jobs echo "job1" > jobs 特定の式や文の出力をまとめてリダイレクトしたい場合は、ブレースを使った複合コマンドでも処理できる。 例 複合コマンドの出力をファイルにリダイレクトする
ファイルディスクリプタの移動 †# 入力 [n]<&digit- # 出力 [n]>&digit-
読み書き可能なファイルディスクリプタ †[n]<>word # ++++++++++++++++++++++ [TEST-3 Commands] ++++++++++++++++++++++ echo "hello" > file.txt exec 10<>file.txt read line <&10 echo "read: $line" echo "world" >&10 # read line cat read "world" cat file.txt # ******** [TEST-3 Output] ******** read: hello hello world コマンド実行 †コマンドの展開 †word(=command) word(args...) ... 最初のwordはコマンド名として受領され、残りはコマンド引数として渡される。 varname=value command [args...] varnameは、commandの実行環境に加えられる。 varname=value varname=valueは、現在の実行環境に作用する。 $ FOO=var echo "foo" foo $ echo $FOO $ FOO=var $ echo $FOO var コマンドの探索と実行 †
# ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ # コマンドが見つからない場合呼ばれる command_not_found_handle() { echo "not found: $@" } # コマンドを作成 cat > mycmd <<'STR' #!/usr/bin/env bash echo $0 STR chmod +x mycmd # 存在しないコマンド # command_not_found_handleが呼ばれる foo args1 args2 echo $? unset command_not_found_handle foo args1 args2 echo $? # mycmdをPATHで探索可能にする PATH=`pwd`:$PATH # hashはまだ空 hash -t mycmd # コマンド実行、hashに登録される mycmd # mycmdはhashで見つかる hash -t mycmd # hashから削除 hash -d mycmd hash -t mycmd # hashに登録 hash mycmd hash -t mycmd # ******** [TEST-1 Output] ******** # コマンドが見つからない場合呼ばれる # コマンドを作成 # 存在しないコマンド # command_not_found_handleが呼ばれる not found: foo args1 args2 0 hoge.sh: 行 72: foo: コマンドが見つかりません 127 # mycmdをPATHで探索可能にする # hashはまだ空 hoge.sh: 79 行: hash: mycmd: 見つかりません # コマンド実行、hashに登録される /home/guest/workspace/bash/mycmd # mycmdはhashで見つかる /home/guest/workspace/bash/mycmd # hashから削除 hoge.sh: 89 行: hash: mycmd: 見つかりません # hashに登録 /home/guest/workspace/bash/mycmd コマンド実行環境 †以下の実行環境を持つ。
異なるシェル実行環境において以下環境は引き継がれる。
# ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ trap "echo trap:parent && exit 0" 0 ( trap "echo trap:child && exit 0" 0 ) ( echo "shell" ) alias myls='ls -l' cat <<'STR' > my.sh #!/bin/bash echo "$0: $PWD" echo "$0: PARAM=$PARAM" echo "$0: MYENV=$MYENV" echo "$0: MYENV2=$MYENV2" echo "$0: $(alias myls)" shopt -p extglob shopt -s extglob shopt -p extglob bash my-sub.sh STR cat <<'STR' > my-sub.sh #!/bin/bash echo "$0: $PWD" echo "$0: PARAM=$PARAM" echo "$0: MYENV=$MYENV" echo "$0: MYENV2=$MYENV2" echo "$0: $(alias myls)" shopt -p extglob STR chmod +x my*.sh MYENV=myenv export MYENV2=myenv2 PARAM=my ./my.sh # ******** [TEST-1 Output] ******** trap:child shell ./my.sh: /home/guest/workspace/bash ./my.sh: PARAM=my ./my.sh: MYENV= ./my.sh: MYENV2=myenv2 ./my.sh: line 6: alias: myls: not found ./my.sh: shopt -u extglob shopt -s extglob my-sub.sh: /home/guest/workspace/bash my-sub.sh: PARAM=my my-sub.sh: MYENV= my-sub.sh: MYENV2=myenv2 my-sub.sh: 6 行: alias: myls: 見つかりません my-sub.sh: shopt -u extglob trap:parent 環境の追加や削除 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ cat <<'STR' > main.sh #!/bin/bash echo "$0: MYENV=$MYENV" echo "$0: PGPORT=$PGPORT" echo "$0: PGDATABASE=$PGDATABASE" # 一部の環境を削除 export -n PGPORT declare +x PGDATABASE # DEBUGはシェルパラメータで以下実行環境でのみ引き継ぎ DEBUG=1 ./sub.sh STR cat <<'STR' > sub.sh #!/bin/bash echo "$0: MYENV=$MYENV" echo "$0: PGPORT=$PGPORT" echo "$0: PGDATABASE=$PGDATABASE" echo "$0: DEBUG=${DEBUG:-}" STR chmod +x {main,sub}.sh # 環境定義 declare -x MYENV=myenv declare -x PGDATABASE=postgres export PGPORT=5432 # サブシェルで実行 ./main.sh # ******** [TEST-1 Output] ******** # 環境定義 # サブシェルで実行 ./main.sh: MYENV=myenv ./main.sh: PGPORT=5432 ./main.sh: PGDATABASE=postgres # 一部の環境を削除 # DEBUGはシェルパラメータで以下実行環境でのみ引き継ぎ ./sub.sh: MYENV=myenv ./sub.sh: PGPORT= ./sub.sh: PGDATABASE= ./sub.sh: DEBUG=1 set -k
# ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ cat <<'STR' > main.sh #!/bin/bash echo "$0: MYENV=$MYENV" echo "$0: PGPORT=$PGPORT" echo "$0: PGDATABASE=$PGDATABASE" STR # コマンド実行文で、name=value形式であればシェルパラメータとして定義 set -k #1 サブシェルで実行 PGDATABASE=postgres ./main.sh PGPORT=5432 MYENV=myenv set +k #2 サブシェルで実行 PGDATABASE=postgres ./main.sh PGPORT=5432 MYENV=myenv # ******** [TEST-1 Output] ******** # コマンド実行文で、name=value形式であればシェルパラメータとして定義 #1 サブシェルで実行 ./main.sh: MYENV=myenv ./main.sh: PGPORT=5432 ./main.sh: PGDATABASE=postgres #2 サブシェルで実行 ./main.sh: MYENV= ./main.sh: PGPORT= ./main.sh: PGDATABASE=postgres 終了ステータス †コマンド実行の終了ステータスは、waitpidシステムコール(と類似の関数群)が返す値であり0〜255の値をとる。 0は正常終了を意味する。致命的なシグナル(N)でコマンドが終了すると、Bashは特別なエラーモードとして128+Nの値を使う。コマンドが見つからない場合は127。コマンドは見つかるが実行可能でない場合は126。 組み込みコマンドは、不正な使用法を示すために終了ステータス2を返す。 シグナル †Bashは対話モードでは、SIGTERMは無視し、SIGINTはハンドルされる。SIGQUITは無視する。ジョブ制御が有効な場合、BashはSIGTTIN、SIGTTOU、SIGTSTPを無視する。 シグナルハンドラは親シェルから継承する。ジョブ制御が無効のとき、非同期なコマンドはSIGINT、SIGQUITを無視する。コマンド置換で実行されるコマンドは、キーボードから送られるシグナルSIGTTIN、SIGTTOU、SIGTSTPを無視する。 シェルは、SIGHUPで終了する。終了する前に対話シェルは全てのジョブにSIGHUPを送る。停止済みのジョブには、SIGHUPを受け取るのを保証するためにSIGCONTが送られる。シェルがSIGHUPを配送するのを防ぐには、
Bashは、コマンドが完了するのを待っており、トラップがセットされたシグナルを受け取った場合、トラップはコマンドが完了するまで実行されない。Bashは、組み込みのwaitを非同期コマンドを待っている時、トラップがセットされたシグナルを受け取ると、waitから128より大きな終了コードで直ちにコマンドから復帰しトラップが実行される。 waitで待つ間にシグナルを送る # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ function handler() { local status=$? echo "trapped($$): $status" exit $status } trap handler SIGINT trap handler SIGTERM cat <<EO > sigchild.sh #!/bin/bash sleep 5 echo "send SIGINT to $$" kill -s SIGINT $$ sleep 10 EO echo "run sigchild.sh" bash sigchild.sh & wait $! exit 0 # ******** [TEST-1 Output] ******** run sigchild.sh send SIGINT to 27488 trapped(27488): 130 バックグラウンドでジョブ実行(huponexit on/off) # # huponexit on # $ shopt -s huponexit $ sleep 120 & $ exit # 再度ログイン $ ps aux | grep [s]leep # いない # # huponexit off # $ shopt -u huponexit $ sleep 120 & $ exit # 再度ログイン $ ps aux | grep [s]leep # いる Linux manより。POSIX規格については関連を参照のこと。
関連
参考 From BSD Man No Name Default Action Description 1 SIGHUP terminate process terminal line hangup 2 SIGINT terminate process interrupt program 3 SIGQUIT create core image quit program 4 SIGILL create core image illegal instruction 5 SIGTRAP create core image trace trap 6 SIGABRT create core image abort program (formerly SIGIOT) 7 SIGEMT create core image emulate instruction executed 8 SIGFPE create core image floating-point exception 9 SIGKILL terminate process kill program 10 SIGBUS create core image bus error 11 SIGSEGV create core image segmentation violation 12 SIGSYS create core image non-existent system call invoked 13 SIGPIPE terminate process write on a pipe with no reader 14 SIGALRM terminate process real-time timer expired 15 SIGTERM terminate process software termination signal 16 SIGURG discard signal urgent condition present on socket 17 SIGSTOP stop process stop (cannot be caught or ignored) 18 SIGTSTP stop process stop signal generated from keyboard 19 SIGCONT discard signal continue after stop 20 SIGCHLD discard signal child status has changed 21 SIGTTIN stop process background read attempted from control terminal 22 SIGTTOU stop process background write attempted to control terminal 23 SIGIO discard signal I/O is possible on a descriptor (see fcntl(2)) 24 SIGXCPU terminate process cpu time limit exceeded (see setrlimit(2)) 25 SIGXFSZ terminate process file size limit exceeded (see setrlimit(2)) 26 SIGVTALRM terminate process virtual time alarm (see setitimer(2)) 27 SIGPROF terminate process profiling timer alarm (see setitimer(2)) 28 SIGWINCH discard signal Window size change 29 SIGINFO discard signal status request from keyboard 30 SIGUSR1 terminate process User defined signal 1 31 SIGUSR2 terminate process User defined signal 2 シェル組み込みコマンド †Bourne Shell組み込みコマンド †: (コロン) †: [arguments] 引数の展開やリダイレクト以外に何もしない。終了ステータスは0。 # 何も実行されない $ : [Comment] $ : echo "hello" $ echo "foo" > foo $ cat foo foo # Truncate $ :>foo $ cat foo : # 変数初期化 # 以下と同様、DEBUGと2回書かなくても良い # DEBUG=${DEBUG:-2} $ : ${DEBUG:=2} $ echo $DEBUG 2 . (ピリオド) †. filename [arguments] 現在のシェルコンテキストでファイルを読み実行する。
# # file _exec.sh # $ cat _exec.sh trap 'read -p "$0($LINENO) $BASH_COMMAND"' DEBUG cat <<EO > debug-child.sh #!/bin/bash pwd echo "hello" echo "world" EO set -T source debug-child.sh exit 0 # # set +T # $ bash _exec.sh _exec.sh(2) cat > debug-child.sh <<EO #!/bin/bash pwd echo "hello" echo "world" EO _exec.sh(9) set +T _exec.sh(11) source debug-child.sh /Users/guest/workspace/bash hello world _exec.sh(13) exit 0 # # set -T # $ bash _exec.sh _exec.sh(2) cat > debug-child.sh <<EO #!/bin/bash pwd echo "hello" echo "world" EO _exec.sh(9) set -T _exec.sh(11) source debug-child.sh _exec.sh(2) pwd /Users/guest/workspace/bash _exec.sh(3) echo "hello" hello _exec.sh(4) echo "world" world _exec.sh(13) exit 0 break †break [n]
# ++++++++++++++++++++++ [TEST-2 Commands] ++++++++++++++++++++++ i=0 j=0 while (( i < 10 )); do echo i=$i for j in {1..20}; do if [ $j -gt 5 ]; then # whileを抜ける break 2 fi echo j=$j done ((i++)) done # ******** [TEST-2 Output] ******** i=0 j=1 j=2 j=3 j=4 j=5 cd †cd [-L|[-P [-e]] [-@] [directory] ディレクトリを変更する。
最初の引数が シンボリックリンクの解決 vagrant@localhost foo]$ ls -la 合計 0 drwxrwxr-x. 3 vagrant vagrant 29 3月 26 05:28 . drwx------. 4 vagrant vagrant 137 3月 26 05:24 .. lrwxrwxrwx. 1 vagrant vagrant 3 3月 26 05:28 link -> sym drwxrwxr-x. 2 vagrant vagrant 18 3月 26 05:28 sym [vagrant@localhost foo]$ pwd /home/vagrant/foo [vagrant@localhost foo]$ cd -P link [vagrant@localhost sym]$ pwd /home/vagrant/foo/sym [vagrant@localhost sym]$ cd .. [vagrant@localhost foo]$ ll 合計 0 lrwxrwxrwx. 1 vagrant vagrant 3 3月 26 05:28 link -> sym drwxrwxr-x. 2 vagrant vagrant 18 3月 26 05:28 sym [vagrant@localhost foo]$ cd -L link [vagrant@localhost link]$ pwd /home/vagrant/foo/link 絶対パスの表示 [vagrant@localhost ~]$ ls foo test.sh [vagrant@localhost ~]$ cd foo [vagrant@localhost foo]$ cd [vagrant@localhost ~]$ cd - foo /home/vagrant/foo continue †continue [n]
以下、 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ i=0 j=0 k=0 while test $k -lt 5; do echo "k = $k" while test $i -lt 3; do #1 echo " i = $i" ((i++)) while test $j -lt 5; do echo " j = $j" ((j++)) if [ $j -gt 2 ]; then echo "#3に移る" break 2 fi done echo "#2" done echo "#3" ((k++)) done # ******** [TEST-1 Output] ******** k = 0 i = 0 j = 0 j = 1 j = 2 #3に移る #3 k = 1 i = 1 j = 3 #3に移る #3 k = 2 i = 2 j = 4 #3に移る #3 k = 3 #3 k = 4 #3 # ++++++++++++++++++++++ [TEST-2 Commands] ++++++++++++++++++++++ i=0 j=0 k=0 while test $k -lt 5; do echo "k = $k" while test $i -lt 3; do #1 echo " i = $i" ((i++)) while test $j -lt 5; do echo " j = $j" ((j++)) if [ $j -gt 2 ]; then echo "#1に移る" continue 2 fi done echo "#2" done echo "#3" ((k++)) done # ******** [TEST-2 Output] ******** k = 0 i = 0 j = 0 j = 1 j = 2 #1に移る i = 1 j = 3 #1に移る i = 2 j = 4 #1に移る #3 k = 1 #3 k = 2 #3 k = 3 #3 k = 4 #3 eval †eval [arguments]
# ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ #1 eval 'echo $HOME' #2 eval 'echo $((1+1))' #3 pwd a=pwd; varname=a; b="\$${varname}"; eval "${b}" # ******** [TEST-1 Output] ******** #1 /home/guest #2 2 #3 /home/guest/workspace/bash /home/guest/workspace/bash # ++++++++++++++++++++++ [TEST-2 Commands] ++++++++++++++++++++++ a= b=99 cat <<STR > com.txt a=10 && echo \$a b=20 && echo \$b echo \$((a+b)) c=100 STR i=1 while read line; do echo "LINE[$i]: $line" eval "$line" ((i++)) done < com.txt echo "a=$a b=$b c=$c" # ******** [TEST-2 Output] ******** LINE[1]: a=10 && echo $a 10 LINE[2]: b=20 && echo $b 20 LINE[3]: echo $((a+b)) 30 LINE[4]: c=100 a=10 b=20 c=100 exec †exec [-cl] [-a name] [command [arguments]] 新規プロセスを作らず現在のシェルを置き換える。-lオプションは、コマンドに渡される0番目の引数の最初にダッシュを置く。loginプログラムが行なっている。-cオプションは、環境を引き継がずコマンド実行を行なう。-aオプションは、0番目の引数としてnameを渡す。コマンド実行できない場合、execfailオプションが有効でなければ非対話シェルは終了する。対話シェルはファイルが実行されないならば失敗コードを返す。サブシェルは、execに失敗すると無条件で終了する。commandが指定されない場合、リダイレクトは現在のシェル環境に作用する。もしリダイレクトがエラーでなければ、0が返る。 例 exec # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ export MYENV=MYENV cat <<'EOF' > _exec.sh #!/bin/bash echo $_ echo $0 echo $@ echo $MYENV EOF exec bash _exec.sh arg1 # ******** [TEST-1 Output] ******** bash _exec.sh arg1 MYENV 例exec -c -a ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ export MYENV=MYENV cat <<'EOF' > _exec.sh #!/bin/bash echo $_ echo $0 echo $@ echo $MYENV EOF exec -c -a myprogram bash _exec.sh arg1 # ******** [TEST-1 Output] ******** myprogram _exec.sh arg1 サンプル execとリダイレクト # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ # fd20のディスクリプタを開き1に複製 exec 20>&1 # fd1をファイルに向ける exec > stdout.out echo "foo" echo "bar" # fd20を閉じ1を復元 exec >&20- cat stdout.out # ******** [TEST-1 Output] ******** foo bar exit †exit [n] シェルを終了し終了コードnを返す。終了ステータスが指定されない場合、最後に実行されたコマンドの終了ステータスとなる。 例exitとEXITトラップの例 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ # ハンドラを設定 trap 'exit_handler' EXIT function exit_handler() { local status=$? echo "called exit_handler: $status" return $status } # あえてFailureとなるコマンドを実行 test "" = "1" # failure # exit 0 exit # exit status 1 # ******** [TEST-1 Output] ******** called exit_handler: 1 例EXITトラップと複数のハンドラを設定 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ # ハンドラ配列 declare -a exit_handlers=() # EXITトラップ trap 'invoke_exit_handlers' EXIT # ハンドラの実行 invoke_exit_handlers() { local exit_status=$? for h in ${exit_handlers[@]}; do $h $exit_status if [ $? -ne 0 ]; then : do something fi done } # ハンドラの追加 add_exit_handler() { exit_handlers[${#exit_handlers[@]}]=$1 } # ハンドラ1 function handler1() { local status=$1 echo "Called ${FUNCNAME[0]}: $status" return $status } # ハンドラ2 function handler2() { local status=$1 echo "Called ${FUNCNAME[0]}: $status" return $status } # ハンドラを追加 add_exit_handler handler1 add_exit_handler handler2 # あえてFailureとなるコマンドを実行 test "" = "1" # failure # exit 0 exit # exit status 1 # ******** [TEST-1 Output] ******** Called handler1: 1 Called handler2: 1 export †export [-fn] [-p] [name[=value]] nameの環境情報が子プロセスに引き継がれるようにする。-fオプションは、nameがシェル関数であることを示す。-nオプションは、nameを引き継がないようにする。nameや-pオプションが指定された場合、exportされる変数リストが表示される。-pオプションは、入力としての再利用可能な形式での出力を行なう。name=valueとなる場合は、valueが設定される。 ## ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ cat <<'STR' > _export.sh #!/bin/bash echo "MYENV: $MYENV" echo "call my_func" myfunc STR # MYENVを引き継ぐ export MYENV=myenv myfunc() { echo ${FUNCNAME}; } # myfuncを引き継ぐ export -f myfunc bash _export.sh # export export -p # ******** [TEST-1 Output] ******** MYENV: myenv call my_func myfunc declare -x HOME="/home/guest" declare -x LANG="ja_JP.UTF-8" declare -x LC_ALL="ja_JP.UTF-8" declare -x LOGNAME="guest" declare -x MYENV="myenv" declare -x OLDPWD="/home/guest" # 長いので省略... getopts †getopts optstring name [args] オプソション解析で使われるコマンド。optstringにコロン(:)が含まれる場合は、スペース区切りで値を持つことを示す。: と ? は、オプション文字列として使用できない。getoptsが呼ばれる度に、nameに次のオプションの値を設定する。OPTINDに次の変数のインデックスが代入される。OPTINDは、シェル、シェルスクリプトが呼び出されると1に初期化される。オプション値はOPTARGに置かれる。getoptsを同じシェル内で繰り返し使用する場合、シェルはOPTINDをリセットしないので、手動でリセットが必要である。 オプションの終わりになると、getoptsは0以上の終了ステータスを返す。OPTINDは、最初のオプションでない引数のインデックスにセットされ、nameは?にセットされる。 getoptsは通常位置パラメータをパースする。 optstringの最初の文字が:の場合、サイレントエラーとなる。OPTERRが0の場合、エラーメッセージは表示されない。 無効なオプションが含まれていた場合、getoptsはnameに?を設定し、サイレントエラーでなければエラーメッセージを表示し、OPTARGをunsetとする。サイレントの場合、見つかったオプション文字がOPTARGに置かれ、診断メッセージは表示されない。 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ #1 OPTERR echo "#1 OPTERR: $OPTERR" #2 オプションのパース # [options] # -a <arg> # -b <arg> # -c # -D # No silent mode while getopts "a:b:cD" optname; do case $optname in a) echo "-a: $OPTARG" ;; b) echo "-b: $OPTARG" ;; c|D) echo "Found $optname option" ;; esac done # オプション解析後の位置 echo $OPTIND # 位置パラメータを最初の引数へセット shift $((OPTIND-1)) # 残りの引数 echo $@ # ******** [TEST-1 Output] ******** $ bash getopts.sh -a a -b b -c -D -f arg1 #1 OPTERR: 1 -a: a -b: b Found c option Found D option _exec.sh: 不正なオプションです -- f #3 OPTIND: 8 #5: arg1 hash †hash [-r] [-p filename] [-dt] [name] pwd †pwd [-LP] 現在のワーキングディレクトリのパスを表示する。-Pはシンボリックリンクを含まないパスを表示する。-Lはシンボリックリンクを含んだパス名を返す。 $ pwd /Users/guest/workspace/bash $ echo $PWD /Users/guest/workspace/bash readonly †readonly [-aAf] [-p] [name[=value]] … nameをリードオンリーにする。-fオプションは、関数への参照を示す。-aオプションは、配列への参照を示す。-Aオプションは、連想配列への参照を示す。nameがないまたは-pオプションが指定される場合は、readonlyの全てのリストが表示される。-pオプションは、入力の再利用可能な形式で出力する。valueが指定されていれば、nameにセットされる。 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ declare -A map=() declare -a array=(1 2 3) myfunc() { echo $FUNCNAME } # 関数を読み取り専用にする readonly -f myfunc myfunc() { echo "FUNCNAME: $FUNCNAME" } # 読み取り専用のmapを定義する readonly -A ro_map=([a]=1) # 再利用可能な形式で出力 readonly -p # 変更不可 ro_map=([b]=2) # 変更可能 array[1]=11 # ******** [TEST-1 Output] ******** # 関数を読み取り専用にする hoge.sh: 行 67: myfunc: 読み取り専用関数です # 読み取り専用のmapを定義する # 再利用可能な形式で出力 declare -r BASHOPTS="cmdhist:complete_fullquote:extquote:force_fignore:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath" declare -ir BASHPID declare -ar BASH_VERSINFO=([0]="4" [1]="4" [2]="23" [3]="1" [4]="release" [5]="x86_64-apple-darwin17.5.0") declare -ir EUID="501" declare -ir PPID="42776" declare -r SHELLOPTS="braceexpand:hashall:interactive-comments" declare -ir UID="501" declare -Ar ro_map=([a]="1" ) # 変更不可 hoge.sh: 行 76: ro_map: 読み取り専用の変数です return †return [n] 関数から戻り呼び出し元にnのステータスを返す。nが指定されない場合は、最後に実行されたコマンドの終了ステータスとなる。trapハンドラでreturnが使われる場合、trapハンドラの前に実行された最後のコマンドの終了ステータスとなる。DEBUGトラップでreturnが使われる場合、returnが呼ばれる前に実行されたコマンドの終了ステータスとなる。 EXITトラップ # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ trap 'my_handler' EXIT my_handler() { echo "called_handler: $?" return 2 } exit 1 # ******** [TEST-1 Output] ******** called_handler: 1 # 終了ステータス $ echo "exit:$?" 1 DEBUGトラップ # ++++++++++++++++++++++ [TEST-2 Commands] ++++++++++++++++++++++ trap 'my_handler' DEBUG my_handler() { echo "$? ($LINENO) $BASH_COMMAND" return 2 } [ "" != "" ] echo $? echo "world" # ******** [TEST-2 Output] ******** 0 (6) [ "" != "" ] 1 (6) echo $? 1 0 (6) echo "world" world # 終了ステータス $ echo "exit:$?" 0 shift †shift [n] 位置パラメータを左シフトする。nの整数で$#以下の値である。nが0または$#より大きい場合は、変わらない。nが指定されていない場合は、nが1と同じとなる。終了ステータスは正常な場合は0、そうでない場合は0でない値となる。 # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } #!/usr/bin/env bash echo "argnum: $#" echo "args: $@" echo "arg[0]: $1" usage() { echo "Usage: $(basename "${BASH_SOURCE}") [OPTIONS] [arg..]" } _# "自前のオプション解析" while [ $# -gt 0 ]; do case "$1" in -h|--help) usage && exit ;; -t) # オプション値を取得 shift echo "-t: $1" ;; -*) echo "invalid parameter: $1" >&2 ;; *) break ;; esac shift done _# "オプション解析後の引数情報" echo "\$#: $#" echo "\$@: $@" # ******** [TEST-1 Output] ******** argnum: 3 args: -t 1 10 arg[0]: -t # 自前のオプション解析 -t: 1 # オプション解析後の引数情報 $#: 1 $@: 10 test、[ †test expr 条件式を評価し、0(true) or 1(false)を返す。 [ は、 ]で終わる必要がある。 ! expr †exprがfalseならば # 長さ0ではない(0なのでfalse) $ test ! -z "" ; echo $? 1 ( expr ) †exprの値を返す。演算子の評価順を上書きするのに使う。 $ [[ 4 -gt 2 && ( 1 -gt 2 || 4 -gt 2 ) ]] ; echo $? 0 expr1 -a expr2 †ext1とexpr2がtrueならばTrue。 $ [ "1" = "1" -a "0" = "0" ]; echo $? 0 expr1 -o expr2 †ext1またはexpr2のいずれかがtrueならばTrue。 $ [ "1" = "0" -o "0" = "0" ]; echo $? 0 0 arguments †式はfalse $ test ; echo $? 1 1 arguments †式がnullでなければtrue。 $ test 1; echo $? 0 $ test ""; echo $? 1 2 arguments †最初の引数が ! で2番目がnullならばtrue。最初の引数が単項演算子で、テスト結果がtrueならばtrue。 $ test ! "" ; echo $? 0 $ test ! 1 ; echo $? 1 3 arguments †以下の条件が順に適用される。
$ test ! -z "" ; echo $? 1 4 arguments †最初の引数が ! ならば、残りの引数で構成される3つの引数をとる式の否定。そうでない場合、式はパースされ上で述べたルールで評価される。 $ [[ ! "a" > "b" ]]; echo $? 0 5 arguments †式はパースされ、上で述べたルールにそって評価される。 times †times
コマンド実行にかかったユーザ、システム時間を表示する。 $ times ls 0m0.268s 0m0.121s 0m0.192s 0m0.193s trap †trap [-lp] [arg] [sigspec …] シグナルを捕捉するハンドラを定義する。 sigspecが0またはEXITの時、シェルが終了するときにargが実行される。sigspecがDEBUGのとき、各コマンド実行の前にargが実行される。sigspecがRETURNのとき、シェル関数やスクリプトが sigspecがERRのとき、パイプライン、リスト、複合コマンドが非0で終了するとargが実行される。 $ trap -p trap -- 'shell_session_update' EXIT $ trap -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE 9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG 17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD 21) SIGTTIN 22) SIGTTOU 23) SIGIO 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGINFO 30) SIGUSR1 31) SIGUSR2 DEBUGトラップ # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ trap 'read -p "$0:$LINENO: $BASH_COMMAND"' DEBUG echo "hello" echo "world" trap - DEBUG echo "hello" echo "world" # ******** [TEST-1 Output] ******** $ bash _exec.sh _exec.sh:4: echo "hello" hello _exec.sh:5: echo "world" world _exec.sh:7: trap - DEBUG hello world RETURNトラップ 実行した環境では、関数にtrapが引き継がれておらず、functrace or extdebugオプションを有効化することでreturnをトラップしている。 # ++++++++++++++++++++++ [TEST-2 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } trap 'echo "@trap return"' RETURN cat <<'STR' > include.sh echo "hello" STR function myfunc() { echo "myfunc" } function include_file() { . include.sh } _# "my" myfunc _# "source" source include.sh _# "include_file" include_file _# "include_file with" _# "functrace or extdebug" _# "set -o functrace" shopt -s extdebug include_file # ******** [TEST-2 Output] ******** # my myfunc # source hello @trap return # include_file hello # include_file with # functrace or extdebug # set -o functrace hello @trap return @trap return umask †umask [-p] [-S] [mode] シェルのumaskをmodeに設定する。 # 入力可能なフォーマットでmode出力 $ umask -p umask 0022 # シンボリック形式でmode出力 $ umask -S u=rwx,g=rx,o=rx # umaskを077に設定 $ umask 077 $ umask -S u=rwx,g=,o= unset †unset [-fnv] [name] シェル変数や関数を削除する。-vオプションは、変数を参照する。-fオプションは、関数を参照する。-nオプションは、namerefを参照する。オプションが指定されなければ、変数を参照し、変数定義がなければ、関数が削除される。readonly属性の変数は削除されない。 # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } _# "変数" noraml_variable="test" _# "ハッシュ" declare -A map=([a]=1) _# "配列" declare -a array=(1 2) _# "関数" function my() { echo "myfunc" } function my_2() { echo "myfunc2" } function var_dump() { echo "++++ var_dump +++" echo "noraml_variable: $noraml_variable" echo "map: ${map[@]}" echo "array: ${array[@]}" declare -F my declare -F my_2 declare -p ref_var declare -p ref_func } _# "名前参照" variable="variable" declare -n ref_var=variable declare -n ref_func=my_2 _# "変数情報の出力" var_dump unset noraml_variable unset map _# "インデックス0のみ削除" unset array[0] _# "関数削除" unset -f my _# "参照先の定義を削除" unset ref_var _# "参照名のみ削除" unset -n ref_func _# "変数情報の出力" var_dump # ******** [TEST-1 Output] ******** $ bash sample.sh # 変数 # ハッシュ # 配列 # 関数 # 名前参照 # 変数情報の出力 ++++ var_dump +++ noraml_variable: test map: 1 array: 1 2 my my_2 declare -n ref_var="variable" declare -n ref_func="my_2" # インデックス0のみ削除 # 関数削除 # 参照先の定義を削除 # 参照名のみ削除 # 変数情報の出力 ++++ var_dump +++ noraml_variable: map: array: 2 my_2 declare -n ref_var="variable" sample.sh: 26 行: declare: ref_func: 見つかりません # 終了ステータス $ echo $? 1 Bash組み込みコマンド †alias †alias [-p] [name[=value] …] コマンドのエイリアスを設定する。-pオプションまたは引数なしで実行すると、再利用可能なフォーマットで現在定義されているエイリアス一覧を出力する。nameだけの場合は、nameとvalueの定義を出力する。 # エイリアスの定義一覧を表示する $ alias alias la='ls -la' alias ll='ls -l' alias lla='ls -la' alias ls='ls -G' # nameのみ指定 $ alias la alias la='ls -la' bind †bind [-m keymap] [-lpsvPSVX] bind [-m keymap] [-q function] [-u function] [-r keyseq] bind [-m keymap] -f filename bind [-m keymap] -x keyseq:shell-command bind [-m keymap] keyseq:function-name bind [-m keymap] keyseq:readline-command 現在のReadlineのキーと関数バインドを表示および設定を行なう。 # Ctrl+]で単語単位でカーソルを前方に移動 $ bind '"\C-]": forward-word' 例:現在の関数とバインディングを表示する $ bind -p "\C-g": abort "\C-x\C-g": abort "\e\C-g": abort "\C-j": accept-line "\C-m": accept-line # alias-expand-line (not bound) # arrow-key-prefix (not bound) # backward-byte (not bound) "\C-b": backward-char "\eOD": backward-char "\e[D": backward-char "\C-h": backward-delete-char "\C-?": backward-delete-char "\C-x\C-?": backward-kill-line "\e\C-h": backward-kill-word "\e\C-?": backward-kill-word "\eb": backward-word "\e<": beginning-of-history "\C-a": beginning-of-line "\eOH": beginning-of-line "\e[H": beginning-of-line "\e[200~": bracketed-paste-begin "\C-xe": call-last-kbd-macro "\ec": capitalize-word "\C-]": character-search "\e\C-]": character-search-backward "\C-l": clear-screen "\C-i": complete "\e\e": complete "\e!": complete-command "\e/": complete-filename "\e@": complete-hostname "\e{": complete-into-braces "\e~": complete-username "\e$": complete-variable # copy-backward-word (not bound) # copy-forward-word (not bound) # copy-region-as-kill (not bound) # dabbrev-expand (not bound) "\C-d": delete-char "\e[3~": delete-char # delete-char-or-list (not bound) "\e\\": delete-horizontal-space "\e-": digit-argument "\e0": digit-argument "\e1": digit-argument "\e2": digit-argument "\e3": digit-argument "\e4": digit-argument "\e5": digit-argument "\e6": digit-argument "\e7": digit-argument "\e8": digit-argument "\e9": digit-argument "\C-x\C-v": display-shell-version "\C-xA": do-lowercase-version "\C-xB": do-lowercase-version "\C-xC": do-lowercase-version "\C-xD": do-lowercase-version "\C-xE": do-lowercase-version "\C-xF": do-lowercase-version "\C-xG": do-lowercase-version "\C-xH": do-lowercase-version "\C-xI": do-lowercase-version "\C-xJ": do-lowercase-version "\C-xK": do-lowercase-version "\C-xL": do-lowercase-version "\C-xM": do-lowercase-version "\C-xN": do-lowercase-version "\C-xO": do-lowercase-version "\C-xP": do-lowercase-version "\C-xQ": do-lowercase-version "\C-xR": do-lowercase-version "\C-xS": do-lowercase-version "\C-xT": do-lowercase-version "\C-xU": do-lowercase-version "\C-xV": do-lowercase-version "\C-xW": do-lowercase-version "\C-xX": do-lowercase-version "\C-xY": do-lowercase-version "\C-xZ": do-lowercase-version "\eA": do-lowercase-version "\eB": do-lowercase-version "\eC": do-lowercase-version "\eD": do-lowercase-version "\eE": do-lowercase-version "\eF": do-lowercase-version "\eG": do-lowercase-version "\eH": do-lowercase-version "\eI": do-lowercase-version "\eJ": do-lowercase-version "\eK": do-lowercase-version "\eL": do-lowercase-version "\eM": do-lowercase-version "\eN": do-lowercase-version "\eP": do-lowercase-version "\eQ": do-lowercase-version "\eR": do-lowercase-version "\eS": do-lowercase-version "\eT": do-lowercase-version "\eU": do-lowercase-version "\eV": do-lowercase-version "\eW": do-lowercase-version "\eX": do-lowercase-version "\eY": do-lowercase-version "\eZ": do-lowercase-version "\el": downcase-word # dump-functions (not bound) # dump-macros (not bound) # dump-variables (not bound) "\e\C-i": dynamic-complete-history "\C-x\C-e": edit-and-execute-command # emacs-editing-mode (not bound) "\C-x)": end-kbd-macro "\e>": end-of-history "\C-e": end-of-line "\eOF": end-of-line "\e[F": end-of-line "\C-x\C-x": exchange-point-and-mark # forward-backward-delete-char (not bound) # forward-byte (not bound) "\C-f": forward-char "\eOC": forward-char "\e[C": forward-char "\C-s": forward-search-history "\ef": forward-word "\eg": glob-complete-word "\C-x*": glob-expand-word "\C-xg": glob-list-expansions # history-and-alias-expand-line (not bound) "\e^": history-expand-line # history-search-backward (not bound) # history-search-forward (not bound) # history-substring-search-backward (not bound) # history-substring-search-forward (not bound) "\e#": insert-comment "\e*": insert-completions "\e.": insert-last-argument "\e_": insert-last-argument "\C-k": kill-line # kill-region (not bound) # kill-whole-line (not bound) "\ed": kill-word # magic-space (not bound) # menu-complete (not bound) # menu-complete-backward (not bound) "\C-n": next-history "\eOB": next-history "\e[B": next-history "\en": non-incremental-forward-search-history # non-incremental-forward-search-history-again (not bound) "\ep": non-incremental-reverse-search-history # non-incremental-reverse-search-history-again (not bound) # old-menu-complete (not bound) "\C-o": operate-and-get-next # overwrite-mode (not bound) "\C-x!": possible-command-completions "\e=": possible-completions "\e?": possible-completions "\C-x/": possible-filename-completions "\C-x@": possible-hostname-completions "\C-x~": possible-username-completions "\C-x$": possible-variable-completions "\C-p": previous-history "\eOA": previous-history "\e[A": previous-history # print-last-kbd-macro (not bound) "\C-q": quoted-insert "\C-v": quoted-insert "\C-x\C-r": re-read-init-file # redraw-current-line (not bound) "\C-r": reverse-search-history "\e\C-r": revert-line "\er": revert-line " ": self-insert "!": self-insert "\"": self-insert "#": self-insert "$": self-insert "%": self-insert "&": self-insert "'": self-insert "(": self-insert ")": self-insert "*": self-insert "+": self-insert ",": self-insert "-": self-insert ".": self-insert "/": self-insert "0": self-insert "1": self-insert "2": self-insert "3": self-insert "4": self-insert "5": self-insert "6": self-insert "7": self-insert "8": self-insert "9": self-insert ":": self-insert ";": self-insert "<": self-insert "=": self-insert ">": self-insert "?": self-insert "@": self-insert "A": self-insert "B": self-insert "C": self-insert "D": self-insert "E": self-insert "F": self-insert "G": self-insert "H": self-insert "I": self-insert "J": self-insert "K": self-insert "L": self-insert "M": self-insert "N": self-insert "O": self-insert "P": self-insert "Q": self-insert "R": self-insert "S": self-insert "T": self-insert "U": self-insert "V": self-insert "W": self-insert "X": self-insert "Y": self-insert "Z": self-insert "[": self-insert "\\": self-insert "]": self-insert "^": self-insert "_": self-insert "`": self-insert "a": self-insert "b": self-insert "c": self-insert "d": self-insert "e": self-insert "f": self-insert "g": self-insert "h": self-insert "i": self-insert "j": self-insert "k": self-insert "l": self-insert "m": self-insert "n": self-insert "o": self-insert "p": self-insert "q": self-insert "r": self-insert "s": self-insert "t": self-insert "u": self-insert "v": self-insert "w": self-insert "x": self-insert "y": self-insert "z": self-insert "{": self-insert "|": self-insert "}": self-insert "~": self-insert "\200": self-insert "\201": self-insert "\202": self-insert "\203": self-insert "\204": self-insert "\205": self-insert "\206": self-insert "\207": self-insert "\210": self-insert "\211": self-insert "\212": self-insert "\213": self-insert "\214": self-insert "\215": self-insert "\216": self-insert "\217": self-insert "\220": self-insert "\221": self-insert "\222": self-insert "\223": self-insert "\224": self-insert "\225": self-insert "\226": self-insert "\227": self-insert "\230": self-insert "\231": self-insert "\232": self-insert "\233": self-insert "\234": self-insert "\235": self-insert "\236": self-insert "\237": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "?": self-insert "\C-@": set-mark "\e ": set-mark # shell-backward-kill-word (not bound) # shell-backward-word (not bound) "\e\C-e": shell-expand-line # shell-forward-word (not bound) # shell-kill-word (not bound) # skip-csi-sequence (not bound) "\C-x(": start-kbd-macro # tab-insert (not bound) "\e&": tilde-expand "\C-t": transpose-chars "\et": transpose-words # tty-status (not bound) "\C-x\C-u": undo "\C-_": undo # universal-argument (not bound) # unix-filename-rubout (not bound) "\C-u": unix-line-discard "\C-w": unix-word-rubout "\eu": upcase-word # vi-append-eol (not bound) # vi-append-mode (not bound) # vi-arg-digit (not bound) # vi-bWord (not bound) # vi-back-to-indent (not bound) # vi-backward-bigword (not bound) # vi-backward-word (not bound) # vi-bword (not bound) # vi-change-case (not bound) # vi-change-char (not bound) # vi-change-to (not bound) # vi-char-search (not bound) # vi-column (not bound) # vi-complete (not bound) # vi-delete (not bound) # vi-delete-to (not bound) # vi-eWord (not bound) # vi-editing-mode (not bound) # vi-end-bigword (not bound) # vi-end-word (not bound) # vi-eof-maybe (not bound) # vi-eword (not bound) # vi-fWord (not bound) # vi-fetch-history (not bound) # vi-first-print (not bound) # vi-forward-bigword (not bound) # vi-forward-word (not bound) # vi-fword (not bound) # vi-goto-mark (not bound) # vi-insert-beg (not bound) # vi-insertion-mode (not bound) # vi-match (not bound) # vi-movement-mode (not bound) # vi-next-word (not bound) # vi-overstrike (not bound) # vi-overstrike-delete (not bound) # vi-prev-word (not bound) # vi-put (not bound) # vi-redo (not bound) # vi-replace (not bound) # vi-rubout (not bound) # vi-search (not bound) # vi-search-again (not bound) # vi-set-mark (not bound) # vi-subst (not bound) # vi-tilde-expand (not bound) # vi-unix-word-rubout (not bound) # vi-yank-arg (not bound) # vi-yank-pop (not bound) # vi-yank-to (not bound) "\C-y": yank "\e.": yank-last-arg "\e_": yank-last-arg "\e\C-y": yank-nth-arg "\ey": yank-pop builtin †builtin [shell-builtin [args]] シェルの組み込みコマンドを実行する。同一名の定義がある場合、オリジナルを呼び出したい場合に有効。 # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } function cd() { echo "my cd called: $@" builtin cd $@ } _# "定義したcdを呼ぶ" _# "そもそも同じ名前のコマンドを定義すべきでないというのはある" _# "ここではサンプルとして実行している" cd $PWD pwd # ******** [TEST-1 Output] ******** $ bash sample.sh # 定義したcdを呼ぶ # そもそも同じ名前のコマンドを定義すべきでないというのはある # ここではサンプルとして実行している my cd called: /home/guest/workspace/bash /home/guest/workspace/bash # 終了ステータス $ echo $? 0 caller †caller [expr] アクティブなサブルーチン呼び出しのコンテキストを返す。スタックトレースの出力ができる。現在のフレームは0から始まる。exprには、正数を指定する。
command †command [-pVv] command [arguments …] 組み込みコマンドまたはPATH探索で見つけられるコマンドを実行する。-pは、パスのデフォルト定義を使う。コマンドが見つかれないまたはエラー終了した場合、終了コード127となる。-Vまたは-vは、コマンドの定義が表示される。 # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } mkdir -p /tmp/test/bin cat <<COM > /tmp/test/bin/pwd #!/bin/bash echo "Current Path: \$(pwd)" COM chmod +x /tmp/test/bin/pwd export PATH=/tmp/test/bin:$PATH function pwd() { echo "my func pwd: $@" command pwd } _# "pwd実行" pwd _# "定義を出力" _# "with -v" command -v pwd _# "with -V" command -V pwd _# "存在しないコマンド実行" command unknown echo $? _# "PATHの/tmp/test/bin/pwdより組み込みpwdが優先される" _# "command pwd" command pwd _# "command /tmp/test/bin/pwd" command /tmp/test/bin/pwd _# "command -p pwd" command -p pwd _# "command -p /tmp/test/bin/pwd" command -p /tmp/test/bin/pwd rm -rf /tmp/test/bin # ******** [TEST-1 Output] ******** $ bash sample.sh # pwd実行 my func pwd: /home/guest/workspace/bash # 定義を出力 # with -v pwd # with -V pwd は関数です pwd () { echo "my func pwd: $@"; command pwd } # 存在しないコマンド実行 sample.sh: 行 29: unknown: コマンドが見つかりません 127 # PATHの/tmp/test/bin/pwdより組み込みpwdが優先される # command pwd /home/guest/workspace/bash # command /tmp/test/bin/pwd Current Path: /home/guest/workspace/bash # command -p pwd /home/guest/workspace/bash # command -p /tmp/test/bin/pwd Current Path: /home/guest/workspace/bash # 終了ステータス $ echo $? 0 declare †declare [-aAfFgilnrtux] [-p] [name[=value] …] 変数の宣言と属性の設定を行なう。nameが指定されない場合、変数の値が表示される。
以下のオプションがある。
echo †echo [-neE] [arg …] スペース区切りでargsを出力する。改行で終了する。-nオプションは、改行を抑制する。-eオプションは、エスケープが有効となる。-Eオプションは、エスケープ文字を無効化する。オプションの終了を意味する # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } _# "エスケープ" echo -e "a b" _# "改行なし" echo -n "foo" _# "エスケープなし" echo -E "b\ar" _# "オプション区切り -- 無効" echo -- bar # ******** [TEST-1 Output] ******** $ bash sample.sh # エスケープ a b # 改行なし foo# エスケープなし b\ar # オプション区切り -- 無効 -- bar # 終了ステータス $ echo $? 0 enable †enable [-a] [-dnps] [-f filename] [name …] 組み込みコマンドの有効および無効化する。-nオプションは、namesを無効にする。
引数なしの場合、シェルの組み込みコマンドリストが表示される。-sオプションは、POSIX特有の組み込みに制限する。 # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } _# "一覧表示" enable _# "一覧表示(有効/無効のindication付き)" _# "testを無効化" enable -n test enable -p -a test cat <<STR > test #!/bin/bash echo "my test" exit 3 STR chmod +x test export PATH=.:$PATH _# "test 1 -eq 1" test 1 echo $? # ******** [TEST-1 Output] ******** $ bash sample.sh # 一覧表示 enable . enable : enable [ enable alias enable bg enable bind enable break enable builtin enable caller enable cd enable command enable compgen enable complete enable compopt enable continue enable declare enable dirs enable disown enable echo enable enable enable eval enable exec enable exit enable export enable false enable fc enable fg enable getopts enable hash enable help enable history enable jobs enable kill enable let enable local enable logout enable mapfile enable popd enable printf enable pushd enable pwd enable read enable readarray enable readonly enable return enable set enable shift enable shopt enable source enable suspend enable test enable times enable trap enable true enable type enable typeset enable ulimit enable umask enable unalias enable unset enable wait # 一覧表示(有効/無効のindication付き) # testを無効化 enable . enable : enable [ enable alias enable bg enable bind enable break enable builtin enable caller enable cd enable command enable compgen enable complete enable compopt enable continue enable declare enable dirs enable disown enable echo enable enable enable eval enable exec enable exit enable export enable false enable fc enable fg enable getopts enable hash enable help enable history enable jobs enable kill enable let enable local enable logout enable mapfile enable popd enable printf enable pushd enable pwd enable read enable readarray enable readonly enable return enable set enable shift enable shopt enable source enable suspend enable -n test enable times enable trap enable true enable type enable typeset enable ulimit enable umask enable unalias enable unset enable wait # test 1 -eq 1 my test 3 # 終了ステータス $ echo $? 0 help †help [-dms] [pattern] 組み込みコマンドのヘルプを表示する。
$ help command command: command [-pVv] command [arg ...] Execute a simple command or display information about commands. Runs COMMAND with ARGS suppressing shell function lookup, or display information about the specified COMMANDs. Can be used to invoke commands on disk when a function with the same name exists. Options: -p use a default value for PATH that is guaranteed to find all of the standard utilities -v print a description of COMMAND similar to the `type' builtin -V print a more verbose description of each COMMAND Exit Status: Returns exit status of COMMAND, or failure if COMMAND is not found. $ help -d command command - Execute a simple command or display information about commands. $ help -m command NAME command - Execute a simple command or display information about commands. SYNOPSIS command [-pVv] command [arg ...] DESCRIPTION Execute a simple command or display information about commands. Runs COMMAND with ARGS suppressing shell function lookup, or display information about the specified COMMANDs. Can be used to invoke commands on disk when a function with the same name exists. Options: -p use a default value for PATH that is guaranteed to find all of the standard utilities -v print a description of COMMAND similar to the `type' builtin -V print a more verbose description of each COMMAND Exit Status: Returns exit status of COMMAND, or failure if COMMAND is not found. SEE ALSO bash(1) IMPLEMENTATION GNU bash, バージョン 4.4.23(1)-release (x86_64-apple-darwin17.5.0) Copyright (C) 2016 Free Software Foundation, Inc. ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html> $ help -s command command: command [-pVv] command [arg ...] let †let expression [expression …] 算術計算を行なう。 # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } declare -i i=10 _# "i+=1" let i+=1 echo $i _# "( )" let i=( i * 10 ) echo $i _# "i++" let i++ echo $i _# "i=0" _# "終了コードに注意" let i=0 echo $? _# "i=1" _# "終了コードに注意" let ++i echo $? # ******** [TEST-1 Output] ******** $ bash sample.sh # i+=1 11 # ( ) 110 # i++ 111 # i=0 # 終了コードに注意 1 # i=1 # 終了コードに注意 0 # 終了ステータス $ echo $? 0 local †local [option] name[=value] … ローカルな変数として定義する。localは関数内でのみ利用可能。localは、関数とその子に可視スコープを制限する。 # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { echo "# $@"; } global=global function test_local() { echo "=> test_local" # globalは、test_localと子で可視 local global echo $global global="test_local" echo $global # test_local2でglobalは上書きされる test_local2 echo $global echo "=> end test_local" } function test_local2() { echo "==> test_local2" # test_local2ではglobalはlocal定義していない echo $global # test_localのglobalを上書き global="test_local2" echo $global echo "==> end test_local2" } _# "globalを定義する関数を呼び出し" test_local _# "echo global" echo $global # ******** [TEST-1 Output] ******** $ bash sample.sh # globalを定義する関数を呼び出し => test_local test_local ==> test_local2 test_local test_local2 ==> end test_local2 test_local2 => end test_local # echo global global # 終了ステータス $ echo $? 0 logout †logout [n] ログインシェルを終了する。親シェルにステータスnを返す。 $ logout Saving session... ...copying shared history... ...saving history...truncating history files... ...completed. [プロセスが完了しました] mapfile †mapfile [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] 標準入力またはディスクリプタfdから行を読みインデックス配列へ格納する。デフォルトの配列変数は
$ mapfile 1 2 3 $ echo ${MAPFILE[0]} 1 $ echo ${MAPFILE[@]} 1 2 3 # インデックス1に格納 $ mapfile -O 1 4 $ echo ${MAPFILE[@]} 1 4 3 # 改行を続けて入力 # 何もない行はNULで配列に格納されている $ mapfile 1 4 $ echo ${MAPFILE[0]} 1 $ echo ${MAPFILE[3]} 4 # コールバック付き # 2行ごとにコールバックを実行 # 引数には、次に割り当てられるインデックスと要素が渡される # コールバック実行時、要素は未割り当てである $ mapfile -C 'IFS=" "; printf "# ARRAY=%s\n# " "${MAPFILE[*]}"; echo' -c 2 -t a b # ARRAY=a # 1 b c d # ARRAY=a b c # 3 d printf †printf [-v var] format [arguments] フォーマット形式に沿って出力する。-vオプションは、出力結果をvarに格納する。 # sample.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# "1 フォーマット指定なし" printf "Hello, World\n" echo "exit: $?" _# "2 エスケープ文字は解釈されない" printf "%s\n" "Hello,\nWorld\n" echo "exit: $?" _# "3 エスケープ文字も解釈される" printf "%b" "Hello,\nWorld\n" echo "exit: $?" _# "4 フォーマット指定より多い引数を与える" printf "%b" "Hello,\nWorld\n" "Hello,\nWorld\n" echo "exit: $?" _# "5 出力を変数に代入" printf -v var "%b" "Hello, World\n" printf "$var" _# "6 シェルインプット可能形式で出力する" printf "%q\n" "Hello World\n" _# "7 日付関連の出力" _# " strftime" _# " 参考: https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strftime.3.html" printf "%(%Y-%m-%d %H:%M:%S)T\n" printf "%(%F %T)T\n" printf "%(%F %T%z)T\n" function log() { printf "%(%F %T)T: %b\n" -1 "$*" } log "exit successfully" # ******** [TEST-1 Output] ******** $ bash sample.sh #1 フォーマット指定なし Hello, World exit: 0 #2 エスケープ文字は解釈されない Hello,\nWorld\n exit: 0 #3 エスケープ文字も解釈される Hello, World exit: 0 #4 フォーマット指定より多い引数を与える Hello, World Hello, World exit: 0 #5 出力を変数に代入 Hello, World #6 シェルインプット可能形式で出力する Hello\ World\\n #7 日付関連の出力 # strftime # 参考: https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strftime.3.html 2019-02-02 10:17:12 2019-02-02 10:17:12 2019-02-02 10:17:12+0900 2019-02-02 10:17:12: exit successfully # 終了ステータス $ echo $? 0 read †read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …] 標準入力または
# read-sample1.sh # ++++++++++++++++++++++ [TEST-1 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " 標準入力から行を読み取り、word1とword2へ代入" read word1 word2 echo $word1 echo $word2 # ******** [TEST-1 Output] ******** $ bash read-sample1.sh # 標準入力から行を読み取り、word1とword2へ代入 hello world hello world # read-sample2.sh # ++++++++++++++++++++++ [TEST-2 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " REPLYから読み取り" read echo $REPLY # ******** [TEST-2 Output] ******** $ bash read-sample2.sh # REPLYから読み取り hello world hello world # read-sample3.sh # ++++++++++++++++++++++ [TEST-3 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " 配列に代入" declare -a array=() read -a array echo ${array[0]} # ******** [TEST-3 Output] ******** $ bash read-sample3.sh # 配列に代入 hello world hello # read-sample4.sh # ++++++++++++++++++++++ [TEST-4 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " 行区切りを.にする" read -d . echo $REPLY # ******** [TEST-4 Output] ******** $ bash read-sample4.sh # 行区切りを.にする hello world hello.hello world hello # read-sample5.sh # ++++++++++++++++++++++ [TEST-5 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " 指定文字数だけ読む" read -n 10 chars echo "chars: $chars" # ******** [TEST-5 Output] ******** $ bash read-sample5.sh # 指定文字数だけ読む 1234567890chars: 1234567890 # read-sample6.sh # ++++++++++++++++++++++ [TEST-6 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " プロンプトに表示する" read -p "input> " line echo "input: $line" # ******** [TEST-6 Output] ******** $ bash read-sample6.sh # プロンプトに表示する input> inputline input: inputline # read-sample7.sh # ++++++++++++++++++++++ [TEST-7 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " タイムアウト付きで読み取る(10sec)" read -t 10 echo $REPLY # ******** [TEST-7 Output] ******** $ bash read-sample7.sh # タイムアウト付きで読み取る(10sec), aだけ打って待つ a # read-sample8.sh # ++++++++++++++++++++++ [TEST-8 Commands] ++++++++++++++++++++++ _#() { printf "%s\n" "#$@"; } _# " ファイルから読む" cat dummy.txt while IFS= read line; do echo $line done < dummy.txt # ******** [TEST-8 Output] ******** $ bash read-sample8.sh # ファイルから読む hello world hello world readarray †readarray [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] 標準入力またはディスクリプタfdから行を読み取り配列へ格納する。mapfileの別名。 source †source filename
type †type [-afptP] [name …] nameの解釈を表示する。
# nameの種別を表示する $ type -t la alias $ type -t command builtin $ type -t while keyword $ function my() { echo "my"; } $ type my my は関数です my () { echo "my" } $ type -t my function # 実行可能なコマンド全て表示 $ type -a ls ls は `ls -G` のエイリアスです ls は /bin/ls です # PATH内のlsを探す $ type -P ls /bin/ls $ which ls /bin/ls # 実行されるディスク上のファイル名を表示する $ type -p cut /usr/bin/cut $ type -p ls # ビルドインは何も表示されない typeset †typeset [-afFgrxilnrtux] [-p] [name[=value] …] declareの別名。 ulimit †ulimit [-HSabcdefiklmnpqrstuvxPT] [limit] シェルで起動されるプロセスが利用可能なリソース制御をする。
例:現在の上限を表示する [vagrant@localhost ~]$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 1880 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 1880 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited 例:オープンできるファイルディスクリプタの最大数を変更する [vagrant@localhost ~]$ ulimit -Hn 2048 [vagrant@localhost ~]$ ulimit -n $((1024*2)) [vagrant@localhost ~]$ ulimit -n 2048 ulimitの設定の反映は、systemd制御下のデーモン管理との兼ね合いもある。詳しくはsystemd、limitsなどのキーワードで調べる必要あり。 $ cat <<EOF > Vagrantfile # -*- mode: ruby -*- # vi: set ft=ruby : # All Vagrant configuration is done below. The "2" in Vagrant.configure # configures the configuration version (we support older styles for # backwards compatibility). Please don't change it unless you know what # you're doing. Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.provision "shell", inline: <<-SHELL yum update -y SHELL end EOF $ vagrant up $ vagrant ssh [vagrant@localhost ~]$ cat /etc/systemd/user.conf # This file is part of systemd. # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # # You can override the directives in this file by creating files in # /etc/systemd/user.conf.d/*.conf. # # See systemd-user.conf(5) for details [Manager] #LogLevel=info #LogTarget=console #LogColor=yes #LogLocation=no #SystemCallArchitectures= #TimerSlackNSec= #DefaultTimerAccuracySec=1min #DefaultStandardOutput=inherit #DefaultStandardError=inherit #DefaultTimeoutStartSec=90s #DefaultTimeoutStopSec=90s #DefaultRestartSec=100ms #DefaultStartLimitInterval=10s #DefaultStartLimitBurst=5 #DefaultEnvironment= #DefaultLimitCPU= #DefaultLimitFSIZE= #DefaultLimitDATA= #DefaultLimitSTACK= #DefaultLimitCORE= #DefaultLimitRSS= #DefaultLimitNOFILE= #DefaultLimitAS= #DefaultLimitNPROC= #DefaultLimitMEMLOCK= #DefaultLimitLOCKS= #DefaultLimitSIGPENDING= #DefaultLimitMSGQUEUE= #DefaultLimitNICE= #DefaultLimitRTPRIO= #DefaultLimitRTTIME= 参考 http://man7.org/linux/man-pages/man5/systemd-system.conf.5.html unalias †unalias [-a] [name … ] aliasを削除する。-aオプションで、全てのaliasを削除する。 $ alias hoge alias hoge='ls -la' $ unalias hoge $ alias hoge -bash: alias: hoge: 見つかりません シェルの振る舞い修正 †set †set [--abefhkmnptuvxBCEHPT] [-o option-name] [argument …] set [+abefhkmnptuvxBCEHPT] [+o option-name] [argument …] shopt †shopt [-pqsu] [-o] [optname …] シェル変数 †URL https://www.gnu.org/software/bash/manual/bash.html#Bourne-Shell-Variables ジョブ制御 †
例:pingで始まるジョブを表示 $ ping localhost > /dev/null & [1] 37862 $ jobs %ping [1]+ 実行中 ping localhost > /dev/null & ジョブ制御コマンド †bg †bg [jobspec …] 中断されたジョブをバックグラウンドで再開する。jobspecが指定されない場合は現在のジョブが使われる。 $ sleep 140 ^Z [1]+ 停止 sleep 140 $ bg [1]+ sleep 140 & $ jobs [1]+ 実行中 sleep 140 & fg †fg [jobspec] フォアグラウンドでジョブを再開する。jobspecが指定されない場合は現在のジョブが使われる。 $ sleep 140 & [1] 42581 $ fg sleep 140 jobs †jobs [-lnprs] [jobspec] jobs -x command [arguments] アクティブなジョブ一覧を表示する。
$ sleep 180 & [1] 44141 $ sleep 280 & [2] 44156 $ jobs [1]- 実行中 sleep 180 & [2]+ 実行中 sleep 280 & $ jobs -n $ jobs -n $ jobs -r [1]- 実行中 sleep 180 & [2]+ 実行中 sleep 280 & $ jobs -s $ jobs -p 44141 44156 $ jobs -l [1]- 44141 実行中 sleep 180 & [2]+ 44156 実行中 sleep 280 & kill †kill [-s sigspec] [-n signum] [-sigspec] jobspec or pid kill -l|-L [exit_status] ジョブまたはプロセスにシグナルを送る。シグナルの指定がない場合は、 例:バックグラウンドで実行中のプロセスにシグナルを送る # ジョブ番号を指定してSIGTERMを送る $ sleep 120 & [1] 14120 $ kill %1 [1]+ Terminated: 15 sleep 120 # プロセスIDを指定してSIGKILLを送る $ sleep 120 & [1] 14607 $ kill -SIGKILL 14607 [1]+ Killed: 9 sleep 120 # シグナル番号15(SIGTERM)をジョブ番号1に送る $ sleep 120 & [1] 15222 $ kill -n 15 %1 [1]+ Terminated: 15 sleep 120 例:シグナル一覧を表示する $ kill -L 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE 9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG 17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD 21) SIGTTIN 22) SIGTTOU 23) SIGIO 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGINFO 30) SIGUSR1 31) SIGUSR2 $ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGEMT 8) SIGFPE 9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGURG 17) SIGSTOP 18) SIGTSTP 19) SIGCONT 20) SIGCHLD 21) SIGTTIN 22) SIGTTOU 23) SIGIO 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGINFO 30) SIGUSR1 31) SIGUSR2 例:シグナル番号のシグナルを表示する $ kill -l 15 TERM $ kill -L 9 KILL wait †wait [-fn] [jobspec or pid …] jobspecまたはpidの終了を待つ。jobspecまたはpidの指定がない場合、現在アクティブの全ての子プロセスを待つ。-nオプションは、いずれかのジョブが終了するのを待つ。 例;バックグラウンドのジョブの終了を待つ # ジョブ1と2の終了を待つ $ sleep 10 & [1] 18848 $ sleep 20 & [2] 18854 $ wait [1]- 終了 sleep 10 [2]+ 終了 sleep 20 例:バックグラウンドのいずれかのジョブが終了するのを待つ $ sleep 10 & [1] 19591 $ sleep 20 & [2] 19612 $ wait -n [1]- 終了 sleep 10 disown †disown [-ar] [-h] [jobspec … | pid … ] アクティブなジョブテーブルからジョブを削除する。-hオプションは、ジョブはテーブルから削除されずSIGHUPが送られないようにする。 例:バックグラウンドジョブをアクティブなジョブテーブルから削除する # 実行中のジョブを停止する $ sleep 130 & [1] 24765 $ jobs [1]+ 実行中 sleep 130 & $ disown %1 $ jobs # 停止中のジョブを停止する $ sleep 130 & [1] 25215 $ jobs [1]+ 実行中 sleep 130 & $ fg %1 sleep 130 ^Z [1]+ 停止 sleep 130 $ disown -r %1 -bash: 警告: プロセスグループ 25215 のジョブ 1 を削除しています $ jobs suspend †suspend [-f] ジョブを中断する。SIGCONTで再開する。ログインシェルはサスペンドされない。Ctrl-Zによる中断に等しい。 例;rootで作業中にシェルを一時中断する [vagrant@localhost ~]$ su パスワード: [root@localhost vagrant]# ls foo test.sh [root@localhost vagrant]# suspend [1]+ 停止 su [vagrant@localhost ~]$ fg su [root@localhost vagrant]# 参考
コマンド履歴 †fc †fc [-e ename] [-lnr] [first] [last] fc -s [pat=rep] [command] コマンド履歴を編集し再実行する。 $ history 3 557 ls 558 sleep 30 & 559 history 3 # viで557-558番を編集 $ fc 557 558 ls sleep 30 & history †history [n] history -c history -d offset history -d start-end history [-anrw] [filename] history -ps arg コマンド履歴を表示する。 例:履歴の最後の3行を表示する $ history 3 560 ls 561 sleep 30 & 562 history 3 例:コマンド履歴完全クリアする $ :> ~/.bash_history && history -c 履歴展開 †
$ history 1 cat ~/.bash_history 2 echo $A 3 history 1 4 history 3 5 A=100 echo $A 6 history 7 echo $A 8 echo $A A=200 9 A=300 echo $A 10 ! 2 11 !=2 12 history 13 echo $A A=200 14 ls ls ls ls 15 history $ !?hist history 1 cat ~/.bash_history 2 echo $A 3 history 1 4 history 3 5 A=100 echo $A 6 history 7 echo $A 8 echo $A A=200 9 A=300 echo $A 10 ! 2 11 !=2 12 history 13 echo $A A=200 14 ls ls ls ls 15 history 16 history $ echo "foo"; !# echo "foo"; echo "foo"; foo foo 参考リンク † |