在这个答案在最底部,Gilles 提到一个命令可以有多个输出或输入。
是的,有cat foo bar | something
, 用于将foo
和bar
作为输入,还有tee
用于输出;但这似乎并不是他真正所说的。
一个程序如何可以有多个输入或输出?
答案1
这个cat foo bar
例子不是我的意思。这里cat
一次只有一个输入和一个输出。
tee
是一个例子:它输出到所有参数,同时加上它的标准输出。使用与中相同类型的 ASCII 艺术图我之前的回答,这是tee foo bar
它在终端中运行时的样子。
+------------------+
| tee |
===|<stdin | +------------+
→ | | | terminal |
| stdout>|=========|<input |
| | → ##==|< |
| | || +------------+
| stderr>|=====##
| | →
| | +-------------+
| 3>|=======|> file "foo" |
| | → +-------------+
| | +-------------+
| 4>|=======|> file "bar" |
| | → +-------------+
| |
+------------------+
在此示例中,tee
将“有用”输出发送到三个通道:发送到终端(因为这是其标准输出连接到的位置)和两个文件。此外,tee
还多了一个错误输出通道。
一个程序通常具有三个输入/输出通道,由它们的标识文件描述符数字:
- 标准输入(简称stdin,文件描述符号0);
- 标准输出(简称stdout,文件描述符号1);
- 标准错误(简称stderr,文件描述符编号2)。
文件描述符 0、1 和 2 的目的只是一个约定问题——没有什么强制程序不能尝试写入文件描述符 0 或从描述符 1 和 2 读取——但这是一个几乎普遍遵循的约定。
如果从终端运行程序,文件描述符 0、1 和 2 开始时会连接到该终端,除非它们已被重定向。其他文件描述符一开始是关闭的,并且在程序打开其他文件时将被使用。
特别是,所有命令都有两个输出:标准输出(对于命令的有效负载,“有用”输出)和标准错误(对于错误或信息性消息)。
shell ( command1 | command2 | command3 | …
) 中的管道将每个命令的标准输出连接到下一个命令的标准输入。所有命令的标准错误都会发送到终端(除非重定向)。
Shell 提供了重定向其他文件描述符的方法。您可能遇到过2>&1
或2>file
重定向标准错误。看
什么时候会使用额外的文件描述符?另一个帖子链接到其他文件描述符的操作示例。
功能丰富的 shell 还提供流程替代将文件重定向推广到管道命令,这样您就不会局限于每个命令具有单个输入和单个输出的线性管道。
很少有命令尝试访问大于 2 的文件描述符,除非它们打开了文件(打开文件会选择一个空闲文件描述符并将其编号返回给应用程序)。 GnuPG 就是一个例子,它期望读取数据以在其标准输入上进行加密/解密/签名/验证,并将结果写入标准输出。可以使用该选项告诉它读取不同文件描述符上的密码--passphrase-fd
。 GnuPG 还具有报告其他文件描述符上的状态数据的选项,因此您可以在 stdout 上输出有效负载,在 stderr 上输出错误消息,并在另一个文件描述符上获得状态信息。下面是一个使用管道命令的输出作为密码的示例:
echo fjbeqsvfu | rot13 | gpg -d --passphrase-fd=3 3<&0 <file.encrypted >file.plaintext
答案2
命令可以有多个输出流,我并不是指写入文件或套接字。考虑大多数 GNU 工具(例如grep
)将错误打印到 stderr 并将预期输出打印到 stdout。虽然在交互式 shell 中两者都会合并 (2>&1),但您仍然可以单独处理它们。它并没有结束,因为如果程序或代码块支持,您可以使用额外的文件描述符。
人为的例子:
{
grep NORMAL log.txt
grep WARN log.txt 1>&3
grep ERROR log.txt 1>&4
} 1> normals.txt 3> warnings.txt 4> errors.txt # 2>/dev/null
答案3
是的。例如,cat foo bar | less
给出两个输入(文件 foo 和文件 bar)并将它们都输出到 less。vim foo*
会将所有以 foo 开头的文件输出到 vim 中。查看每个文件后,您可以使用 :n (如果您更改了任何内容,则使用 :wn )切换到下一个输出。我认为吉尔斯解释得很好。如果您使用管道 ( | ),它将获取一个命令的输出,将其输入到另一命令中,然后输出结果。这是多个输出的另一个例子。