当 AWK 调用并用 ^C 中断时,使 bash 退出 != 0

当 AWK 调用并用 ^C 中断时,使 bash 退出 != 0

我对以下 (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),但(至少对我而言)不太清楚该要求是否也适用于awks 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.00781252 / 256(对于of SEGV11如果核心被转储,则为 0.542969 ((128+11)/256),否则为 0.0429688 (11/256)),在 Solaris 10 或 11 或其 Linux 端口上nawk找到nawk传家宝工具箱,bwk-awkawk由 Brian Kernighan 本人维护Kawk)在某些 BSD 上找到的基础awk(此处在 Debian GNU/Linux 上进行了测试)。/usr/xpg4/bin/awk在 Solaris 11 上的行为类似于gawk.

因此,根据s返回的值system(3)(一个整数,其中位 0 到 6 是信号编号,位 7 是核心位,位 8 到 15 是退出代码),上面awksystem()返回:

  • 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被终止,例如。如果你的是或,你可以这样做:awkmawkshbashyash

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。显然是一个一致性错误。
  • gawkbwk-awk: 给出sexit 1给出 256,被 SIGINT 杀死给出 2,被 SIGSEGV 11 杀死,核心给出 139)。
  • mawk:与 相同system(),看起来mawk是唯一考虑到这一点的实现。

相关内容