このスクリプトでは sed コマンドが何をしているのか理解できません。

このスクリプトでは sed コマンドが何をしているのか理解できません。

他のスクリプトから派生した機能を持つスクリプトがあります。 1行ずつ試みましたが、sed正規表現が複雑すぎます。

#!/usr/bin/env bash

# This function will update the value associated with a key,
# remove a comment from the beginning of the line,
# or append the key value pair to the end of the file if the key is not found

# To use this function in a script
# source this script:
#   . lineinfile
#
# To invoke the function:
#   lineinfile "key=value" "filename"
# OR
#   lineinfile "key value" "filename"


lineinfile() {
  [ -s $2 ] || echo "${1}" >> ${2}
  if [[ "$1" == *"="* ]]; then
    sed -i -e "/^#\?.*\(${1%%=*}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
  elif [[ "$1" == *" "* ]]; then
    sed -i -e "/^#\?.*\(${1%% *}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
  elif [[ "$1" == *$'\t\t'* ]]; then
    sed -i -e "/^#\?.*\(${1%%$'\t\t'*}\).*/{s@@${1}@;:a;n;ba;q}" -e "\$a${1}" ${2}
  fi
}

関数の最初の行[ -s $2 ] || echo "${1}" >> ${2}- 2番目の位置引数がサイズがゼロ以外の既存のファイルであることを確認し、内容を$1ファイルの末尾に追加します。ここでは$2なぜ使用されますか?||

if-elifブロックが何をテストしているのかわかりません。 if条件でと一致させることは何ですか*"="* *" "**$'\t\t'*また、sedコマンドが何をしているのかわかりません。正規表現は複雑です。誰でも私のためにsedコマンドを分析できますか?

答え1

  1. || 前のコマンドにゼロ以外の終了コード(false / error)がある場合は、次のコマンドが実行されます。

    [ -s $2 ] || echo "${1}" >> ${2}
    

    以下と同じ:

    if ! [ -s $2 ] ; then echo "${1}" >> ${2} ; fi
    

    ファイルが存在しないか空の場合、それらの1つは$1最初の引数()をファイル()に追加します。$2ただし、引用については、以下の次の項目2を参照してください。

    ところで、関数はこの時点で返すことができ、返す必要があります。sedこれで必要な値が含まれているので、ファイルを実行する必要はありません。たとえば(printf代わりに使用echo- 参照なぜprintfがechoより優れているのですか?):

    [ -s "$2" ] || printf '%s\n' "$1" >> "$2" && return
    

    またはより良い:

    if ! [ -s "$2" ] ; then printf '%s\n' "$1" >> "$2" ; return ; fi
    

    最初の形式では、前のreturnコマンド(printf)が成功した場合にのみ実行されます。printf2 番目の形式は、成功または失敗に関係なく常に返されます。return依存関係がprintf後続の項目に依存する理由はありません(この省略式のif / then / fi構造でコマンドを「接続」する一般的な慣用語にすぎません)。ほとんどの場合、printf成功しますが、時には(たとえば、権限やディスクがいっぱいになった場合など)失敗します。失敗した場合、関数はしなければならないどのように戻ってsedもスクリプトも失敗するので、実行する必要はありません。ただしreturn、引数を使用しないと、実行する最後のコマンドの終了コードが返されるため、呼び出し側は成功または失敗を検出できます。

  2. 著者は、シェルで引用符の目的や動作方法、中括弧(例えば)が何であるかを理解していないようです${var}いいえ引用の代替案については、以下を"$var"参照してください。$VAR対${VAR}と引用そしてスペースやその他の特殊文字が原因でシェルスクリプトが停止するのはなぜですか?

    著者は$2必要に応じて決して引用しません(ファイル名には、スペース、タブ、改行、引用なしで使用すると、シェルスクリプトを破損する可能性があるシェルメタ文字を含めることができます)。

  3. 3つのif / elifテストは、最初の引数($1)に=シンボル、スペース、または2つのタブが含まれていることを確認します。見つかったバージョンに応じて、わずかに異なる3つのバージョンのsedスクリプトのいずれかを実行します。

    sedスクリプトは、=「キー」バリアント、スペース、または2つのタブがあるかどうかを確認し、オプションでを使用します#。一致するものがある場合は、それを値に置き換えて$1ループを実行します。残りの出力ファイルを読みます。私の考えでは、ここの目的は最初の項目だけを交換することですkey=value

    一致するものがない場合(したがってループとシャットダウンは行われません)、ファイルの$1末尾に追加されます。

  4. 著者はこれについて考えすぎたようです(または十分ではないかもしれません)。$1最初にキーと値の変数に分割した場合は、1つのsedスクリプトでこれを実行できます。つまり、最初にデータを抽出し、それを$1一貫した形式で「正規化」してから、sedそれをスクリプトで使用します。

    あるいは、単にPerlで完全に書き直してください。これは、実行する作業にシェルとsedの利点が必要な場合(そしてほとんどのsedバージョンよりも優れており、強力な正規表現エンジンがある場合)に最適です。if/elif/elif/fiたとえば、関数の一部を次のように置き換えます。

     perl -0777 -i -pe '
          BEGIN { $r = shift; ($key,$val) = split /(=| |\t\t)/, $r };
          s/\z/$r\n/ unless (s/^#?.*\b$key\b.*$/$r/m)' key=value filename
    

    このPerlバージョンは、3つのバリエーション(=、スペース、2つのタブ - 最後の2つは引用が必要です)の両方で動作します。ファイル全体を一度に飲み込み(-0777オプション)、複数行の検索と置換操作(/m正規表現修飾子)を実行しようとします。操作が失敗すると、最初の引数(改行文字を含む)がファイルの末尾に追加されます(\z)。また、ソースで、 foo=123およびを区別できないバグを修正しますfoobar=123\bこの目的のために、単語境界表示が使用される。 sedでは、キーパターンを使用し\<て囲むことができます。\>

    ところで、そのX unless Y構成は単なるものですif not Y, then do X。で書いても、if (! s/^#?.*$key.*$/$r/m) {s/\z/$r\n/}同じことができます。

  5. 関数名lineinfileは非常に一般的ですが、行うことは非常に具体的です。さらに悪いことは、名前が一致しないか、関数が実際に使用される用途を暗示していないことです。これは一般的に悪い習慣と見なされます。

関連情報