興味深いBashプログラミング手法

これらのトリックは、Googleの内部プロジェクト「トイレでのテスト」で説明されています(トイレでのテスト-トイレにリーフレットを配布して、開発者にテストを思い出させます)。

この記事では、それらを改訂して補足しました。





安全性


次の行で各スクリプトを開始します

#!/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" ]]
      
      





また、追加機能も提供します。



新しい演算子:





補足/変更された演算子:





例:

 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
      
      







組み込み変数




例:

 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



All Articles