作为较大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
循环实际上每两秒运行一次,结果也一样
>>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
。
为什么?
如果在执行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<<
如果我们在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
您需要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
重复时,您会遇到与第一种情况相同的问题,第二遍读到相同管道的结果将到达EOF。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句