在 bash 中你可以这样做:
echo test >&1
(重定向到标准输出,尽管它已经到那里了)echo test >&2
(重定向到stderr)echo test >&0
(重定向到标准输入)当我执行最后一个操作时,我的终端仍然像其他两个一样打印
test
,但很难知道为什么。首先,为什么这会起作用?其次,重定向到 stdin 有什么好的用途吗?
更准确地说,
>&0
所做的是将文件描述符0复制为文件描述符1。如果程序的stdin仅打开用于读取,那么当您的程序尝试写入stdout(文件描述符1)时,它将得到一个错误,因为文件描述符 1 也仅打开用于读取。
您可以通过编写一个小的 shell 脚本来检查其自己的文件描述符来演示这一点:
10156115.sh:
#!/bin/bash
bash -c 'ls -l /proc/$$/fd' >&0
然后使用可识别的 stdin、stdout 和 stderr 调用它:
$ touch stdin
$ ./10156115.sh < stdin > stdout 2> stderr
结果是您在
stderr
中得到以下结果:
ls: write error: Bad file descriptor
但是,默认情况下,这三个都是终端:(输出简化)
$ ls -l /proc/$$/fd
lrwx------ 0 -> /dev/pts/14
lrwx------ 1 -> /dev/pts/14
lrwx------ 2 -> /dev/pts/14
lrwx------ 255 -> /dev/pts/14
通常,这三个实际上都是打开读+写的,因此如果从普通 shell 单独使用,
>&0
重定向根本没有任何效果。
这个有什么用处吗?
这没有任何常见用途,但如果有人调用您的脚本重定向
stdout
和 stderr
,并且无论您出于什么原因,您都可以将其用作肮脏的黑客,以获取打印到终端的方法。无法改变这一点:
if [ ! -t /dev/fd/1 -a ! -t /dev/fd/2 -a -t /dev/fd/0 ]; then
echo "My message that I really, really want to go to a terminal" >&0
fi
但我不建议实际这样做。
由于历史原因,终端上的标准文件描述符是开放式读/写而不是只读(具体来说,它打开一次并
dup()
其他人)。这对于想要获取管道输入但也从用户读取输入(来自 stdout
,或更常见的是 stderr
)的程序有时很有用,尽管在这种情况下使用 /dev/tty
更可靠。有些系统不仅将其应用于 tty:*BSD 双向打开套接字对(“管道”),并且某些系统实用程序(我记得 ufsdump
是一个示例)依赖于此。
重定向 to
stdin
(即仅打开以进行写入)通常没有用,因为大多数程序希望它打开以进行读取(或有时是读/写,如上所述)。
是的,
>&0
有一个很好的用途。如果我想将输入和输出重定向到同一设备(例如串行端口),我可以以读写模式打开 stdin 并将 stdout 重定向到它:
rz <>/dev/ttyUSB0 >&0
它不仅可以节省我的打字时间,而且如果我在脚本中执行此操作,我会让 shellcheck 满意。