从Bash参考手册中,我获得了关于execBash内置命令的以下信息:
如果提供了命令,它将替换shell而不创建新进程
现在我有了以下bash脚本:
#/bin/bash
行政人员;
回波123;
出口0
这个执行,我得到了这个:
cleanup.sh ex1.bash file.bash file.bash~output.log
(当前目录中的文件)
现在,如果我有这个脚本:
#/bin/bash
执行官ls | cat
回声123
出口0
我得到以下输出:
cleanup.sh
ex1.bash
file.bash
file.bash~
output.log
123
我的问题是:
如果调用exec时,它替换shell而不创建新进程,为什么在put|cat时,会打印echo 123,但如果没有它,它就不会打印。所以,如果有人能解释这种行为的逻辑,我会很高兴
谢谢
编辑:
在@torek响应之后,我得到了一个更难解释的行为:
1.exec ls>;out命令创建out文件,并将ls的命令结果放入其中
2.exec ls>;out1 ls>;out2只创建文件,但不放入任何结果。如果命令按建议工作,我认为命令2应该与命令1具有相同的结果(更重要的是,我认为它不应该创建out2文件)
在这种特殊情况下,管道中有exec。为了执行一系列管道命令,shell必须首先分叉,形成一个子shell。(具体来说,它必须先创建管道,然后创建fork,这样管道“左侧”运行的所有对象都可以将其输出发送到管道“右侧”的任何对象。)
要了解实际情况,请比较:
猫
与:
{exec ls;也重复这个;}cat
前者在不离开子shell的情况下运行ls,因此该子shell仍然可以运行echo。后者通过离开子外壳来运行ls,因此子外壳不再执行echo,并且也不会打印
(大括号{cmd1;cmd2;}的使用通常会抑制使用括号(cmd1;cmd2)获得的子shell分叉操作,但在管道的情况下,分叉是“强制”的,就像以前一样。)
只有在单词exec之后出现“无需运行”时,才会重定向当前shell。因此,例如,exec>;标准输出4<;输入5>&燃气轮机;append修改当前shell,但exec foo>;标准输出4<;输入5>&燃气轮机;append尝试执行命令foo。[注:这并非严格准确;见附录。]
有趣的是,在交互式shell中,在exec foo>;输出失败,因为没有命令foo,shell仍然停留,但stdout仍然重定向到文件输出。(您可以使用exec>;/dev/tty进行恢复。在脚本中,exec foo失败将终止脚本。)
对于@Pumbaa80,这里有一些更具说明性的东西:
#/bin/bash
shopt-s execfail
执行官ls | cat-E
回显这个到stdout
回显此信息至stderr 1>&;2.
(注意:cat-E是从我通常的cat-vET简化而来的,这是我方便的“让我以可识别的方式查看非打印字符”)。运行此脚本时,来自ls的输出应用了cat-E(在Linux上,这使行尾显示为$符号),但发送到stdout和stderr(在其余两行上)的输出被重定向。将| cat-E更改为>;out并且在脚本运行后,观察文件out的内容:最后两个echo不在其中
现在将ls更改为foo(或其他一些找不到的命令),然后再次运行脚本。这一次的输出是:
$。/demo.sh
./demo.sh:第3行:exec:foo:未找到
这是去斯特德的
文件out现在包含第一行echo生成的内容
这使得exec的“真正作用”尽可能明显(但阿尔伯特·爱因斯坦没有说得更明显:-)
通常,当shell执行“简单命令”(有关精确定义,请参阅手册页,但这特别排除了“管道”中的命令)时,它会准备使用指定的任何I/O重定向操作<,>,等等,打开所需的文件。然后,shell调用fork(或一些等效但更有效的变体,如vfork或clone,具体取决于底层操作系统、配置等),并在子进程中重新排列打开的文件描述符(使用dup2调用或等效调用),以实现所需的最终安排:>;out将打开描述符移动到fd 1-stdout-while6>;out将打开描述符移动到fd 6
但是,如果指定exec关键字,shell将抑制fork步骤。它像往常一样执行所有文件打开和文件描述符重新排列操作,但这一次,会影响任何和所有后续命令。最后,在完成所有重定向之后,shell尝试执行命令(如果有)。如果没有命令,或者调用execve()失败并且shell应该继续运行(是交互式的,或者您已经设置了execfail),则shell将打开。如果execve()。如果未设置execfail,并且shell不是交互式的,则shell退出
(还有command\u not\u found\u handleshell函数增加的复杂性:根据测试结果,bash的exec似乎禁止运行它。exec关键字通常使shell不会查看自己的函数,即,如果您有一个shell函数f,则以简单的命令运行fs shell函数,正如在子shell中运行它的(f),但运行(exec f)会跳过它。)
至于为什么ls>;out1 ls>;out2创建两个文件(带或不带exec),这很简单:shell打开每个重定向,然后使用dup2移动文件描述符。如果有两个普通的重定向,shell会同时打开,将第一个重定向移动到fd 1(stdout),然后将第二个移动到fd 1(再次执行stdout),关闭进程中的第一个。最后,它运行ls ls,因为这是删除>;out1>;out2后剩下的内容。只要没有名为ls的文件,ls命令会向stderr投诉,而不会向stdout写入任何内容