有没有一种简单的方法可以执行以下命令?
watch foo 2>/dev/null | tr ' ' '-'
strace foo 2>/dev/null | tr ' ' '-'
something_else foo 2>/dev/null | tr ' ' '-'
是否有共同的期望something_else
应该如何表现?是否有 shell 结构可以消除这些命令的歧义(比如重定向哪个程序的输出)?
有没有一种简单的方法可以执行以下命令?
watch foo 2>/dev/null | tr ' ' '-'
strace foo 2>/dev/null | tr ' ' '-'
something_else foo 2>/dev/null | tr ' ' '-'
是否有共同的期望something_else
应该如何表现?是否有 shell 结构可以消除这些命令的歧义(比如重定向哪个程序的输出)?
命令本身不处理重定向。当 shell 创建一个运行命令的新进程时,shell 将按照指示设置 IO。命令本身只是运行而无需关心它。
您的
something_else
命令将像往常一样将输出发送到 stdout 和 stderr。TL;博士:
是的,作为常规命令。shell 不理解
command1 command2
为两个命令,它是一个命令和一个参数(即使它可能是/bin
例如在 中找到的有效二进制文件的名称)。它command1
是后来command2
通过exec()
系统调用而不是 shell 运行的。不,
someting_else
不会以任何特殊方式对待。如上所述,除了一个命令和一个参数外,没有其他结构。这个问题的困惑似乎还在于和都 被假定为由 shell 运行,但实际上它是一个父子进程链。strace
foo
shell 如何处理命令和重定向
Bourne-like shell(即
bash
,dash
,ksh
)都遵循关于如何解释命令的 POSIX 规则。POSIX 规定:““简单命令”是一系列可选的变量赋值和重定向,可以任意顺序,可选地后跟单词和重定向,由控制运算符终止。这可以写成一个形式:一般规则是重定向适用于命令,该命令将是最不赋值的字。在您的示例中,
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
从两个错误流中执行类似的操作strace
并stat
进入同一个文件。相比之下,一些命令允许明确指定目的地作为它们的选项之一。因此,strace -o tracelog.txt stat noexist 2>stracelog.txt
您只有文件stat
中的输出stracelog.txt
- 即使文件描述符是继承的,现在-o
标志是 的属性strace
,并且即使文件描述符是继承的,输出也由命令管理。这也给了我们一个小提示:理论上,命令可以在通过系统调用将文件描述符复制到现有文件描述符的意义上“重定向”
dup2()
,这与 shell 将使用的机制完全相同,但就>
重定向符号的类型而言 - 这仍然在shell 控制,因此只能由父 shell 解释。为什么 2>/dev/null ?
人们普遍认为诊断输出转到
stderr
. 事实上,我们已经有两篇关于这个主题的优秀文章:进度报告/日志信息属于 stderr 还是 stdout?
何时在命令行应用程序中使用标准错误流?
watch
在你的strace
例子中遵循惯例,仅此而已。本身没有要求指定2>/dev/null
隐藏此类命令的输出。如果您确实想为作为参数传递的命令指定重定向,
strace
例如,您需要在它周围有一个 shell。例如,请注意
-f
标志的使用,因为如果您的重点是跟踪应用于命令的系统调用,那么如果没有标志stat
,您可能看不到它们。-f