Bashのスクリプトテクニック

Linux管理者は、Bashで定期的にスクリプトを作成する必要があります。 以下に、この作業を高速化する方法、およびスクリプトの信頼性を向上させる方法に関するヒントを示します。

ヒント1

何も尋ねずにアクションを実行するスクリプトを作成しないでください。 このようなスクリプトはほとんど必要ありません。 しかし、コピー、同期、何かを起動するのに十分な、あらゆる種類の「良い」ものです。 また、お気に入りのMidnight Commanderで間違ったスクリプトを突然クリックした場合、システムに何が起こるかわかりません。 それは交通ルールのようなものです-「血で書かれた」。

ヒント2

前のものに基づいて、各スクリプトの冒頭に次のようなものを配置すると便利です。
read -n 1 -p " ,     (y/[a]): " AMSURE [ "$AMSURE" = "y" ] || exit echo "" 1>&2
      
      



ちなみに、ここではechoコマンドが必要です。これは、<y>ボタンを押した後、改行がなくなるため、他の出力が同じ行に送られるためです。

ヒント3

これがすべての重要なヒントです。 毎回同じことを書かないようにするには、関数ライブラリを使用します。 Bashに関する多くの記事を読んだので、このトピックにはほとんど注意が払われていないことを認めざるを得ません。 おそらく証拠のため。 ただし、これを思い出す必要があると思います。 だから。

myfunc.shなどの独自の関数ライブラリを取得し、たとえば/ usr / binに配置します。 スクリプトを書くとき、それはあなたの仕事を減らすのを助けるだけでなく、あなたが何らかの機能を改善するならば、あなたは一挙に多くのスクリプトを修正することも可能にします。

たとえば、ヒント2に照らして、次のような関数を作成できます。
 myAskYN() { local AMSURE if [ -n "$1" ] ; then read -n 1 -p "$1 (y/[a]): " AMSURE else read -n 1 AMSURE fi echo "" 1>&2 if [ "$AMSURE" = "y" ] ; then return 0 else return 1 fi }
      
      



この関数のオプションのパラメーターは質問文字列のみです。 行が設定されていない場合、静かにクリックを待機します(この関数を呼び出す前に、スクリプトが必要なものをすべて出力している場合)。 したがって、次のようなアプリケーションが可能です。
 myAskYN " ,    ?" || exit
      
      



同様の関数myAskYNEをもう1つ書くことができます。この場合、最後に文字Eを付け、returnをexitに置き換えます。 その後、レコードはさらにシンプルになります。
 myAskYNE " ,    ?"
      
      



利点は明白です:a)コードの記述が少なく、b)コードが読みやすい、c)テストの接頭辞 "(y / [a]):"のように些細なことに気を取られない([a]は任意の意味であり、引用符は、これがデフォルトであることを示します)。

そして最後の1つです。 ライブラリの関数を使用するには、スクリプト自体に関数を含めることを忘れないでください。
 #!/bin/bash a1=myfunc.sh ; source "$a1" ; if [ $? -ne 0 ] ; then echo " —    $a1" 1>&2 ; exit 1 ; fi myAskYN " ,    ?" echo Run!
      
      



これは標準的なものであり、スクリプトロジックに直接適用されないため、意図的に呼び出しとエラー処理全体を1行に入れます。 なぜそれを半分の画面に拡大するのですか また、スクリプト名が変数に割り当てられていることに注意してください。 これにより、スクリプト名を1回設定できるため、必要に応じて、文字列を複製し、ライブラリ名を置き換えて関数の別のライブラリを接続できます。

現在、これらの3行で始まるスクリプトは、確認なしで何かを実行することはありません。 myAskYESNOと呼ばれる同様の関数myAskYNを書くのはあなたにお任せします。

ヒント4

成功を収め、最小限のコメントでいくつかの明らかな機能を実証します。
 sayWait() { local AMSURE [ -n "$1" ] && echo "$@" 1>&2 read -n 1 -p "(    )" AMSURE echo "" 1>&2 } cdAndCheck() { cd "$1" if ! [ "$(pwd)" = "$1" ] ; then echo "!!     $1 -  . ." 1>&2 exit 1 fi } checkDir() { if ! [ -d "$1" ] ; then if [ -z "$2" ] ; then echo "!!  $1 -  . ." 1>&2 else echo "$2" 1>&2 fi exit 1 fi } checkFile() { if ! [ -f "$1" ] ; then if [ -z "$2" ] ; then echo "!!  $1 -  . ." 1>&2 else echo "$2" 1>&2 fi exit 1 fi } checkParm() { if [ -z "$1" ] ; then echo "!!$2.  . ." 1>&2 exit 1 fi }
      
      



ここでは、エコー後に常に発生する1>と2の組み合わせに注目してください。 実際には、スクリプトにはおそらくいくつかの貴重な情報が表示されます。 また、この情報は常に画面に収まるとは限らないため、ファイルに保存したり、lessに送信したりすると便利です。 1>と2の組み合わせは、出力を標準エラーデバイスにリダイレクトすることを意味します。 そして、この方法でスクリプトを呼び出すと:
 my-script.sh > out.txt my-script.sh | less
      
      



