ループ内の変数にgrepを使用する

ループ内の変数にgrepを使用する

grepの結果をループ内の変数として保存するのに問題があります。

while read file;do
  Server=$(echo $file | awk '{ print $1 }')
  FDate=$(echo $file | awk '{ print $2 }')
  ST=$(cat foobar | grep $Server | awk '{ print $3 }')
  #ST=$(grep $Server foobar | awk '{ print $3 }')

  echo "Server = $Server"
  echo "FDate = $FDate"
  echo "ST = $ST"

done < inputfile

最初のST varは、反復ごとに「Usage:grep [Option] ... Pattern [File]」出力を提供します。これは、コマンドが正しく読み取られていないことを意味します。

コメントアウトされた2番目のST変数は実際にスクリプト全体を中断し、エコーを試みると他のすべての変数が空になります。

これで、コマンドラインで同じことをしようとするとうまくいきます。

$ testme=$(cat foobar | grep Big | awk '{ print $3}'
$ echo "$testme"
tada

だから私の質問は、このgrepコマンドを変数に保存する方法です。パターンマッチングには可能な結果が1つしかないため、複数のマッチングについて心配する必要はありません。ただし、ループ内の各サーバーは、列3(tada、tada1、tada2)に異なる文字列を持つことができます。

編集する:

入力ファイルには、複数の列を含むサーバーのリストがあります。現在行の列1にリストされているサーバーをインポートし、foobarファイルから一致するものを検索し、列3から文字列を取得します。

「使用」メッセージが表示されるにもかかわらず、スクリプトが実際に機能していることがわかりました。入力ファイルの一部のサーバーエントリがfoobarファイルにまだ表示されていないため、grepに一致はありませんが、まだawkにパイプしようとする可能性があります。よくわかりません。

しかし、私はまだ「使用」メッセージを削除したいと思います。私の考えでは「set -opipelinefail」がうまくいくかもしれませんが、そうしない方が良いです。

答え1

while read file;do                                       # 1
  Server=$(echo $file | awk '{ print $1 }')              # 2
  FDate=$(echo $file | awk '{ print $2 }')               # 3
  ST=$(cat foobar | grep $Server | awk '{ print $3 }')   # 4
  #ST=$(grep $Server foobar | awk '{ print $3 }')        # 5

grep少なくとも検索パターンが必要です(またはそれに対応するオプションを提供するかオプション-e)。したがって、空の場合は、4行と5行の引用符なしの内容-f$Server$Server噴射(あなたも見ることができます いつ二重引用符が必要ですか?)、そして

  • 行4grepにはパラメータはありません。必須パラメーターがない場合は、使用法の説明が印刷されます。
  • 行5では、grep単一のパラメータを取得してfoobarパターンにします。基本的には標準入力から読み込み、ループ内にはループと同じ標準入力があるので、そこからすべてを読みます。

さて、ループ全体を見ると、次の質問が考えられます。シェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?少なくともある程度単純化することができます。read入力はフィールド自体に分割できるため、コマンド置換を削除できます。

これにより、おそらく値のいずれかまたは両方が空の場合に対処する必要があります。そして、awkも作業を実行できるので、grep次のようにしましょう。

while read server fdate; do
    if [ -z "$server" ] || [ -z "$fdate" ]; do
        continue
    fi
    ST=$(awk < foobar -v server="$server" '$0 ~ server { print $3 }')

    echo "server $server fdate $fdate ST $ST"
done < inputfile

(または最終的に何をしたいかに応じて、全体をawkプログラムに置き換えます。)

答え2

シェルwhileループgrep(またはawkどのこれはシェルループのコマンドと似ています。バラよりシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?理由があります。簡単に言えば、シェルは他のプログラムにタスクを実行させること、リダイレクトとパイプを設定し、実際にタスクを実行している他のプログラムにファイル名とデータを提供するのに非常に熟練していますが、タスク自体を実行するのには不便です。シェルは遅く、変数を二重引用符で囲まないなどのユーザーエラーが発生しやすい(たとえば、$ Serverを引用できませんでした)。これが"$Server"問題の直接の原因ですgrep。とにかく、他の理由の1つは失敗したことです。 $ Serverに実際にawkの後に値が含まれていることを確認してください。

とにかく、短いスクリプトで必要なすべての操作を実行できますawk。たとえば、

awk 'NR==FNR { fdates[$1] = $2 ; next}; # read first file into fdates array

     $1 in fdates {  # process second file
       printf "Server = %s\nFDate = %s\nST = %s\n", $1, fdates[$1], $3;
     }' inputfile foobar

英語で:

  • 読む最初fdatesファイルを作成し、キーフィールド1(サーバー)と値フィールド2(FDate)という名前の連想配列に保存します。

  • 2番目のファイル(およびそれ以降のファイル)を読み取るときにサーバー名がfdatesのキーである場合は、必要な詳細を印刷します。

メモ:サーバー名を検索するファイルのフィールドが指定されていませんfoobar。上記のスクリプトはフィールド1にあると想定していますfoobar。別のフィールドにいる場合は、両方の行を変更します$1$1 in fdatesそしてラインprintf)に適応します。

サーバー名が次のような場合どこかに1行にfoobar(つまり、一致する固定フィールド番号がない)、次のようにスクリプトを作成できます。

awk 'NR==FNR { fdates[$1] = $2 ; next};
     { for (server in fdates) {
       if ($0 ~ server) {
         printf "Server = %s\nFDate = %s\nST = %s\n", server, fdates[server], $3;
       }
     }' inputfile foobar

英語で:

  • 読む最初ファイルを保存するには、キーがフィールド1(サーバー)で値がフィールド2(FDate)の連想配列に保存されます。fdatesつまり、最初のバージョンと同じです。

  • 次に、ファイルの後続の行ごとに配列fdatesの各要素を繰り返します。行の任意の場所にキー(サーバー名)と一致する正規表現がある場合は、必要な詳細が印刷されます。

fdates2番目のバージョンは、含まれているすべてのサーバー名に対して正規表現一致を実行する必要があるため、最初のバージョンより少し遅くなります。ワイヤーfoobar

どちらのバージョンもwhile readループよりはるかに高速で、awk1に対して約3回の呼び出しを実行し、catループを介して毎回ファイル全体を読み取りますgrep。上記の各awkスクリプトは一度だけ呼び出され、一度だけfoobar読む必要があります。inputfilefoobar

関連情報