作为更大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 }'
Run Code Online (Sandbox Code Playgroud)
所有相同的结果,即使我确认awk
循环确实每两秒只运行一次
>>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<<
Run Code Online (Sandbox Code Playgroud)
也许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<<
Run Code Online (Sandbox Code Playgroud)
一切似乎都很好。缓存(如果是这样的话)被禁用,我从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<<
Run Code Online (Sandbox Code Playgroud)
我预计第 3 行要么导致对命令的新评估(提供新的日期值),要么重复第一行中的值。两者都不会发生。
这让我很难过。我不明白为什么我在第 2-5 行得到相同的值。改变f
来自1
于2
明确禁止这是怎么回事任何缓存。但是f
从2
back更改为1
并没有给我第一个的缓存副本f=1
,而是重复了f=2
. 将命令字符串更改为新值并f=3
触发对 的新调用date
。
为什么?
如果在执行 awk 程序期间多次将相同的文件名或相同的 shell 命令与 getline 一起使用(请参阅使用 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<<
Run Code Online (Sandbox Code Playgroud)
如果我们将date
此处的命令替换为记录运行时间的内容,我们还可以看到记录显示它只执行一次。
getline
也确实在 EOF 时返回零,错误时返回 -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
Run Code Online (Sandbox Code Playgroud)
您需要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<<
Run Code Online (Sandbox Code Playgroud)
使用"date; : " NR | getline x;
,所有命令行都是不同的,因此每个命令行都有一个单独的管道。
使用"date; : " $1 | getline x;
,当$1
重复时,您会遇到与第一种情况相同的问题,对同一管道的第二次读取会遇到 EOF。