不要なエラーメッセージやサービスメッセージは含まれませんが、実際に表示したいものだけが含まれます。

ヒント5

Bashは、関数から値を返すことについてあまりうまくいっていません。 ただし、独自のライブラリを使用すると、この問題は簡単に解決されます。 関数が値を入力する変数を入力し、関数を終了したら、この変数を分析します。 ところで、変数宣言は関数のライブラリの一番上に置くといいでしょう。 また、どこでも使用する他の変数を作成できます。 これが関数ライブラリの始まりです。
 curPath= #     ,    cRes= #        pYes= #  --yes,   
      
      



これで、コレクションに別の便利な関数を追加できます。
 input1() { local a1 if [ -n "$1" ] ; then read -p "$1" -sn 1 cRes else read -sn 1 cRes fi #    while [ "$2" = "${2#*$cRes}" ] ; do read -sn 1 cRes done echo $cRes 1>&2 }
      
      



以下にその使用例を示します。
 cat <<'EOF'   : ------------------------ a)  1 b)  2 .)  EOF input1 " : " "ab." echo " : $cRes"
      
      



この関数は、キーストロークを指定されたもの(たとえば、a、b、およびピリオド)のリストに制限します。 他のキーは認識されず、押されても何も出力されません。 この例では、戻り変数($ cRes)の使用も示しています。 ユーザーが押した文字を返します。

ヒント6

パラメーターなしのスクリプト それらの処理については多くの文献が書かれています。 私のビジョンを共有します。

  1. パラメータがシーケンスに関係なく処理されることが非常に望ましいです。
  2. スクリプトが多く文字が少ないという単純な理由で、1文字のパラメーター(したがってgetopts)を使用するのは好きではありません。 また、あるスクリプトでは-rは置換を意味し、別のスクリプトでは複製を意味し、3番目のスクリプトでは削除がほぼ不可能であることを忘れないでください。 したがって、2つの表記法を同時に使用します。a)--show-files-only、b)-sfo(前の表記法の略語として)。 実践では、このようなキーは即座に非常に長い間記憶されることが示されています。
  3. スクリプトは、未知のキーに対してエラーをスローする必要があります。 これは、パラメーターを書き込む際のエラーの特定に部分的に役立ちます。
  4. ヒント2から、ルールを採用します。確認なしでスクリプトを実行しないでください。 ただし、これに重要な例外を追加します---yesキーが指定されていない場合(もちろん、キーはどれでもかまいません)。
  5. キーの後に値が続く場合があります。 この場合、次のルールが長いキーに適用されます:--source-file = my.txt(同等のスペル)、および短いキー:-sf my.txt(スペース内)。
この観点から、パラメーター処理は次のようになります。
 while [ 1 ] ; do if [ "$1" = "--yes" ] ; then pYes=1 elif [ "${1#--source-file=}" != "$1" ] ; then pSourceFile="${1#--source-file=}" elif [ "$1" = "-sf" ] ; then shift ; pSourceFile="$1" elif [ "${1#--dest-file=}" != "$1" ] ; then pDestFile="${1#--dest-file=}" elif [ "$1" = "-df" ] ; then shift ; pDestFile="$1" elif [ -z "$1" ] ; then break #   else echo ":  " 1>&2 exit 1 fi shift done checkParm "$pSourceFile" "   " checkParm "$pDestFile" "   " if [ "$pYes" != "1" ] ; then myAskYNE " ,    ?" fi echo "source=$pSourceFile, destination=$pDestFile"
      
      



このコードは次の機能を提供します。 これは基本部分であり、さらに発展させることができます。 たとえば、ライブラリにいくつかのパラメータ処理関数を追加します。
 procParmS() { [ -z "$2" ] && return 1 if [ "$1" = "$2" ] ; then cRes="$3" return 0 fi return 1 } procParmL() { [ -z "$1" ] && return 1 if [ "${2#$1=}" != "$2" ] ; then cRes="${2#$1=}" return 0 fi return 1 }
      
      



この場合、パラメーター処理サイクルははるかに消化しやすくなります。
 while [ 1 ] ; do if [ "$1" = "--yes" ] ; then pYes=1 elif procParmS "-sf" "$1" "$2" ; then pSourceFile="$cRes" ; shift elif procParmL "--source-file" "$1" ; then pSourceFile="$cRes" elif procParmS "-df" "$1" "$2" ; then pDestFile="$cRes" ; shift elif procParmL "--dest-file" "$1" ; then pDestFile="$cRes" elif [ -z "$1" ] ; then break #   else echo ":  " 1>&2 exit 1 fi shift done
      
      



実際、このサイクルは、キーの名前とこのキーの変数名を除いて何も考えずに、スクリプトからスクリプトにコピーできます。 さらに、この場合、それらは繰り返されず、エラーの可能性は除外されます。

完璧に制限はありません。たとえば、procParmSで3番目のパラメーターの空でない値を確認し、この場合は誤って抜け出すなど、関数を長時間「改善」できます。 などなど。

この例の関数ライブラリファイルは、 ここからダウンロードできます

ここでファイルをテストします。



All Articles