Bash进程替换和退出码

8
我想将以下内容翻译如下:
使用以下命令可以获得所需的结果并将输出镜像到标准输出,对结果进行零长度检查:
git status --short && (git status --short | xargs -Istr test -z str)
转换为以下代码行:
git status --short | tee >(xargs -Istr test -z str)
但是这种方式总是返回tee的退出状态码(始终为零)。是否有一种优雅的方法来获取替代进程的退出代码?
【编辑】
目前我使用以下命令,它可以避免运行相同的命令两次,但似乎还需要更好的解决方法:
OUT=$(git status --short) && echo "${OUT}" && test -z "${OUT}"

不好意思,请问您想要实现什么目标?只是想检查一下那个目录中是否有git状态吗? - Alexander Janssen
是的,它是部署脚本的一部分,如果目录不干净,应该退出非零。 - jodell
https://stackoverflow.com/questions/54771556/detecting-exit-status-on-process-substitution - Luchostein
3个回答

3

我已经在这个问题上工作了一段时间,似乎除了使用内联信号之外,没有其他方法可以使用过程替换来实现这一点,并且这只能用于输入管道,因此我不会进一步扩展它。

然而,在bash-4.0中提供了协处理器,可以用于取代过程替换,并提供清洁回收。

你提供的以下代码片段:

git status --short | tee >(xargs -Istr test -z str)

可以用类似的东西替代:

coproc GIT_XARGS { xargs -Istr test -z str; }
{ git status --short | tee; } >&${GIT_XARGS[1]}
exec {GIT_XARGS[1]}>&-
wait ${GIT_XARGS_PID}

现在,需要解释一下: coproc 调用创建了一个新的协处理程序,将其命名为 GIT_XARGS(您可以使用任何名称),并运行括号中的命令。一对管道被创建用于协处理程序,重定向其标准输入和标准输出。 coproc 调用设置了两个变量:
  1. ${GIT_XARGS[@]} 包含适当的进程标准输入和标准输出的管道([0] 用于从标准输出读取,[1] 用于写入标准输入),
  2. ${GIT_XARGS_PID} 包含协处理程序的 PID。
之后,运行您的命令,并将其输出定向到第二个管道(即协处理程序的标准输入)。看起来像密码的部分 >&${GIT_XARGS[1]} 被扩展为类似于 >&60 的内容,这是常规的输出到文件描述符重定向。
请注意,我需要将您的命令放在大括号中。这是因为管道会导致子进程被生成,它们不会继承父进程的文件描述符。换句话说,以下内容:
git status --short | tee >&${GIT_XARGS[1]}

如果不加大括号,命令将会由管道符分隔成两部分,在执行tee命令时会因无效的文件描述符报错。这是因为相关的文件描述符存在于父进程中而不是派生出来的tee进程中。加上大括号可以使bash将重定向作用于整个管道。

使用exec调用关闭与协同进程的通信管道。当您使用进程替换时,该进程作为输出重定向的一部分被产生,并且在重定向不再生效后就立即关闭了与该进程之间的管道。由于协同进程的管道生命周期超过单个重定向,因此我们需要显式地关闭它。

关闭输出管道应该导致进程在标准输入上获得EOF状态并优雅地终止。我们使用wait等待其终止并回收它。wait返回协同进程的退出状态。

最后,请注意,在这种情况下,您不能使用kill终止协同进程,因为那样会改变其退出状态。


这里实际上没有用到tee,stdout已经被重定向到stdout了。 - weynhamz
对我来说,exec {GIT_XARGS[1]}>&- 返回 -bash: exec: {GIT_XARGS[1]}: 未找到 - Brecht Machiels
你是否拥有bash-4.0或更新版本? - Michał Górny

3
请看这里:
  $ echo xxx | tee >(xargs test -n); echo $?
xxx
0
  $ echo xxx | tee >(xargs test -z); echo $?
xxx
0

再看这里:

  $echo xxx | tee >(xargs test -z; echo  "${PIPESTATUS[*]}")
xxx
123
  $echo xxx | tee >(xargs test -n; echo  "${PIPESTATUS[*]}")
xxx
0

是这样吗?

请参考此处了解更多信息。


5
如果有其他人认为PIPESTATUS可以解决这个问题,那就错了。如果将echo $?移到>(...)结构内部,它也会像预期的那样运行。如果将PIPESTATUS版本移到外面,在这两种情况下它都返回0。 - Russell Reed

0
#!/bin/bash
if read q < <(git status -s)
then
  echo $q
  exit
fi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接