在POSIX awk中,如何通过command
处理其输出后,从command | getline var
获取退出状态(返回代码)?如果exit 1
以非零退出状态退出,我希望我的awk脚本command
。
例如,假设我有一个名为foo.awk
的awk脚本,如下所示:
function close_and_get_exit_status(cmd) {
# magic goes here...
}
BEGIN {
cmd = "echo foo; echo bar; echo baz; false"
while ((cmd | getline line) > 0)
print "got a line of text: " line
if (close_and_get_exit_status(cmd) != 0) {
print "ERROR: command '" cmd "' failed" | "cat >&2"
exit 1
}
print "command '" cmd "' was successful"
}
然后我想要发生以下事情:
$ awk -f foo.awk
got a line of text: foo
got a line of text: bar
got a line of text: baz
ERROR: command 'echo foo; echo bar; echo baz; false' failed
$ echo $?
1
根据POSIX specification for awk,command | getline
返回1表示成功输入,0表示文件结束,-1表示错误。如果command
以非零退出状态退出,则不是错误,因此不能用于查看command
是否已完成且已失败。
类似地,close()
不能用于此目的:close()
仅在关闭失败时返回非零,而不是在关联命令返回非零退出状态时返回非零。 (在gawk中,close(command)
返回command
的退出状态。这是我想要的行为,但我认为它违反了POSIX规范,并且并非awk的所有实现都以这种方式运行。)
awk system()
函数返回命令的退出状态,但据我所知,没有办法使用getline
。
答案 0 :(得分:4)
最简单的方法是在命令执行后从shell回显退出状态,然后使用getline读取它。 e.g。
$ cat tst.awk
BEGIN {
cmd = "echo foo; echo bar; echo baz; false"
mod = cmd "; echo \"$?\""
while ((mod | getline line) > 0) {
if (numLines++)
print "got a line of text: " prev
prev = line
}
status = line
close(mod)
if (status != 0) {
print "ERROR: command '" cmd "' failed" | "cat >&2"
exit 1
}
print "command '" cmd "' was successful"
}
$ awk -f tst.awk
got a line of text: foo
got a line of text: bar
got a line of text: baz
ERROR: command 'echo foo; echo bar; echo baz; false' failed
$ echo $?
1
如果有人正在阅读此内容并考虑使用getline,请务必阅读http://awk.freeshell.org/AllAboutGetline并完全了解所有注意事项以及首先执行此操作的含义。
答案 1 :(得分:2)
不是理想的解决方案,但您可以这样做:
"command || echo failure" | getline var; ... if( var == "failure" ) exit;
有一些歧义,你必须选择字符串“failure”,使得命令永远不会生成相同的字符串,但这可能是一个适当的解决方法。
答案 2 :(得分:1)
以下是可怕的复杂,但它:
fflush()
尚未加入POSIX标准,but it will be且广泛可用)基本方法是在命令完成后回显退出状态/返回值。如果最后一行不为零,则退出awk脚本并显示错误。为了防止代码误将命令输出的一行文本输入到退出状态,命令输出的每行文本前面都会加上一个后来被删除的字母。
function stderr(msg) { print msg | "cat >&2"; }
function error(msg) { stderr("ERROR: " msg); }
function fatal(msg) { error(msg); exit 1; }
# Wrap cmd so that each output line of cmd is prefixed with "d".
# After cmd is done, an additional line of the format "r<ret>" is
# printed where "<ret>" is the integer return code/exit status of the
# command.
function safe_cmd_getline_wrap(cmd) {
return \
"exec 3>&1;" \
"ret=$(" \
" exec 4>&1;" \
" { ( "cmd" ) 4>&-; echo $? >&4; } 3>&- |" \
" awk '{print\"d\"$0;fflush()}' >&3 4>&-;" \
");" \
"exec 3>&-;" \
"echo r${ret};"
}
# like "cmd | getline line" except:
# * if getline fails, the awk script exits with an error
# * if cmd fails (returns non-zero), the awk script exits with an
# error
# * safe_cmd_getline_close(cmd) must be used instead of close(cmd)
function safe_cmd_getline(cmd, wrapped_cmd,ret,type) {
wrapped_cmd = safe_cmd_getline_wrap(cmd)
ret = (wrapped_cmd | getline line)
if (ret == -1) fatal("failed to read line from command: " cmd)
if (ret == 0) return 0
type = substr(line, 1, 1)
line = substr(line, 2)
if (type == "d") return 1
if (line != "0") fatal("command '" cmd "' failed")
return 0
}
function safe_cmd_getline_close(cmd) {
if (close(safe_cmd_getline_wrap(cmd))) fatal("failed to close " cmd)
}
你使用上面这样的:
cmd = "ls no-such-file"
while (safe_cmd_getline(cmd)) {
print "got a line of text: " line
}
safe_cmd_getline_close(cmd)
答案 3 :(得分:1)
如果您有mktemp
命令,则可以将退出状态存储在一个临时文件中:
#!/bin/sh
set -e
file=$(mktemp)
finish() {
rm -f "$file"
}
trap 'finish' EXIT
trap 'finish; trap - INT; kill -s INT $$' INT
trap 'finish; trap - TERM; kill $$' TERM
awk -v file="$file" 'BEGIN{
o_cmd="echo foo; echo bar; echo baz; false"
cmd = "("o_cmd "); echo $? >\""file"\""
print cmd
while ((cmd | getline) > 0) {
print "got a line of text: " $0
}
close(cmd)
getline ecode <file; close(file)
print "exit status:", ecode
if(ecode)exit 1
}'