有没有一种简单的方法可以执行以下命令的操作?
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
不会受到任何特殊对待。如上所述,除了一个命令和一个参数之外没有其他结构。这个问题的困惑似乎还在于两个都 strace
并foo
假设由 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
来自两个错误流的操作strace
并stat
进入同一个文件。相比之下,某些命令允许显式指定目的地作为其选项之一。因此,您只有文件中strace -o tracelog.txt stat noexist 2>stracelog.txt
的输出- 即使文件描述符是继承的,现在标志是 的属性,并且即使文件描述符是继承的,输出也由命令管理。stat
stracelog.txt
-o
strace
这也给了我们一个小提示:理论上,命令可以“重定向”,即通过系统调用将文件描述符复制到现有文件描述符上dup2()
,这与 shell 使用的机制完全相同,但就>
重定向符号的类型而言 - 这仍然是在shell 控制,因此只能由父 shell 解释。
为什么 2>/dev/null ?
人们普遍认为诊断输出会发送到stderr
.事实上,我们已经有两篇关于这个主题的优秀文章:
watch
在strace
你的例子中遵循惯例,仅此而已。本身没有要求指定2>/dev/null
隐藏此类命令的输出。
例如,如果您确实想为作为参数传递的命令指定重定向,strace
则需要有一个 shell 围绕它。例如,
strace -f bash -c 'stat /etc/passwd nonexisting 2>/dev/null'
请注意标志的使用-f
,因为如果您的重点是跟踪应用于stat
命令的系统调用,那么如果没有标志,您可能看不到它们-f
。