この記事では、それらを改訂して補足しました。
安全性
次の行で各スクリプトを開始します
#!/bin/bash set -o nounset set -o errexit
これにより、よくある2つの間違いを防ぎます。
1)宣言されていない変数を使用する試み
2)コマンドの異常終了を無視する
コマンドが失敗する可能性があり、これが適切な場合は、次のコードを使用できます。
if ! <possible failing command> ; then echo "failure ignored" fi
一部のコマンドは、「mkdir -p」や「rm -f」などのクラッシュコードを返さないことに注意してください。
また、スクリプトからサブプログラムのチェーン(command1 | command 2 | command3)を呼び出すのは困難です。この制限を回避するには、次の構成を使用できます。
(./failing_command && echo A)
この場合、演算子「&&」は次のコマンドの実行を許可しません。詳細については、「http://fvue.nl/wiki/Bash:_Error_handling」を参照してください
機能
Bashでは、通常のコマンドのような関数を使用できます。これにより、コードの可読性が大幅に向上します。
例1:
ExtractBashComments() { egrep "^#" } cat myscript.sh | ExtractBashComments | wc
comments=$(ExtractBashComments < myscript.sh)
例2:
SumLines() { # iterating over stdin - similar to awk local sum=0 local line=”” while read line ; do sum=$((${sum} + ${line})) done echo ${sum} } SumLines < data_one_number_per_line.txt
例3:
log() { # classic logger local prefix="[$(date +%Y/%m/%d\ %H:%M:%S)]: " echo "${prefix} $@" >&2 } log "INFO" "a message"
すべてのコードを関数に転送して、グローバル変数/定数のみを残し、すべての高レベルのロジックが存在する関数「main」を呼び出してみてください。
変数宣言
Bashでは、最も重要ないくつかのタイプの変数を宣言できます。
local (関数内でのみ使用される変数の場合)
読み取り専用 (エラーの原因となる再割り当てを試みる変数)
## DEFAULT_VAL , , '-7' readonly DEFAULT_VAL=${DEFAULT_VAL:-7} myfunc() { # local some_var=${DEFAULT_VAL} ... }
既に宣言されている変数から読み取り専用の変数を作成することができます。
x=5 x=6 readonly x x=7 # failure
すべての変数がローカルまたは読み取り専用であることを確認してください。これにより、可読性が向上し、エラーの数が減少します。
バックティックの代わりに$()を使用します ``
バッククォートは読みにくく、一部のフォントではシングルクォートと簡単に混同される可能性があります。
また、$()構文を使用すると、エスケープの頭痛のないネストされた呼び出しを使用できます。
# : ABCD echo "A-`echo B-\`echo C-\\\`echo D\\\`\``" echo "A-$(echo B-$(echo C-$(echo D)))"
単一角かっこ[]の代わりに二重角かっこ[[]]を使用する
二重角括弧は、変数の代わりに意図しないパスの使用を防ぎます:
$ [ a < b ] -bash: b: No such file or directory $ [[ a < b ]]
場合によっては、構文が単純化されます。
[ "${name}" \> "a" -o ${name} \< "m" ] [[ "${name}" > "a" && "${name}" < "m" ]]
また、追加機能も提供します。
新しい演算子:
- || 論理OR-二重括弧のみ。
- &&論理AND-二重括弧のみ。
- <文字列変数の比較(文字列比較)-二重括弧ではシールドは不要です。
- ==置換を使用した文字列変数の比較(グロビングとの文字列照合)-二重括弧のみを使用。
- =〜正規表現を使用した文字列変数の比較(正規表現と一致する文字列)-二重括弧でのみ。
補足/変更された演算子:
- -lt数値比較
- -n空でない文字列変数
- -z文字列変数が空です(文字列が空です)
- -eq数値等式
- -neデジタル不等式
例:
t="abc123" [[ "$t" == abc* ]] # true (globbing) [[ "$t" == "abc*" ]] # false (literal matching) [[ "$t" =~ [abc]+[123]+ ]] # true (regular expression) [[ "$t" =~ "abc*" ]] # false (literal matching)
bash 3.2以降では、正規表現またはワイルドカード表現を引用符で囲まないでください。式にスペースが含まれている場合は、変数に入れることができます。
r="a b+" [[ "a bbb" =~ $r ]] # true
文字列変数と置換の比較は、caseステートメントでも利用できます。
case $t in abc*) <action> ;; esac
文字列変数を操作します。
Bashには、文字列変数を操作するためのいくつかの(過小評価された)オプションがあります。
基本:
f="path1/path2/file.ext" len="${#f}" # = 20 ( ) # : ${<>:<_>} ${<>:<_>:<_>} slice1="${f:6}" # = "path2/file.ext" slice2="${f:6:5}" # = "path2" slice3="${f: -8}" # = "file.ext" ( '-') pos=6 len=5 slice4="${f:${pos}:${len}}" # = "path2"
置換置換:
f="path1/path2/file.ext" single_subst="${f/path?/x}" # = "x/path2/file.ext" ( ) global_subst="${f//path?/x}" # = "x/x/file.ext" ( )
変数の分離:
f="path1/path2/file.ext" readonly DIR_SEP="/" array=(${f//${DIR_SEP}/ }) second_dir="${array[1]}" # = path2
置換の削除:
#行の先頭から最初の一致まで削除
f="path1/path2/file.ext" extension="${f#*.}" # = "ext"
#行の先頭から最後の一致まで削除
f="path1/path2/file.ext" filename="${f##*/}" # = "file.ext"
#行末から最初の一致まで削除
f="path1/path2/file.ext" dirname="${f%/*}" # = "path1/path2"
#行末から最後の一致まで削除
f="path1/path2/file.ext" root="${f%%/*}" # = "path1"
一時ファイルを取り除く
一部のコマンドはファイル名が入力されることを期待し、演算子「<()」がそれらを支援します。入力するコマンドを取り、ファイル名として使用できるものに変換します。
#2つのURLをダウンロードしてdiffに渡す
diff <(wget -O - url1) <(wget -O - url2)
トークンを使用して複数行の変数を渡す:
#MARKER-任意の単語。
command << MARKER ... ${var} $(cmd) ... MARKER
置換を回避したい場合は、マーカーを引用符で囲むことができます。
#コンストラクトは変数の値ではなく '$ var'を返します
var="text" cat << 'MARKER' ... $var ... MARKER
組み込み変数
- $ 0スクリプトの名前
- $ 1 $ 2 ... $ nスクリプト/関数に渡されるパラメーター(スクリプト/関数の位置パラメーター)
- スクリプトの$$ PID(スクリプトのPID)
- $! 最後に実行されたコマンドのPID(およびバックグラウンドで実行)
- $? 最後のコマンドによって返されたステータス(最後のコマンドの終了ステータス(パイプラインコマンドの場合は$ {PIPESTATUS}))
- $#スクリプト/関数に渡されるパラメーターの数(スクリプト/関数に渡されるパラメーターの数)
- $ @単語として表されるスクリプト/関数に渡されるすべてのパラメーター(引数を個別の単語として表示)
- $ *スクリプト/関数に渡されるすべてのパラメーター。1つの単語として表示されます(引数を1つの単語として表示します)
- 原則として:
- $ *めったに役に立たない
- $ @空のパラメーターおよびスペースを含むパラメーターを正しく処理します
- $ @使用される場合、通常は二重引用符で囲まれます-"$ @"
例:
for i in "$@"; do echo '$@ param:' $i; done for i in "$*"; do echo '$! param:' $i; done
結論:
bash ./parameters.sh arg1 arg2 $@ param: arg1 $@ param: arg2 $! param: arg1 arg2
デバッグ
構文チェック(スクリプトの実行時間が15秒より長い場合は時間を節約します):
bash -n myscript.sh
トレース:
bash -v myscripts.sh
複雑なコマンドの開示によるトレース:
bash -x myscript.sh
-vおよび-xパラメーターはコードで設定できます。これは、スクリプトが1つのマシンで実行され、ロギングが別のマシンで行われる場合に役立ちます。
set -o verbose set -o xtrace
シェルスクリプトを使用してはならないという兆候:
- スクリプトには数百行以上が含まれています。
- 通常の配列よりも複雑なデータ構造が必要です。
- あなたは引用とエスケープでわいせつをするのにうんざりしています。
- 多くの文字列変数を処理/修正する必要があります。
- サードパーティのプログラムを呼び出す必要はなく、パイプも必要ありません。
- スピード/パフォーマンスはあなたにとって重要です。
プロジェクトがこのリストの項目と一致する場合は、PythonまたはRubyの言語を検討してください。
参照:
高度なBashスクリプトガイド: tldp.org/LDP/abs/html
Bashリファレンスマニュアル: www.gnu.org/software/bash/manual/bashref.html
元の記事: robertmuth.blogspot.ru/2012/08/better-bash-scripting-in-15-minutes.html