Bash 在管道中,下游程序如何在输入结束后获得上游程序的出口代码?

Bash 在管道中,下游程序如何在输入结束后获得上游程序的出口代码?,bash,pipe,sh,exit-code,Bash,Pipe,Sh,Exit Code,我写了一个程序,它等待标准输入的数据,并将其写入 临时位置,并在输入结束时将其移动到用户指定的位置 通过这个,我想启用这样的管道 # Does not work; truncates myfile cat myfile | some_filter > myfile # shall enable pseudo-inplace modification cat myfile | some_filter | my_program myfile 但我的程序当前会写入该文件,而不管上游管道是否

我写了一个程序,它等待标准输入的数据,并将其写入 临时位置,并在输入结束时将其移动到用户指定的位置

通过这个,我想启用这样的管道

# Does not work; truncates myfile
cat myfile | some_filter > myfile 
# shall enable pseudo-inplace modification
cat myfile | some_filter | my_program myfile 
但我的程序当前会写入该文件,而不管上游管道是否成功。如果上游遇到错误,我希望避免数据丢失,因此如果管道上游的某个程序抛出错误,则中止该程序

到目前为止,我用bash编写了这个程序,但并不局限于此

我如何在我的程序中检查这一点


编辑:更具体地说:我想让我的用户尽可能多地处理创建临时文件、检查中间程序和类似程序的成功与否。我希望用户能够在某个文件上调用某个程序(例如,向文本文件中添加列、排序、筛选等),并将结果写回同一个文件,但前提是中间程序返回成功。

重定向,例如
>文件
由shell处理,在调用命令之前打开文件进行写入。解决方法是-如@dood在评论中建议的,使用临时文件:

tmp=$(mktemp)
a file | b | c > "$tmp"; && mv "$tmp" file
现在由
mktemp
创建的文件用于重定向。然后在文件移动到
文件
之后

但是,这将仅检查管道中的最后一个命令,如果成功,则移动该命令

中有一个名为
PIPESTATUS
的变量,该变量包含管道的所有退出状态,例如:

% ls | cat | cat
% echo "${PIPESTATUS[@]}"
0 0 0
一个失败:

% ls | cat | false
% echo "${PIPESTATUS[@]}"
0 141 1
可以使用此选项检查管道中的所有命令是否已成功退出:

% ls | cat | cat
% [[ "${PIPESTATUS[@]}" =~ ^0( 0)*$ ]] && echo "good"
good
以你为例:

tmp=$(mktemp)
some_filter < myfile > "$tmp"
[[ "${PIPESTATUS[@]}" =~ ^0( 0)*$ ]] && mv "$tmp" "myfile"
tmp=$(mktemp)
一些过滤器“$tmp”
[[“${PIPESTATUS[@]}”=~^0(0)*$]&&mv“$tmp”“myfile”

正则表达式
^0(0)*$
匹配
0
0
0
。。。因此,如果管道中的所有命令都成功退出,则基本匹配。

重定向(例如
>文件)由shell处理,shell在调用命令之前打开文件进行写入。解决方法是-如@dood在评论中建议的,使用临时文件:

tmp=$(mktemp)
a file | b | c > "$tmp"; && mv "$tmp" file
现在由
mktemp
创建的文件用于重定向。然后在文件移动到
文件
之后

但是,这将仅检查管道中的最后一个命令,如果成功,则移动该命令

中有一个名为
PIPESTATUS
的变量,该变量包含管道的所有退出状态,例如:

% ls | cat | cat
% echo "${PIPESTATUS[@]}"
0 0 0
一个失败:

% ls | cat | false
% echo "${PIPESTATUS[@]}"
0 141 1
可以使用此选项检查管道中的所有命令是否已成功退出:

% ls | cat | cat
% [[ "${PIPESTATUS[@]}" =~ ^0( 0)*$ ]] && echo "good"
good
以你为例:

tmp=$(mktemp)
some_filter < myfile > "$tmp"
[[ "${PIPESTATUS[@]}" =~ ^0( 0)*$ ]] && mv "$tmp" "myfile"
tmp=$(mktemp)
一些过滤器“$tmp”
[[“${PIPESTATUS[@]}”=~^0(0)*$]&&mv“$tmp”“myfile”

正则表达式
^0(0)*$
匹配
0
0
0
。。。因此,如果管道中的所有命令都成功退出,则基本上是匹配的。

在管道中运行命令的全部要点是它们同时执行。运行
cmd1 | cmd2 | cmd3
时,
cmd3
很可能在
cmd1
完成之前开始执行。因此,除非您可以调用星际迷航式的时间异常,否则无法在cmd3启动之前确定cmd1的退出状态。如果需要等到cmd1完成后再开始cmd3,则不能将它们放在同一管道中。嗯,那不完全是真的,你可以像这样做

mkfifo/tmp/foo;{cmd1;echo>/tmp/foo;}cmd2{cat/tmp/foo;cmd3;}

但你为什么要这么做?(请注意,即使在这种情况下,管道最后部分中的命令也会在cmd1完成之前执行,但
cat
会阻塞,因此
cmd3
的执行会延迟。)


还要注意的是,无论您尝试什么,都可能会出现死锁。如果管道已满,cmd1和cmd2将在写入时无限期阻塞,cmd3将永远不会启动。

在管道中运行命令的关键在于它们同时执行。运行
cmd1 | cmd2 | cmd3
时,
cmd3
很可能在
cmd1
完成之前开始执行。因此,除非您可以调用星际迷航式的时间异常,否则无法在cmd3启动之前确定cmd1的退出状态。如果需要等到cmd1完成后再开始cmd3,则不能将它们放在同一管道中。嗯,那不完全是真的,你可以像这样做

mkfifo/tmp/foo;{cmd1;echo>/tmp/foo;}cmd2{cat/tmp/foo;cmd3;}

但你为什么要这么做?(请注意,即使在这种情况下,管道最后部分中的命令也会在cmd1完成之前执行,但
cat
会阻塞,因此
cmd3
的执行会延迟。)


还要注意的是,无论您尝试什么,都可能会出现死锁。如果管道已满,cmd1和cmd2将在写入时无限期阻塞,cmd3将永远不会启动。

您可以尝试使用mktemp创建临时文件,写入临时文件,检查过程是否成功完成,然后从临时文件写入您的文件,删除临时文件。要解释@dood所说的内容:
tmpfile=$(mktemp);process1 | process2>$tmpfile和mv$tmpfile myfile
另外,
cat myfile |一些过滤器
也在使用中。改用
some_filter
。@dood是的,这就是我在程序中写的东西。但是,我想从用户那里屏蔽它,这就是为什么我问是否可以从下游程序中检查上游程序的错误代码。@anishsane你完全正确,我写这篇文章只是为了更好地可视化管道结构。你可以试着用mktemp制作一个临时文件,写入临时文件,检查进程是否成功完成,然后从临时文件写入