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

One*_*n69 9 command-line shell terminal command

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

echo 'hello world' | tee aa bb cc >&-
Run Code Online (Sandbox Code Playgroud)

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

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

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

Sté*_*las 11

引用sh语言的 POSIX 规范:

\n
\n

2.7.6 复制输出文件描述符

\n

重定向运算符:

\n

[n]>&word

\n

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

\n
\n

(强调我的)。

\n

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

\n

所以在:

\n
cmd >&-\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n

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

\n

和:

\n
chsh >&-\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n

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

\n

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

\n

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

\n
$ echo test | tee aa bb cc >&-\ntee: \'standard output\': Bad file descriptor\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

对于teebusybox,它不使用fopen_safer()gnulib 中的功能,

\n
$ echo test | busybox tee aa bb cc >&-\n$ cat aa\ntest\ntest\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
echo test | tee aa bb cc > /dev/null\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
echo test | tee aa bb > cc\n
Run Code Online (Sandbox Code Playgroud)\n
\n
echo test | tee aa bb cc >&-\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n

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

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

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

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

    \n

    如果您想将cmd1stderr 发送到cmd2stderr 并将 stdout 发送到 ,file那么这cmd1 2>&1 > file | cmd2会妨碍您将 stderr 发送到cmd2但 stdout 发送到filecmd2

    \n

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

    \n
    cmd1 2>&1 >&- > file | cmd2\n
    Run Code Online (Sandbox Code Playgroud)\n

    虽然你也可以这样做:

    \n
    { cmd1 2>&1 > file; } | cmd2\n
    Run Code Online (Sandbox Code Playgroud)\n

    或者

    \n
    cmd1 > file 2> >(cmd2)\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n
\n

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

\n