我正在尝试使用文件描述符来更好地理解它们,但我无法理解以下内容。
$ grep "..." 3>&1 1>/dev/null
1
12
13
123
321
3
上面的内容没有在 shell 中显示任何匹配项,这显然是发生的,因为我正在重定向到 /dev/null。我不明白的是为什么3>&1
不这样做,这样我仍然可以看到输出,因为我在 fd 3 中复制了它。
我缺少什么?
答案1
您的重定向做使 FD 3 成为 FD 1 的副本,因此 FD 3 将指向标准输出和任何内容写给它将转到(默认情况下)TTY。然后将 FD 1 重定向到/dev/null
,因此写入 FD 1 的任何内容都将被丢弃。由于grep
从未写入 FD 3,因此不会发生任何可见的情况。
这些按顺序发生:首先是一个副本(dup2
FD 1 的 ) 被指向 FD 3,因此 FD 3 此后将指向 FD 1 当前所指向的内容,其次 FD 1 会被指向 的指针替换/dev/null
。
最终结果如图所示如下图:
标准错误(粉红色,FD 2)和 FD 3 发送至 TTY,标准输出发送至/dev/null
. FD 3 仍然指向 TTY,因为这是制作副本时 FD 1 所指向的位置。然而,该grep
命令不会尝试向 FD 3 写入任何内容,因此该副本永远不会有任何用途。
“复制”是定向的:3>&1
处理后,写入 FD 3 的任何内容都将转到处理时 FD 1 所指向的位置。它不会“保留”除此之外的任何内容:如果您随后重定向 FD 1,则写入其中的任何内容都会转到新位置。您所做的操作将保留在 FD 1 的原始目的地,以备您以后想要使用它。影响grep
标准输出最终位置的唯一重定向是1>...
明确谈论它的去向的重定向。
如果grep
是写入 FD 3,它将按预期出现在终端中。因为它通常只输出到 FD 1,所以它的所有实际输出都被丢弃。
如果我们愿意,我们可以制作一个 grep 输出到 FD 3:
( grep "..." >&3 )
这将采用 FD 1 上的常规输出并将其指向(新创建的)FD 3。如果你直接运行它,这是行不通的,因为 FD 3 不会去任何地方,但我们可以将它合并到类似这样的东西中来利用它:
( grep "..." >&3 ) 3>&1 1>/dev/null
括号中的命令输出到 FD 3。之后的重定向 1) 将 FD 3(现在实际上有内容)指向 FD 1,2) 然后再次将 FD 1 引导走(这没有效果)。最终结果是,您将grep "..."
再次将输出输出到标准输出,这正是它本来应该在的位置,无需大惊小怪。
这种重定向的实际用途是就像是
cmd 3>&1 1>&2 2>&3 3>&- | foo
它交换 FD 1 和 2,使用 FD 3 作为临时存储点。有时它也用于 shell 脚本技巧,例如在 POSIX sh 中伪造进程替换( cmd1 | ( cmd2 | ( main_command /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )
。否则,命令本身按编号使用任何非标准文件描述符的情况相当罕见(当然,许多命令自己打开文件并获取一个)。
答案2
您正在重定向文件描述符3到文件描述符1,并且您将 1 重定向到 /dev/null。你没有复制任何东西。
正常的方法是复制文件描述符带有tee
.
另外,我不知道你的上下文是什么,但 3 不是内置文件描述符。想必您已经将其设置为指向某些内容?