大きなスクリプトの一部として、ランダムな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
変更すると、進行中のキャッシュが顕著に無効になります。ただし、後ろからに変更すると、最初のエントリのキャッシュされたコピーは提供されず、値のコピーが提供されます。コマンド文字列を新しい値に変更し、への新しい呼び出しをトリガします。1
2
f
2
1
f=1
f=2
f=3
date
なぜ?
ベストアンサー1
getline
awkプログラムの実行中に同じファイル名または同じシェルコマンドが複数回使用される場合(参照)明示的な入力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
示す履歴も表示できます。
getline
EOF では 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が発生します。