">&1" 和 ">/proc/self/fd/1" 重定向有什么区别?

ale*_*rov 7 bash io-redirection socket

我正在处理一些rc.local在启动时运行的脚本,我注意到输出重定向的工作方式很奇怪。

如果我写了类似的东西echo "foo" >&1,它最终会出现在系统日志中,一切都很好。

但是当我写echo "foo" >>/dev/stdout, or 时echo "foo" >>/proc/self/fd/1,我收到一个错误,说没有这样的设备或地址。

进一步调查显示,这/proc/self/fd/1实际上是一个套接字。ls -l /proc/self/fdrc.local印刷品中包括以下内容:

lr-x------. 1 root root 64 Jul 14 05:28 0 -> /dev/null
lrwx------. 1 root root 64 Jul 14 05:28 1 -> socket:[18485]
lrwx------. 1 root root 64 Jul 14 05:28 2 -> socket:[18485]
lr-x------. 1 root root 64 Jul 14 05:28 255 -> /etc/rc.d/rc.local
Run Code Online (Sandbox Code Playgroud)

所描述的行为可以很容易地重现nc。首先,找出哪个文件描述符绑定到套接字。您可以通过nc -l -p 25566 -c "ls -l /proc/self/fd"在第一个终端然后telnet localhost 25566在其他终端发出来完成。就我而言,它是第 5 个描述符。

好的。然后,要重现该问题,请在第一个终端中:

nc -l -p 25566 -c "echo 'hello' >&5"
Run Code Online (Sandbox Code Playgroud)

在第二个终端:

telnet localhost 25566
Run Code Online (Sandbox Code Playgroud)

您将在有关建立和关闭连接的 telnet 消息之间的第二个终端输出中看到“hello”。

现在文件描述符伪文件的情况来自/proc

第一个终端:

nc -l -p 25566 -c "echo 'hello' >/proc/self/fd/5"
Run Code Online (Sandbox Code Playgroud)

第二个终端:

telnet localhost 25566
Run Code Online (Sandbox Code Playgroud)

现在第二个终端只包含关于连接建立和立即关闭的 telnet 消息,第一个终端显示错误:sh: /proc/self/fd/5: No such device or address

编辑:操作系统是 Fedora 23 i686 服务器


所以,问题。>&1和 和有>/proc/self/fd/1什么区别?是否有一些通用且可靠的方法将输出重定向到某个文件,这与当前的标准输出完全对应?

谢谢你。


编辑2:

为清楚起见,上述情况下 Fedora 23 i686 的准确输入/输出:

Terminal 1                                       | Terminal 2
$ nc -l -p 25566 -c 'ls -go /proc/self/fd'       | 
                                                 | $ telnet localhost 25566
                                                 | Trying ::1...
                                                 | Connected to localhost.
                                                 | Escape character is '^]'.
                                                 | total 0
                                                 | lr-x------ 1 64 Jul 14 08:56 0 -> pipe:[19687]
                                                 | l-wx------ 1 64 Jul 14 08:56 1 -> pipe:[19688]
                                                 | lrwx------ 1 64 Jul 14 08:56 2 -> /dev/tty2
                                                 | lr-x------ 1 64 Jul 14 08:56 3 -> pipe:[19687]
                                                 | lr-x------ 1 64 Jul 14 08:56 4 -> /proc/1285/fd
                                                 | lrwx------ 1 64 Jul 14 08:56 5 -> socket:[19686]
                                                 | l-wx------ 1 64 Jul 14 08:56 7 -> /pipe:[19688]
                                                 | Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >&5"              | 
                                                 | $ telnet localhost 25566
                                                 | Trying ::1...
                                                 | Connected to localhost.
                                                 | Escape character is '^]'.
                                                 | hi
                                                 | Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >/proc/self/fd/5" | 
                                                 | $ telnet localhost 25566
                                                 | Trying ::1...
                                                 | Connected to localhost.
sh: /proc/self/fd/5: No such device or address   | Escape character is '^]'.
                                                 | Connection closed by foreign host.
$                                                | $
Run Code Online (Sandbox Code Playgroud)

ilk*_*chu 8

使用>&N. 它是便携式的,正如您所见,实际上可以与套接字一起使用。

您将使用的唯一原因/proc/self/fd是您正在运行一个需要文件名的程序,并且不能被告知使用已经打开的文件描述符。例如<(cmd...)重定向使用它,因为几乎所有命令行实用程序都可以打开由名称指向的文件,但并非所有都直接支持预先打开的文件描述符。

不过,您的 shell 可以使用预先存在的文件描述符,因此无需通过/proc.


此外,/proc/NNN/fd/特定于 Linux,并要求您安装了/proc. 在我的Linux系统,/dev/stdout/dev/fd/*和其他人是符号链接/proc/self/fd/*等等,所以他们要求/proc太多。在其他 Unix 上,它们可能会有所不同。根据对一个较旧问题的回答, /dev/stdout特别将其列为 POSIX 之外。

至于为什么重定向不能像您尝试的那样工作:尝试使用strace,两者之间的区别在于,对于>&N重定向,bash调用dup()文件描述符,而>/proc/self/fd/N它只是尝试将其作为普通文件打开open()。显然proc不支持像这样打开套接字的新副本,所以调用失败。流套接字几乎是点对点链接,因此禁止打开新副本似乎并不太不自然。但是为什么它适用于管道或带有dup,我不知道。

$ ls -l /proc/self/fd/3
lrwx------ 1 itvirta itvirta 64 Jul 14 18:24 /proc/self/fd/3 -> socket:[168157]
$ strace bash -c "echo foo >>/proc/self/fd/3" 2>&1 | grep open.*proc/self
open("/proc/self/fd/3", O_WRONLY|O_CREAT|O_APPEND, 0666) = -1 ENXIO (No such device or address)
Run Code Online (Sandbox Code Playgroud)

同时这个答案有关于便携性的一些信息/proc/NNN/fd