我对以下 (G)AWK 脚本片段有疑问:
do {
...
} while (system("sleep 10"))
我的目的是当用户在睡眠期间按 ^C 时打破循环,但它不起作用。
我相信问题在于,当用 ^C 中断时,Bash 会以 0 退出,至少在由 AWK 执行时是这样system()
:
$ awk 'BEGIN { print "\n" system("sleep 2") }'
(let the sleep complete)
0
$ awk 'BEGIN { print "\n" system("sleep 2") }'
^C
0
为什么会这样呢?
这是 Bash 或 (G)AWK 中的错误吗?
是否有一个简单的解决方案,不涉及复杂的 Bash 特定语法,例如trap
?
我能想到的最好的办法是:
do {
...
} while (42 == system("sleep 10 && exit 42"))
对我来说,这仍然像是一个拼凑的东西。
答案1
awksystem()
应该返回什么指定不明确。
在实现中似乎常见的awk
是,在正常退出时,它返回退出代码(传递给模 256 的数字exit(3)
),但是当 shell 进程被信号杀死时,会有很多不同的行为。
另请注意,虽然 C 函数system(3)
旨在忽略父级中的 SIGINT (和 SIGQUIT),但(至少对我而言)不太清楚该要求是否也适用于awk
s system()
。一些awk
实现(例如mawk
)将在该 SIGINT 时终止(这也是我希望看到的行为,因为我不喜欢仅仅因为awk
碰巧正在运行该system()
函数而忽略我的 CTRL-C),一些(例如gawk
或传统的实现)惯于。
另请注意,某些 shell 可以拦截其中一些信号并最终调用exit()
,这会影响行为(例如,请参阅有关 Bourne shell 的注释中的讨论),这就是我exec
在下面的示例中使用从循环中删除 shell 的原因。
对于SIGINT 上返回的值(如果考虑1 的system()
话,还会有更多变化),我们看到:close()
$ nawk 'BEGIN {print system("exec kill -s INT $$")}'
0.0078125
$ bwk-awk 'BEGIN {print system("exec kill -s INT $$")}'
0.0078125
$ mawk 'BEGIN {print system("exec kill -s INT $$")}'
130
$ gawk 'BEGIN {print system("exec kill -s INT $$")}'
0
0.0078125
2 / 256
(对于of SEGV
,11
如果核心被转储,则为 0.542969 ((128+11)/256),否则为 0.0429688 (11/256)),在 Solaris 10 或 11 或其 Linux 端口上nawk
找到nawk
传家宝工具箱,bwk-awk
是awk
由 Brian Kernighan 本人维护(K
中awk
)在某些 BSD 上找到的基础awk
(此处在 Debian GNU/Linux 上进行了测试)。/usr/xpg4/bin/awk
在 Solaris 11 上的行为类似于gawk
.
因此,根据s
返回的值system(3)
(一个整数,其中位 0 到 6 是信号编号,位 7 是核心位,位 8 到 15 是退出代码),上面awk
的system()
返回:
s / 256
(传统awk
实现),int(s/256)
(gawk
),- 或者在 中
mawk
,与 Bourne 或 C-shell 等 shell 所做的转换相同((s&127)+128
如果被杀死,s>>8
否则),除了如果核心被转储,您将得到(s&127)+256
而不是(s&127)+128
(值为(s&255)+128
)。
所以,在这里,你可以这样做:
awk 'BEGIN{print system("trap exit\\ 1 INT; sleep 10")}'
但是它仍然会导致某些实现awk
被终止,例如。如果你的是或,你可以这样做:awk
mawk
sh
bash
yash
awk 'BEGIN{print system("set -m; sleep 10; exit")}'
所以sleep
在它自己的进程组中运行(并且只有它获得 SIGINT)。
另一种选择是在调用之前忽略 SIGINT awk
。但是,如果信号在启动时已被忽略,则大多数 shell(这是 POSIX 要求)无法更改信号处理程序。所以像这样的事情:
(
trap '' INT
awk 'BEGIN{print system("trap exit\\ 1 INT; sleep 10; exit")}'
)
行不通。zsh
但没有这种(自我造成的)限制,所以如果你知道zsh
可用,你可以这样做:
(
trap '' INT
awk 'BEGIN{print system("exec zsh -c \"TRAPINT() exit 1; sleep 10\"")}'
)
awk
无论是mawk
或other都可以工作gawk
,并且可以避免扰乱工作控制。但此时,值得考虑使用perl
// python
...ruby
而不是awk
可以根据需要调整信号处理的位置。
笔记
1close()
管道之上,如:
awk 'BEGIN {cmd = "kill -s INT $$"; cmd | getline; print close(cmd)}'
首先,这次在我尝试过的所有实现中^C
都会中断awk
(没有像 for 那样忽略popen(3)
/的 SIGINT/SIGQUIT pclose(3)
(实现该方法的自然方法getline
)的要求system(3)
)。
但是当涉及到退出状态时(如上面的/s
返回的值在哪里),我们看到:pclose(3)
waitpid(2)
system()
- Solaris
nawk
:不起作用,您不能close()
在 Solaris 中那样调用nawk
。 /usr/xpg4/bin/awk
在 Solaris 上。即使exit(1)
进程已完成,也始终返回 0。显然是一个一致性错误。gawk
和bwk-awk
: 给出s
(exit 1
给出 256,被 SIGINT 杀死给出 2,被 SIGSEGV 11 杀死,核心给出 139)。mawk
:与 相同system()
,看起来mawk
是唯一考虑到这一点的实现。