如果第一个命令失败,则防止第二个命令在 bash 管道中启动

如果第一个命令失败,则防止第二个命令在 bash 管道中启动

在学习(或多或少)一些关于管道的有用讨论之后,例如 获取通过管道传输到另一个进程的退出状态当管道中的一个进程失败时退出当第一个命令失败时,我仍然无法避免启动第二个命令。我是否遗漏了有关管道的基本细节?

例如

$ somecommand | tar -T - -czf /tmp/someProject.tar.gz

tar.gz如果somecommand不能正常工作并且只产生一些错误消息而不是预期的文件列表,则不应创建几乎空的文件。

答案1

是的,那里有一些关于管道的基本细节。

管道的要点是并行运行两个或多个命令,这避免了必须完整存储所有数据,并且可以节省时间,因为所有进程可以同时工作。根据定义,这意味着第二个命令在第一个命令退出之前启动,因此第一个命令的退出状态尚不可用。

简单的解决方法是使用临时文件。这里的存储应该不会有太大问题,因为我们只传递文件名列表,而不是数据本身。例如:

tmp=$(mktemp)
if somecommand > "$tmp"; then
    tar -T - -czf /tmp/someProject.tar.gz < "$tmp"
fi
rm -f "$tmp"

或者确实像 terdon 评论一样,只需运行即可,如果失败tar则随后删除 tar 文件。somecommand但是,如果somecommand在失败之前生成部分但重要的文件列表,则在创建要删除的存档时仍然可能会导致一些不必要的 I/O。

另外,至少在 GNU tar 中,默认情况下-T会对看起来像命令行选项的引号和行进行一些处理,因此如果您有令人讨厌的文件名,您可能需要考虑到这一点,或者查看--verbatim-files-from, 或--null.其他 tar 实现也可能存在类似的问题。

答案2

您似乎误解了管道命令的本质。

在命令管道中,全部命令并行启动(参见这里例如)。这就是为什么在您的构造中无法tar“等待”成功完成somecommand,因为 会读取在运行过程中创建的tar输出。somecommand

您可以采用一些解决方法来缓解这种情况:

  • 将 的输出缓冲somecommand在临时文件中,并且仅tar在退出状态somecommand信号为“成功”时才运行,然后将其删除(仅当您有足够的存储空间时才可行)
  • 正如 @terdon 所提到的,如果失败,请使用该pipefail选项删除不可用的文件。tar.gzsomecommand

答案3

最简单的方法是使用&&.由于您还想捕获输出,因此您可以先重定向它,然后再使用它。

somecommand > somefile && tar -T somefile -czf /tmp/someProject.tar.gz

如果somecommand以 then 以外的任何内容退出exit 0,则tar不会执行。

答案4

管道不是执行此操作的方法。仅当第一个命令成功时,该&&构造才执行第二个命令。

例如在没有名为“goober”的文件的目录中:

$ ls goober | echo "done"
done
ls: cannot access 'goober': No such file or directory
$ ls goober && echo "done"
ls: cannot access 'goober': No such file or directory

在你的例子中:

$ somecommand && tar -T - -czf /tmp/someProject.tar.gz

如果somecommand失败,则tar不会运行。

相关内容