【问题标题】:The cat, the echo, and the process substitutioncat、echo 和进程替换
【发布时间】:2020-06-09 04:38:36
【问题描述】:

基本上我想了解这些命令之间的区别:

cat <<< yolo | tee f.txt
echo yolo | tee t.txt

还有那些命令:

cat <<< yolo > >(tee f.txt)
echo yolo > >(tee t.txt)

前两个命令的结果完全相同:打印“yolo”,然后终端返回控制权,这正是我所期望的。

[user@localhost ~]$ cat

yolo

[user@localhost ~]$ echo yolo | tee t.txt

yolo

但是使用进程替换,echo 会发生一些奇怪的事情。

[user@localhost ~]$ cat >(tee f.txt)

yolo

[user@localhost ~]$ echo yolo > >(tee t.txt)

[user@localhost ~]$ yolo

终端返回控件文本被打印出来之前。 在这种情况下,为什么我会更早获得控制权?

这一定与进程的打开方式以及文件描述符如何在进程之间传递有关,但我有点达到了我的知识极限。

如果我通过管道将它连接到其他任何东西,一切都会恢复正常,例如echo yolo &gt; &gt;(tee t.txt) | cat.

更奇怪的是,xargs'ing into echo 效果很好:

[user@localhost ~]$ xargs echo >(tee t.txt)

yolo

但是你可以说xargs是这里的主程序,而不是echo

如果我使用 cat 的输入进程替换,我会得到不同的结果:

cat &lt; &lt;(echo yolo) &gt; &gt;(tee t.txt)

有时它会给我这个:

[user@localhost ~]$ cat >(tee t.txt)

[user@localhost ~]$ yolo

有时是这样的:

[user@localhost ~]$ cat >(tee t.txt)

yolo

所以我猜这可能与系统执行命令的速度有关,这使得它无法预测。

这是否意味着输出进程替换(例如本例中的tee)在后台运行?

【问题讨论】:

  • 这似乎与以下事实有关您应该再次获得与前三个相同的行为。至少这是我观察到的。
  • 无论我打电话给/usr/bin/echo yolo &gt; &gt;(tee t.txt) 还是'echo' yolo &gt; &gt;(tee t.txt)(带引号)我都得到与echo yolo &gt; &gt;(tee t.txt) 相同的结果。但是如果这是与性能相关的,那么调用 /usr/bin/echo 可能会稍微慢一些/更快,具体取决于系统,导致问题在不同的计算机上出现/消失。
  • 专业提示:您可以通过管道将进程替换的输出同步到另一个进程,这样另一个进程在输入流打开之前不会退出并且 bash 会等待它,所以它会有效地等待进程替换。所以echo yolo &gt;(tee t.txt) | cat会同步输出。
  • @KamilCuk 是的,这实际上是我的建议之一;)但这并不是一个真正的选择,因为在我的情况下,我对echo 的退出代码感兴趣,我想避免使用 PIPESTATUS(是的,我知道 echo 总是返回 0,但您可能已经猜到我最终会使用另一个程序)。
  • 为什么要避免使用 PIPESTATUS?这会起作用:{ echo yolo; echo $? &gt; "temporary_tile"; } | tee t.txt;

标签: bash echo cat process-substitution


【解决方案1】:

啊,我好像找到了it...

流程替代 ...进程list异步方式运行,其输入或输出显示为文件名...

一旦命令(或分叉进程完成),控制权将返回到终端并显示下一个提示。我最初怀疑echo 被内置可能起到了作用,但它实际上只是扭曲了时间。 IE。一旦使用&gt; &gt;(tee t.txt) tee 何时打印到控制台并不完全确定。

就此而言,试试这个(对于你的第三个例子):

$ cat <<< yolo > >(sleep 1; tee f.txt)
$ yolo

相对于:

$ { echo yolo; sleep 1 ;} > >(tee t.txt)
yolo
$

不同之处在于&gt;(list) 进程替换是如前所述异步执行的。尝试两个示例中的前一个,即使sleep 的值更大,以模拟长时间运行的进程。当你继续使用你的 shell 时它会一直存在(实际上即使你终止它,在这种情况下它会被重新设置,但你仍然会在进程列表中看到;旁注:“命令作为命令替换的结果会忽略键盘生成的作业控制信号SIGTTINSIGTTOUSIGTSTP。” -> 与异步 (&amp;) 执行示例不同,即使丢失终端也不会杀死它们下面)。

另一方面,Pipelines

如果管道不是异步执行的(参见列表),shell 会等待管道中的所有命令完成。

在管道中的所有命令执行完毕之前,Shell 不会恢复控制(下一个命令不会执行)。

试试:

$ echo yolo |  (sleep 1; tee f.txt)
yolo
$

相对于(类似于使用&gt;(list)):

echo yolo |  ((sleep 1; tee f.txt) &)
$ yolo

(实际上不需要双子shell,我只是用它来抑制正在运行的shell中的作业控制消息)。

【讨论】:

  • 很有趣,但我不太明白为什么tee 稍后会终止。我认为退出 catecho 会像使用管道时一样关闭文件描述符。否则,这意味着由进程替换启动的进程可能会尝试从关闭的管道中读取(更准确地说,是在启动程序退出后立即从最近关闭的管道中读取)。
  • 事件链是:echo 终止。 echo 关闭标准输出。 tee get 通知输入已关闭。 tee 终止。所以因为我们有调度程序,bash 进程可以在teebash 可以看到echotee 看到之前终止。所以这就是你所看到的 - bash 获得足够的 cpu 时间来写入 $,而 tee 稍后终止,刷新它的标准输出并写入 yolo
  • 我认为echo yolo | ((sleep 1; tee f.txt) &amp;) 的例子很有启发性。我可以很好地理解为什么这个命令过早地把控制权交还给我,而且基本上进程替换是(几乎)相同的。
  • 我添加了一个部分来解释差异。文件/管道处理的机制非常相似(在&gt;(list) 的情况下,它会再次复制句柄,并且可以通过/dev/fd/ 访问它 -> 指向/proc/)。
猜你喜欢
  • 1970-01-01
  • 2014-04-24
  • 2011-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-16
  • 1970-01-01
  • 2011-05-28
相关资源
最近更新 更多