#author("2024-04-14T09:20:48+09:00","","") #author("2024-04-21T14:16:08+09:00","","") #topicpath * 目次 [#dc23a09c] #contents(); #ls2(Lang/shell/bash/); //////////////////////////////////////////////////////////////////////////////// * 変数 [#jc85b30c] ** 代入 [#jb98a96a] - 等号の両隣にスペースを入れてはいけない - 通常の変数 val=123 - 配列 array=( 0 1 2 3 ) array=([0]=0 [3]=3 [1]=1 [2]=2 ) # 配列番号指定。記述は順番通りでなくても良い ** 削除 unset [#r4dc11fd] unset val1 # 変数の削除 unset array[0] array[3] # 配列から、特定の要素のみを削除 unset array # 要素指定無しで配列を指定した場合、その配列全体を削除 ** 連番の数値を自動生成する [#se270791] $ echo {1..10} 1 2 3 4 5 6 7 8 9 10 $ echo {10..1} 10 9 8 7 6 5 4 3 2 1 - 飛び飛びの値の数列を生成したい場合は、 [[seq>Lang/shell/bash#seq]]を使うと良い。 //////////////////////////////////////////////////////////////////////////////// * 配列 [#c2070597] //============================================================================== ** 初期化 [#xdd7180f] A=() A=( 1 2 3 4 ) //============================================================================== ** 追加 [#u91caa18] - 配列 A の後ろに変数 B を追加 A=( ${A[@]} ${B} ) 又は A+=( ${B} ) - 配列 A の前に変数 B を追加 A=( ${B} ${A[@]} ) //============================================================================== ** 部分配列の取り出し [#q35b167c] //------------------------------------------------------------------------------ *** 書式 [#va7a489d] - 書式:配列 VAR の配列番号 <begin> から <end> までを取り出す ${VAR[@]:<begin>:<end>} - 書式:配列 VAR の配列番号 <begin> 以降を取り出す ${VAR[@]:<begin>} - 書式:配列 VAR の配列番号 <end> までを取り出す ${VAR[@]::<end>} //------------------------------------------------------------------------------ *** 例 [#t282aefa] A=( 0 1 2 3 4 5 6 ) echo ${A[@]:2:4} # 実行結果: 2 3 4 5 echo ${A[@]:2} # 実行結果: 2 3 4 5 6 echo ${A[@]::2} # 実行結果: 0 1 //////////////////////////////////////////////////////////////////////////////// * 特殊変数 [#a73a8fb9] ** 環境変数 [#rec3bc6e] |~変数 |~意味 | |$0 |シェル、あるいはプログラムの名前 | |$1 $2 ... | 引数 | |$@ |引数の配列 | |$* |~| |$# |引数の数 | |$- |シェルの呼出し時に指定されたオプション | |$_ |前のコマンドの最後の引数 | |$! |最後に起動したバックグラウンドジョブのPID | |$? |最後に実行したコマンドや関数のRETURN値 | |$$ |そのプロセスのPID | //////////////////////////////////////////////////////////////////////////////// ** その他の特殊変数 [#nead9074] |~変数 |~意味 | |${#val[@]} |${val} の長さ(文字列としてみた場合の文字数に相当) | |${#val} |配列${val[@]} の配列数 | |~変数 |~意味 | |${#val} |${val[0]} の長さ(文字列としてみた場合の文字数に相当) | |${#val[N]} |${val[N]} の長さ(文字列としてみた場合の文字数に相当) | |${#val[@]} |配列${val[@]} の配列数 | //////////////////////////////////////////////////////////////////////////////// ** 変数と環境変数 [#xc5924ed] - 定義されている変数を確認するには、set と export が使える。 - export は、環境変数(export されている変数)を表示する - set は、環境変数に加え、シェル変数も表示する。よって、環境変数を調べるのに使ってはいけない。 //////////////////////////////////////////////////////////////////////////////// * match [#g98339b4] 変数 ${hoge} に対して |~書法 |~意味 |~備考 | |${hoge%<ptrn>} |末尾から見て、<ptrn> に最も短く一致する部分を取り除いた値 | | |${hoge%%<ptrn>} |末尾から見て、<ptrn> に最も長く一致する部分を取り除いた値 | | |${hoge#<ptrn>} |先頭から見て、<ptrn> に最も短く一致する部分を取り除いた値 | | |${hoge##<ptrn>} |先頭から見て、<ptrn> に最も長く一致する部分を取り除いた値 | | |${hoge/<ptrn>/<str>} |${hoge} の値で<ptrn>に最も長く一致する最初の部分だけを<str>で置換する |<ptrn>が # で始まる場合は、hoge の先頭と一致しなければならない&br;<ste>がNULLの場合は、一致した部分が削除される。| |${hoge//<ptrn>/<str>} |${hoge} の値で<ptrn>に一致する部分全てを<str>で置換する |~| ** 例 [#s0cb59d0] FULLPATH=/home/user/file_name.ext PATH=${FULLPATH%/*} # パス部分を表示 FNAME=${FULLPATH##*/} # ファイル名以下を表示 FNAME_ONLY=${FULLPATH%.*} # ファイル名のみを表示 DOTEXT=${FULLPATH##*.} # 拡張子を表示 //////////////////////////////////////////////////////////////////////////////// * パス文字列操作 [#k8728c5a] //============================================================================== ** パスからファイル名を取得する - basename [#md335c15] - 書式: $ basename <path> - 実行例: $ basename /usr/bin/perl perl //============================================================================== ** パスからディレクトリ部分を取得する - dirname [#q81c99b5] - 書式: $ dirname <path> - 実行例: $ dirname /usr/bin/perl /usr/bin //////////////////////////////////////////////////////////////////////////////// * 置換演算子 [#fb1828e2] 変数 val に対して |~書法 |>|~意味 |~備考 | |~|~val が存在しNULLでない場合 |~左以外の場合 |~| |${val:-word} |valを返す |wordを返す | | |${val:=word} |valを返す |valにwordを設定して返す |位置パラメータや特殊なパラメータは除く | |${val:?msg} |valを返す |val の後にmsgを出力し、現在のコマンド或はスクリプトを中止する(対話型シェルではない場合)&br;msgを省略すると "parameter null or not set" を出力する。 | | |${val:+word} |wordを返す |NULLを返す | | |${val:offset} |>|サブ文字を展開する。&br;offsetの位置からlength文字の長さのサブ文字r列をvalから取り出す。&br;文字の位置は0からカウントする。&br;lengthが省略された場合は、offsetからvalの最後までが展開の対象になる。&br;offsetが負値の場合、開始位置はvalの終端からカウントされる。&br;valが@の場合、lengthはoffsetを先頭とする位置パラメータの番号になる。 | | |${val:offset:length} |~|~| | //////////////////////////////////////////////////////////////////////////////// * 評価 [#f3207d47] ** 文字列評価 [#o6a8d813] |~記号 |~意味 | |-z <string> |<string> の長さが 0 ならば真 | |-n <string> |<string> の長さが 0 でなければ真 | |string1 == string2 |文字列が同じならば真。== の代わりに = でも可 | |tring1 != string2 |2 つの文字列が異なれば真 | |string1 < string2 |現 在のロケールにおいて、string1 が string2 よりも辞書順で前にある場合に真 | |string1 > string2 |現在のロケールにおいて、string1 が string2 よりも辞書順で後に ある場合に真 | *** Cのstrlen() 的使い方をしたいとき [#q8a20d89] - ${val} の文字列長が0であるか否かによって処理を変える場合は if [ -n ${val} ]; then 処理1 fi ではなく、 if [ ${#val} -gt 0 ]; then 処理1 fi とするとうまくいく。 ** ファイル属性 [#a273542e] |~記号 |~意味 | |-d <file> |<file>が存在し、且つディレクトリである | |-e <file> |<file>が存在する | |-f <file> |<file>が存在し、且つ通常ファイルである | |-h <file> |<file>が存在し、且つシンボリックリンクである | |-r <file> |<file>にreadパーミッションが与えられている | |-s <file> |<file>が存在し、且つ空ではない | |-w <file> |<file>にwriteパーミッションが与えられている | |-x <file> |<file>にexecuteパーミッションが与えられている | |-O <file> |<file>の所有者である | |-G <file> |<file>のグループIDが自分のものと一致する(複数のグループに属している場合はそのいずれかと一致する) | |<file1> -nt <file2> |<file1>が<file2>よりも新しい(ファイル変更時刻を比較) | |<file1> -ot <file2> |<file1>が<file2>よりも古い(ファイル変更時刻を比較) | ** 数値評価 [#rdf1765e] |~記号 |~意味 | |arg1 -eq arg2 |arg1 が arg2 に対して等しい | |arg1 -ne arg2 |arg1 が arg2 に対して等しくない | |arg1 -lt arg2 |arg1 が arg2 に対して小さい | |arg1 -le arg2 |arg1 が arg2 に対して小さいか、等しい | |arg1 -gt arg2 |arg1 が arg2 に対して大きい | |arg1 -ge arg2 |arg1 が arg2 に対して大きいか、等しい | ※ arg1, arg2 には、正または負の整数を使用出来る。 //////////////////////////////////////////////////////////////////////////////// * フロー制御 [#i2364e5e] //============================================================================== ** if [#ne6a73ae] if [ 条件式1 ]; then 処理1 elif [ 条件式2 ]; then 処理2 else 処理3 fi - elif は、C/C++の else if に相当 //============================================================================== ** for [#for] for i in "${val_list[@]}"; do 処理 if [ 次ループへ飛ぶ条件 ]; then continue; fi if [ ループ終了条件 ]; then break; fi done -- val_list: 配列変数。配列全てを表す為、${val_list[@]} と書く。 -- i: 配列 ${val_list[@]} から1要素ずつ代入される為に用意する変数。処理の中ではこのiで取り扱う。変数名は i でなくとも何でも可。for文の中では代入先としての位置づけなので、先頭に変数を表すドル記号($)をつけてはいけない。 //------------------------------------------------------------------------------ *** index を使う [#for-index] for ((i=0; i < 10; ++i)); do echo "i = ${i}"; done -- この書式の場合、括弧は上記例のように二重にする必要があるので注意。 //------------------------------------------------------------------------------ *** コマンドの実行結果を for ループ条件にする [#for-command-result] for val in $(<command>); do 処理 done - 例 for f in $(ls); do echo "file: $f"; done - $(<command>) を配列のときと同じようにダブルクォートで括ってはいけない。(自動連番の場合と同様) //------------------------------------------------------------------------------ *** 自動連番でループを回す [#for-automatic-serial-number] for i in {1..10}; do 処理 done とすれば出来る。但し、上の例の {1..10} はダブルクォートで括ってはいけないので注意! -- 昇順の自動連番以外で、自動生成した数列(飛び飛び、降順など)でアクセスしたい場合は seq を使用するとよい。 //------------------------------------------------------------------------------ *** 数列の自動生成(seq) を使う [#seq] - seq は、昇順・降順で番号を自動生成する。 seq <first_num> [increment_num] <last_num> seq [first_num] <last_num> -- first_numが省略された場合のデフォルト値は1 -- increment_num が省略された場合のデフォルト値も1 -- 全ての数値は浮動小数点値として解釈される -- 詳しくは、 man 1 seq を参照のこと - 使用例 -- 配列に対し、順次アクセス VALS=( a b c d e ); for i in `seq 0 5`; do # double-quote で括らないこと! echo "val is $VALS[$i]"; done -- 配列に対し、逆順にアクセス VALS=( a b c d e ); for i in `seq 5 -1 0`; do # double-quote で括らないこと! echo "val is $VALS[$i]"; done -- 配列に対し、飛び飛びにアクセス VALS=( a b c d e ); for i in `seq 0 2`; do # double-quote で括らないこと! echo "val is $VALS[$i]"; done //============================================================================== ** while [#while] while [ 条件 ]; do 処理 if [ 次ループへ飛ぶ条件 ]; then continue; fi if [ ループ終了条件 ]; then break; fi done -- continue, break はfor文同様に使用出来る。 -- 条件式が成立する限り、ループを続ける。 -- do キーワード以降は、for文と同じ //============================================================================== ** case [#fa3ff88b] case ${i} in word_1 ) 処理1 ;; word_2 ) 処理2 ;; * ) 上記どの条件にも当てはまらない場合に行う処理 ;; esac -- ${i} は、評価したい変数(そのときに応じて、名前は何でも可) -- word_1, word_2,... は、${i} が一致するか否かを調べたいキーワードを記述。 -- C/C++ のswitch-case 等、同等の構文に見られる default に相当するキーワードがないので、default 相当のことをやらせたい場合には、上記のように最後に * の条件文を書いておく。 -- 各処理の最後には、2つの連続したセミコロン(;;)を記述する。これが、C/C++ の場合の break 文に相当する。 //////////////////////////////////////////////////////////////////////////////// * echo の出力先 [#c1d933eb] - 通常、特に指定しなければ、echo は stdout のリダイレクト先へ出力される - stdout への出力を明示するとき $ echo "message" >&1 - stderr へ出力するとき $ echo "message" >&2 //////////////////////////////////////////////////////////////////////////////// * printf [#nbeaafe7] - C/C++ の printf() のように、フォーマット文字列を解釈して出力する //============================================================================== ** 改行 [#xe04ff81] - echo と違い、改行したい時は "\n" が必要(echo の場合は -n を付けない限り、終端で自動改行される) - 渡す引数が文字列の場合は問題ないが、配列である場合は改行文字が思うように機能しないので注意が必要。 - 下記のうち、改行文字が正しく動作するのは (4) のみである。他は表示内容の上にコマンドプロンプトなど、後から表示されるものに一部上書きされたりする。 (1) A=( "Hello" "world" "\n" ); printf "${A[@]}"; (2) printf "${A[@]}\n"; (3) B=( "Hello" "world\n" ); printf "${B[@]}"; (4) C="Hello world\n"; printf "${C}\n"; - そもそもこういう引数は printf の仕様から見て間違っており、printf の bug ではない。 - 安易に echo と printf は単純な置き換えが出来ない例として示した。 //////////////////////////////////////////////////////////////////////////////// * pipe [#q80b142e] //============================================================================== ** alias を含むコマンドライン [#u200534f] - .bashrc で alias llast1='ls -1tr | tail -1' みたいなことをしている場合に、そのファイルの内容をgrep して別ファイルに保存する場合は、 $ grep hoge `llast1` | grep -v huga > a.log とやってもうまくいかない。 $ cat < `llast1` | grep hoge | grep -v huga | tee a.log とやればうまくいく。または $ ( cat `llast1` | grep hoge | grep -v huga ) | tee a.log でもうまくいく。 //============================================================================== ** pipe で繋がれたコマンドラインの各マンドの終了ステータスを取得する - PIPESTATUS [#PIPESTATUS] - 下記のようなコマンドラインを実行したとする。 $ command1 | command2 command3 | command4 - その終了ステータスは ${PIPESTATUS[@]} に格納される。 - 例: $ exit 3 | exit 5 | exit 2 | exit 0 | exit 1 $ echo ${PIPESTATUS[@]} 3 5 2 0 1 - この方法は、下記のようなコマンドラインで command の終了ステータスを取得したい場合に使用する。 $ command 2>&1 | tee <file-name> -- このようなコマンドラインの終了ステータス ''$?'' は、パイプで最後に繋がれたコマンドの終了ステータスが格納される。上記の例では tee コマンドの終了ステータスとなる。command の終了ステータスを取得したい場合は ''${PIPESTATUS[0]}'' を見る必要がある。 //////////////////////////////////////////////////////////////////////////////// * 算術式を扱う [#w8d8bbdd] //============================================================================== ** expr を使わない [#ue2bee62] (( sum = $a + $b )) # addition (( sub = $a - $b )) # subtraction (( mul = $a * $b )) # multiplication (( div = $a / $b )) # division (( mod = $a % $b )) # modulo (remainder) (( exp = $a ** $b )) # exponentiation //============================================================================== ** expr を使う [#e88d9f46] sum=$(expr $a + $b ) # addition sub=$(expr $a - $b ) # subtraction div=$(expr $a / $b ) # division mod=$(expr $a % $b ) # modulo (remainder) - expr では、以下の演算は出来ない: -- 乗算 -- べき乗算 //============================================================================== ** 参考リンク [#iaac37ad] - [[How to use arithmetic operations in bash>https://www.xmodulo.com/arithmetic-operations-bash.html]] //////////////////////////////////////////////////////////////////////////////// * shell script の debug [#oa3d1b9e] //============================================================================== ** -x オプション [#ia5413f8] -x オプションは、単純なコマンド、for コマンド、case コマンド、 select コマンド、算術 for コマンドをそれぞれ展開した後、 PS4 を展開した値を表示し、その後にそのコマンドと展開した引き数や、結び付いた単語のリストを表示する。 - 実行方法 -- script の debug には -x オプションを使って起動する。 $ bash -x <shell-script-file> -- 又は、スクリプトファイルのインタプリタ行に -x を付与する #!/bin/bash -x - 表示内容 -- + で始まる行:スクリプト内で実行されたコマンド -- ++ で始まる行:バッククォート内で実行されたコマンド //============================================================================== ** -v オプション [#nb79c600] - -v オプションは、これから実行しようとする行を逐次表示する - 実行方法 -- script の debug には -v オプションを使って起動する。 $ bash -v <shell-script-file> //////////////////////////////////////////////////////////////////////////////// * 呼び出し元の関数名を取得 - FUNCNAME [#FUNCNAME] - 呼び出し元の関数名は、下記から取得出来る: ${FUNCNAME[1]} //////////////////////////////////////////////////////////////////////////////// * 呼び出し元の行番号を取得 - BASH_LINENO [#BASH_LINENO] - 呼び出し元の行番号は、下記から取得出来る: ${BASH_LINENO[0]} //////////////////////////////////////////////////////////////////////////////// * ログ関数の実装例 [#log-func] function log_print { local readonly msg="${@}"; local readonly func=${FUNCNAME[1]}; local readonly line=$(printf "%04d" ${BASH_LINENO[0]}); echo "${line}: ${func}: ${msg}"; # printf で複数の値を一度にフォーマットすることは出来ない。 } //////////////////////////////////////////////////////////////////////////////// * ini ファイル形式のデータを読む [#r5d4835f] function get_section_from_ini_file { # 指定された ini file 形式のファイルから、指定されたセクションの内容を取り出し出力する local ini_file=${1}; local section=${2}; sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \ -e 's/;.*$//' \ -e 's/[[:space:]]*$//' \ -e 's/^[[:space:]]*//' \ -e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \ < ${ini_file} \ | sed -n -e "/^\[${section}\]/,/^\s*\[/{/^[^;].*\=.*/p;}"; } //////////////////////////////////////////////////////////////////////////////// * 組み込みコマンド [#v962abf2] //============================================================================== ** local <val> [#zf3e6868] - シェル変数 <val> を関数内ローカルな変数として定義する。 - 関数外では使用出来ないことに注意。 - 例 function hoge { local TEST=1; ... } //============================================================================== ** readonly <val> [#wf7bcc16] - シェル変数 <val> を読み取り変数変数として定義する。(C++で言うところの const 修飾子) - 例 readonly OK=0; readonly NG=1; //============================================================================== ** local readonly <val> [#f371f4d5] - シェル変数を、関数内ローカルな読み取り専用変数として定義する。 - &color(red){逆にして ''readonly local'' とすると、 local としても readonly としても機能しないので注意。}; - 例 function hoge { local readonly TEST=1; ... } //////////////////////////////////////////////////////////////////////////////// * Scripts [#d2966a3b] ** llast [#ked8a072] #!/bin/bash TARGET=${1}; TARGET_FILE=`ls -1tr | tail -${TARGET} | head -1` echo ${TARGET_FILE}; ** findd [#d228ab40] #!/bin/bash # find ${@} find -printf "%AY-%Am-%Ad %p\n"; # 最後にアクセスした日付を併記 find ${@} -printf "%TY-%Tm-%Td %p\n"; # 最後に修正した日付を併記 //////////////////////////////////////////////////////////////////////////////// * トラブルシューティング [#e0482f92] //============================================================================== ** /bin/sh: 警告: シェルレベル (1000) は高すぎます。1に再設定されました [#v15b4558] - シェルスクリプトまたはシェル関数の無限再帰呼出しを行っている可能性がある。