>&- 在 unix / linux 终端中做什么?

>&- 在 unix / linux 终端中做什么?

我对 Unix 命令行不太了解。我发现这个答案要使用tee命令而不输出到标准输出,请通过“关闭标准输出”,如下>&-所示:

echo 'hello world' | tee aa bb cc >&-

我做了很多研究并发现另一个帖子询问操作员的功能。不幸的是,我找不到任何详细的答案。

我所知道的是它关闭了stdout,但我无法确定它的确切含义以及为什么有人会特别想要这样做。我很想知道这在其他哪些场景中可能有用。

是否有任何资源可以让我更多地了解此类操作员的内部运作?

答案1

引用sh该语言的 POSIX 规范:

2.7.6 复制输出文件描述符

重定向运算符:

[n]>&word

应从另一个输出文件描述符复制一个输出文件描述符,或将关闭一个。如果 word 的计算结果为一位或多位数字,则 n 表示的文件描述符或标准输出(如果未指定 n)应成为 word 表示的文件描述符的副本;如果 word 中的数字不代表已打开用于输出的文件描述符,则会导致重定向错误;请参阅 Shell 错误的后果。如果 word 的计算结果为“-”,则关闭文件描述符 n 或标准输出(如果未指定 n)。尝试关闭未打开的文件描述符不应构成错误。如果 word 的计算结果为其他内容,则行为未指定。

(强调我的)。

So>&-1>&-1 的缩写,1 是 stdout 的文件描述符。

所以在:

cmd >&-

shellclose(1)在稍后执行的进程中执行此操作cmd。因此,cmd它的 fd 0 和 2 可能会在启动时打开(因为它们分别是 stdin 和 stderr,它们像 stdout 一样通常并且按照惯例总是在某个东西上,因为这是命令期望默认情况下接受输入并默认发送错误的地方),但FD 1关闭了。文件描述符 3 及以上也可能被关闭。

这意味着如果cmd打开了某个文件(如open("some-file", O_WRONLY, 0600)),它将在第一个空闲文件描述符上打开,并且 fd 将为 1!这意味着这some-file将成为其标准输出随后的位置。例如,如果printf("Please enter your name: ")稍后发生,则会进入some-file

这曾经是(几十年来)利用 setuid 可执行文件的一种方法。

例如,chshsetuid 可执行文件可/etc/passwd根据用户的请求进行编辑以更改用户的登录 shell。

和:

chsh >&-

chsh最终可能会/etc/passwd在 fd 1 上打开 a 并将其打算发送给用户的消息写在那里。

意识到这个问题后,软件开始通过在启动时检查 fd 0、1 或 2 是否关闭来防止这种情况,如果是,/dev/null则打开它们。

我似乎记得这就是 GNU libc(GNU 系统上的任何可执行文件使用的)在检测到它至少作为 setuid 可执行文件或其他敏感上下文运行时所做的事情,但我现在无法验证它,所以要么我记错了,要么他们改变了行为。

无论如何,如果你tee现在查看 GNU 的源代码,在打开文件进行写入时,它使用fopen_safer()gnulib(不是 GNU libc)的变体fopen(),如果使用的 fd fopen()<= 2,则将其移动到高于 2 的 fd。

因此,在 中,至少tee aa bb cc >&-通过该实现,将在 2 以上的第一个 fd(可能是 3)上打开,在下一个 fd 上打开,依此类推,并且 1 保持关闭,所以我们看到:teeaabb

$ echo test | tee aa bb cc >&-
tee: 'standard output': Bad file descriptor

tee当 fd 1 (stdout) 关闭时无法写入时,Where会报告错误。

对于tee来自 busybox 的,它不使用fopen_safer()来自 gnulib 的,

$ echo test | busybox tee aa bb cc >&-
$ cat aa
test
test

aa实际上是在 fd 1 上,这解释了为什么test在其中写入了两次:一次是因为它是在 stdout(fd 1)上写入的,另一次是因为它被写入了打开的 fd aa(也是 fd 1)。

我希望它清楚地表明,如果您想丢弃tee的标准输出(或任何命令),您应该不是关闭它,但将其重定向到 /dev/null :

echo test | tee aa bb cc > /dev/null

不过,在这里,您可以这样做,而不是丢弃它:

echo test | tee aa bb > cc
echo test | tee aa bb cc >&-

仅适用于那些tee检测到 stdout 已关闭并自行在 /dev/null 上重新打开它的实现,作为一种保护措施,如上所述,这既不是 GNUtee也不是 busybox的情况tee(至少在链接到当前版本的 GNU 时)库)。

您可能想要关闭 stdout(或 stdin 或 stderr)的一些原因:

  • 利用 setuid/setgid 或更一般的软件,这些软件在执行时会提升其特权,但在这些病态情况下表现不佳。

  • 测试您的软件以检查它在这些情况下表现正常。

  • in zshcmd1 > file | cmd2将 的输出发送cmd1到两者file并通过管道发送到cmd2(类似于cmd1 | tee file | cmd2除了teeing 是在内部完成并且更灵活。

    cmd1如果您想将stderr 发送到cmd2 并将 stdout 发送到 ,这会妨碍您将 stderr 发送到file但将 stdout 发送到和。cmd1 2>&1 > file | cmd2cmd2filecmd2

    tee可以通过执行以下操作来禁用该ing:

    cmd1 2>&1 >&- > file | cmd2
    

    虽然你也可以这样做:

    { cmd1 2>&1 > file; } | cmd2
    

    或者

    cmd1 > file 2> >(cmd2)
    

1<&-就此而言,>&-和之间的唯一区别<&-是它们在未指定时操作的 fd(分别为 1 和 0)。他们都只是做了一个close(fd)

相关内容