Bash

基本的に非対話型シェルを中心に記載。
詳細な内容については、本家サイトのリファレンスマニュアル external_linkを参照。

Bash 5.2(20220919)時点の情報で記載。

基本

変数宣言

関数内での変数は必ずlocalまたはreadonlyを指定し、変数スコープを明示化する。
localまたはreadonlyを指定しなかった場合、スコープはグローバルになるので注意。

#!/bin/bash # グローバル変数 foobar1="aaa" function hoge() { foobar2="bbb" # グローバル変数(関数内外でアクセス可) local foobar3="bbb" # ローカル変数(関数内のみアクセス可) readonly foobar4="bbb" # ローカル変数(関数内のみアクセス可) }

サブプロセス実行

()で囲った処理はサブプロセスで実行される。

echo $BASHPID ( echo $BASHPID ) echo $BASHPID
142113
142114
142113

外部コマンド起動

$(コマンド)で実行する。

list=$(ls -1)

バッククオート(`~~~`)で囲むのは非推奨な古い書き方。

数値計算

sum=$(( (i + 2) *10 ))

exprは性能が悪いので使用しない。

スクリプト終了時に必ず実行したい処理

trapを使用することで実現可能。

function cleanup() { # 終了処理 ~~~ 終了処理 ~~~ } # EXIT時にcleanup関数を実行 trap cleanup EXIT ~~~ メイン処理 ~~~

基本構文

シェバン(shebang)

起動時にスクリプトを読み込むインタプリタを指定する。シェルスクリプトの先頭行に記載する。

#!/bin/bash  ※シェバン(shebang) ~~ 2行目以降に処理を記載 ~~

/bin/shを指定してるサイトを見かけるが、使用するシェルを明確に記載するべき。
またshではPOSIX仕様に沿った機能以外使用できない。

コメント

コメント記号#から行の終端までをコメントとして認識する。複数行コメントは存在しない。

# 行の先頭からすべてコメント foobar=20 # '#'から行末までがコメント(foobar=20は有効な式)

行の継続

行末にバックスラッシュ\を記載した場合、コマンドが次行に継続することを示す。

# cp -p "/tmp/aaa/*" "/tmp/bbb"を2行に分けて記載 cp -p "/tmp/aaa/*" \ "/tmp/bbb"

シングルクォート、ダブルクォート

文字列を記述する場合、シングルクォート'文字列'またはダブルクォート"文字列"で囲む。
但しシングルクォートとダブルクォートには下記点で異なる。

項目説明
シングルクォートシェル展開されない
ダブルクォートシェル展開される

ANSI-C文字

下記に代表的なものを記載。

文字説明
\n改行
\rキャリッジリターン
\t水平タブ
\バックスラッシュ
'シングルクォート
"ダブルクォート
?クエスチョン
\nnn8進数
\xHH16進数
\uHHHH, \UHHHHHHHHUnicode文字

パイプライン

パイプを使用することであるコマンドの出力を次のコマンドの入力として渡すことができる。
パイプの形式は以下の通り。コマンドとコマンドの間に|または|&で記述する。
[time [-p]] [!] command1 [ | or |& command2 ] …

|の場合は標準出力が次コマンドに渡されるが、|&の場合は標準出力に加えて標準エラーも渡される(2>&1と同様)。
パイプを使用した場合の終了ステータスは、pipefailが無効な場合はパイプの最後のコマンドの終了ステータスとなる。
pipefailが有効な場合は異常終了したコマンドの終了ステータスとなる。

# ※shファイルが存在しない場合 ls -1 *.sh | wc -l echo $?
※pipefailが無効な場合、最後のコマンド`wc -l`の終了ステータスが返却
ls: cannot access '*.sh': No such file or directory
0

※pipefailが有効な場合、異常終了したコマンド`ls -1 *.sh`の終了ステータスが返却
ls: cannot access '*.sh': No such file or directory
2

コマンドリスト

; & && ||の演算子で区切ることで複数のコマンドを連続して実行することができる。

演算子説明
;順次実行。終了ステータスの値にかかわらず、順番に実行される。
&並列実行。コマンドはバックグラウンド実行され、終了ステータスは常に0が返る。
&&AND実行。コマンドの終了ステータスが0の場合、次のコマンドが実行される。

ANDおよびOR実行の終了ステータスは最後に実行したコマンドの終了ステータスとなる。

制御構文

分岐制御

if
if test-commands; then consequent-commands; [elif more-test-commands; then more-consequents;] [else alternate-consequents;] fi

test-commandsの終了ステータスが0の場合、consequent-commandsを実行する。
test-commandsの終了ステータスが0以外の場合、more-test-commandsを評価し終了ステータスが0の場合、more-consequentsを実行する。 more-test-commandsの終了ステータスが0以外の場合、alternate-consequentsを実行する。 終了ステータスは最後に実行されたコマンドの終了ステータス、すべての終了ステータスga0の場合は0となる。

case
case word in [ [(] pattern [| pattern]…) command-list ;;]… esac
select
select name [in words …]; do commands; done
((...))
(( expression ))
...
[[ expression ]]

ループ制御

until

構文は以下の通り。

until test-commands; do consequent-commands; done

test-commandsの終了ステータスが0以外の場合、consequent-commandsを実行する。
終了ステータスは最後に実行されたconsequent-commandsの終了ステータス、1度もconsequent-commandsを実行していない場合は0となる。

while

構文は以下の通り。

while test-commands; do consequent-commands; done

test-commandsの終了ステータスが0の場合、consequent-commandsを実行する。
終了ステータスは最後に実行されたconsequent-commandsの終了ステータス、1度もconsequent-commandsを実行していない場合は0となる。

for

構文は以下の通り。

for name [ [in [words …] ] ; ] do commands; done

wordsを展開し、展開結果をnameに1つずつ設定しながらcommandsを実行する。wordsが省略された場合、$@が指定されたものとして実行する。 終了ステータスは最後に実行されたcommandsの終了ステータス、wordsの展開結果が空で1度もcommandsを実行していない場合は0となる。

for (( expr1 ; expr2 ; expr3 )) ; do commands ; done

最初にexpr1が1度評価される。expr2の終了ステータスが0以外の場合、commandsを実行し、その後expr3が評価される。 終了ステータスは最後に実行されたcommandsの終了ステータス、またはいずれかの式が無効な場合はfalseとなる。

シェルコマンド

予約語

下記の単語はシェルで予約語とされている。
if then elif else fi time for in until while do done case esac coproc select function { } [[ ]] !

シェル関数

シェルパラメータ

シェル拡張

リダイレクト

コマンド実行

シェルスクリプト

組み込みコマンド

シェル変数

Bash機能

ジョブ制御

コマンドライン

履歴

性能を意識した書き方

基本的な考えとして

  • 外部コマンドの呼び出しは可能な限り避ける
  • ループの使用を控える

外部コマンドの起動は可能な限り避ける

Tip

開発時向け設定

冒頭に下記をsetする。

set -euxo pipefail
オプション説明
-eコマンドに失敗した時点でスクリプトをエラー終了する。
-u未初期化変数があるとエラー終了する。
-x実行コマンドを出力する。
-cリダイレクトでファイルを上書きしようとした場合にエラー終了する。>ではなく`>
-o pipefailパイプの途中でエラーが発生した場合、該当パイプのエラーコードで終了ステータスを返却する。

カンマ区切りの文字列を配列に代入

カンマをスペースに置換して代入。
指定方法:「変数名//置換前文字列/置換後文字列」。//はsedの/gと同様。すべて置換。

foo=a,b,c,d,e,f array=(${foo//,/ })

配列をカンマ区切りの文字列に置換

配列全体を標準出力し、スペースをカンマに置換。

bar=`echo ${array[@]} | sed 's/ /,/g'`

数値判定

exprを使用しないやり方。

num=100 if [[ ${num#-} != *[!0-9]* ]]; then # 数値の場合 fi

一時的に環境変数を変更してコマンドを実行

date # デフォルト(ja_JP.UTF-8)で出力 LANG=C date # 一時的にLANG=Cでdateコマンドを実行 date # LANGはCに変更されておらず、デフォルト(ja_JP.UTF-8)で出力
2022年 11月  5日 土曜日 23:42:55 JST
Sat Nov  5 23:44:26 JST 2022
2022年 11月  5日 土曜日 23:44:31 JST

関数への引数の渡し方(標準入力)

以下の方法で関数の引数に標準出力を渡すことが可能。

function foobar() { local val=$(cat -) echo ${val} } echo "hoge1" | foobar echo "hoge2" | foobar "-"
hoge1
hoge2

二重起動防止

flockコマンドを使用し、特定のファイルディスクリプタでファイルをオープンして実現する。 POSIXに準拠させる場合、ファイルディスクリプタは0~9の範囲しかないので注意が必要。

サブシェル

#!/bin/bash # ここは排他区間外 ( flock -w 0 -x 9 || exit 1 # 排他区間処理 ) 9>/tmp/lock # ここは排他区間外

非サブシェル

#!/bin/bash # ここは排他区間外 { flock -w 0 -x 9 || exit 1 # 排他区間処理 } 9>/tmp/lock # ここは排他区間外

デバッグ手法

一時停止

read -p "文言" を使用することで、特定の位置で処理を一時停止することが可能。

echo "foobar" read -p "Enterキーを押下してください" # Enterキーの入力待ち echo "hoge"