パイプ「cmd」のawk getline | REVOLVE '"cmd" getline var'パイプのawk getlineはその値をキャッシュします。

パイプ「cmd」のawk getline | REVOLVE '

大きなスクリプトの一部として、ランダムなawk日付文字列をエポック以降の秒に変換する必要があります。これはawk関数として機能しないので、date各入力ラインに対して呼び出すことができると思いました。 (振り返ってみるとそれを使うこともできたがperl、その考えは捨てよう。)

予期しない結果を見た後、問題をこの問題(bashおよびGNU awk)に単純化しました。

for f in {1..5}; do echo $f; sleep 2; done | awk '{ "date" | getline x; printf ">>%s<<\n", x }'

awkループが実際に2秒に1回だけ実行されることを確認しましたが、結果はすべて同じです。

>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<
>>29 Jun 2020 10:38:24<<

キャッシュも可能ですgetline。だから私はこれを試しました

for f in {1..5}; do echo $f; sleep 2; done | awk '{ "date; : " NR | getline x; printf ">>NR=%d - %s<<\n", NR, x }'

>>NR=1 - 29 Jun 2020 10:44:05<<
>>NR=2 - 29 Jun 2020 10:44:07<<
>>NR=3 - 29 Jun 2020 10:44:09<<
>>NR=4 - 29 Jun 2020 10:44:11<<
>>NR=5 - 29 Jun 2020 10:44:13<<

すべてがよさそうです。キャッシュ(この場合)は無効になりますdate

その後、再びこのパスに沿ってパイプされたコマンドに繰り返し値を指定しました。getline

for f in 1 2 1 1 2 3; do echo $f; sleep 2; done | awk '{ "date; : " $1 | getline x; printf ">>NR=%d - f=%d - %s<<\n", NR, $1, x }'

>>NR=1 - f=1 - 29 Jun 2020 10:43:01<<
>>NR=2 - f=2 - 29 Jun 2020 10:43:03<<
>>NR=3 - f=1 - 29 Jun 2020 10:43:03<<
>>NR=4 - f=1 - 29 Jun 2020 10:43:03<<
>>NR=5 - f=2 - 29 Jun 2020 10:43:03<<
>>NR=6 - f=3 - 29 Jun 2020 10:43:11<<

私は3行目でコマンドが新しく評価されるか(新しい日付値を提供する)、最初の行の値が繰り返されると予想しました。どちらも起こりませんでした。

これは私をパニックにさせました。なぜ行2-5で同じ値が得られるのか分かりません。からにf変更すると、進行中のキャッシュが顕著に無効になります。ただし、後ろからに変更すると、最初のエントリのキャッシュされたコピーは提供されず、値のコピーが提供されます。コマンド文字列を新しい値に変更し、への新しい呼び出しをトリガします。12f21f=1f=2f=3date

なぜ?

ベストアンサー1

GNU awk マニュアルには次のものがあります。それ:

getlineawkプログラムの実行中に同じファイル名または同じシェルコマンドが複数回使用される場合(参照)明示的な入力getline)は、ファイルを最初に開くとき(またはコマンドを実行したとき)にのみ適用されます。この時点で、ファイルまたはコマンドから最初の入力レコードを読み込みます。次に、同じファイルまたはコマンドが使用されると、そのファイルまたはコマンドgetlineから別のレコードを読み取ることができます。

したがって、コマンドを一度だけ実行し、追加の読み取りからEOFを取得するため、以前の値はx変更されません。x読むたびに捨てるとどうなるか比較してみてください。

$ for f in {1..3}; do echo $f; sleep 2; done |
   awk '{ "date" | getline x; printf ">>%s<<\n", x; x ="done" }'
>>Mon Jun 29 13:37:53 EEST 2020<<
>>done<<
>>done<<

ここのコマンドを、コマンドが実行された時点の履歴を保存するコマンドに置き換えると、そのコマンドが一度だけ実行されたことをdate示す履歴も表示できます。

getlineEOF では 0 を返し、エラーでは -1 を返すので、次のことを確認できます。

$ for f in {1..3}; do echo $f; sleep 2; done |
    awk '{ if (("date" | getline x) > 0) printf ">>%s<<\n", x;
           else printf "error or eof\n"; }'
>>Mon Jun 29 13:46:58 EEST 2020<<
error or eof
error or eof

close()次に再び開くようにawkに指示するには、明示的にパイプを使用する必要があります。

$ for f in {1..3}; do echo $f; sleep 2; done |
   awk '{ "date" | getline x; printf ">>%s<<\n", x; x = "done"; close("date") }'
>>Mon Jun 29 13:39:19 EEST 2020<<
>>Mon Jun 29 13:39:21 EEST 2020<<
>>Mon Jun 29 13:39:23 EEST 2020<<

を使用すると、"date; : " NR | getline x;すべてのコマンドラインが異なるため、それぞれに別々のパイプラインがあります。

"date; : " $1 | getline x;を使用すると、$1反復時に最初の場合と同じ問題が発生し、同じパイプに対する2番目の読み取りでEOFが発生します。

おすすめ記事