启动其他命令的命令是否有一个标准,如何将 shell 结构视为重定向?

启动其他命令的命令是否有一个标准,如何将 shell 结构视为重定向?

有没有一种简单的方法可以执行以下命令的操作?

watch foo 2>/dev/null | tr ' ' '-'
strace foo 2>/dev/null | tr ' ' '-'
something_else foo 2>/dev/null | tr ' ' '-'    

是否有共同的期望something_else应该如何表现?是否有 shell 结构可以消除这些命令的歧义(例如重定向哪个程序的输出)?

答案1

命令本身不处理重定向。当 shell 创建一个要在其中运行命令的新进程时,shell 将按照指示设置 IO。命令本身只是运行,无需关心它。

您的something_else命令将像往常一样将输出发送到 stdout 和 stderr。

答案2

长话短说:

是否存在共同的期望 some_else 应该如何表现?

是的,作为常规命令。 shell 不理解为两个命令,它是一个命令和一个参数(即使它可能是例如command1 command2中找到的有效二进制文件的名称)。/bin这是command1稍后command2通过exec()系统调用运行的,而不是 shell。

是否有 shell 结构可以消除这些命令的歧义(例如重定向哪个程序的输出)

不,someting_else不会受到任何特殊对待。如上所述,除了一个命令和一个参数之外没有其他结构。这个问题的困惑似乎还在于两个都 stracefoo假设由 shell 运行,但实际上它是一个父子进程链。

shell 如何处理命令和重定向

类似 Bourne 的 shell(即bash, dash,ksh)都遵循关于如何解释命令的 POSIX 规则。POSIX 指定:““简单命令”是一系列可选的变量赋值和重定向,可以任意顺序,可选地后跟单词和重定向,并由控制运算符终止。这可以写成一个表格:

[VAR=foo BAR=baz] command1 [arg1, arg2...] [ n>m ]

一般规则是重定向适用于命令,该命令将保留大部分非赋值字。在您的示例中,strace是命令,foo是 的参数strace。重定向仅适用于命令,而不适用于参数,即strace.不是foo由 shell 运行,而是由 运行strace,在这种情况下foo成为 的子进程strace。同样,somethingelse将被视为foo带有参数的命令。

文件描述符

就非内置命令而言,它们是 shell 的子进程,并且子进程继承 shell 的文件描述符,因此它们通常不管理重定向 - 它们接收“预先打包”的目的地,因此strace到目前为止2>/dev/null,它可能不会关心文件描述符 2 实际上是什么(除非它正在主动检查源代码级别)。strace仍会将输出写入文件描述符 2,但 shell 已将该文件描述符连接到指向/dev/null.

由于文件描述符是继承的,这也解释了为什么如果您执行类似strace stat noexist 2>stracelog.txt来自两个错误流的操作stracestat进入同一个文件。相比之下,某些命令允许显式指定目的地作为其选项之一。因此,您只有文件中strace -o tracelog.txt stat noexist 2>stracelog.txt的输出- 即使文件描述符是继承的,现在标志是 的属性,并且即使文件描述符是继承的,输出也由命令管理。statstracelog.txt-ostrace

这也给了我们一个小提示:理论上,命令可以“重定向”,即通过系统调用将文件描述符复制到现有文件描述符上dup2(),这与 shell 使用的机制完全相同,但就>重定向符号的类型而言 - 这仍然是在shell 控制,因此只能由父 shell 解释。

为什么 2>/dev/null ?

人们普遍认为诊断输出会发送到stderr.事实上,我们已经有两篇关于这个主题的优秀文章:

watchstrace你的例子中遵循惯例,仅此而已。本身没有要求指定2>/dev/null隐藏此类命令的输出。

例如,如果您确实想为作为参数传递的命令指定重定向,strace则需要有一个 shell 围绕它。例如,

strace -f bash -c 'stat /etc/passwd nonexisting 2>/dev/null'

请注意标志的使用-f,因为如果您的重点是跟踪应用于stat命令的系统调用,那么如果没有标志,您可能看不到它们-f

相关内容