理解这个命令中的重定向吗?

理解这个命令中的重定向吗?

我发现了一个神奇的命令这里

./my.sh 3>all 1> >(tee out >&3) 2> >(tee err >&3)

有些地方我很困惑:

  1. 是否3>all意味着为 file 设置文件描述符 3 all
  2. 做什么1> >2> >?根据我的理解,命令应该是./my.sh 3>all 1>(tee out >&3) 2>(tee err >&3).但这是行不通的。
  3. 为什么不(tee err >&3)覆盖文件all

这是我的my.sh

#!/bin/bash

echo myecho
ls dflj

答案1

您可以在 bash 手册页中阅读有关此语法的信息工艺替代

>(list)。过程列表运行时其输出连接到 /dev/fd 中的某个文件。该文件的名称作为扩展结果作为参数传递给当前命令。

查看此命令的输出,它不执行任何重定向:

echo >(echo hi >/tmp/a) >(echo lo >/tmp/b)

它是(在我的系统上):

/dev/fd/63 /dev/fd/62

所以你必须读1> >(...)1>>(...)。第二部分被替换为/dev/fd/63,然后我们1> /dev/fd/63将 stdout 重定向到文件描述符 63。

bash 在单独的进程中运行命令>(...),并将该进程的标准输入连接到文件描述符 63。查看以下示例:

set -x
echo hello > >(cat -n)

回声的标准输出通过管道传输到的输入中cat -n,您将得到:

+ echo hello
++ cat -n
 1  hello

也许您所缺少的是,当您拥有文件描述符(fd) 为一个文件,然后进程(bash正在使用的进程>(...)),您可以在新进程中继承相同的fd。所以这2个进程共享同一个fd。而且,只有一个文件偏移量对于一个fd,因此如果进程1向fd写入3个字符,则偏移量从0移动到3。如果进程2然后向fd写入5个字符,则数据被放置在偏移量3处,并且偏移量变为8。如果进程2 1 写入另一个字符,并将其放置在偏移量 8 处,依此类推。这就是您问题中的两个tee命令如何设法写入同一文件all而不互相覆盖的方式。

使用>&3不会创建新的fd;它只是关闭当前的 stdout fd 1,然后将 fd 3 重新编号为 fd 1。因此,即使每个进程现在看到的编号不同(请man dup2参阅底层系统调用),这两个进程仍然只有一个 fd 。

答案2

为了尝试比 meuh 伟大的理论答案更字面地解释 - 正如您可能知道的那样,有许多默认文件描述符:

  • 0代表标准输入
  • 1代表标准输出
  • 2代表标准错误

您的命令执行的操作如下:

  • 3>all打开一个指向文件的新文件描述符all
  • 1> >(tee out >&3)将 stdout ( 1) 重定向到 tee 命令打开并返回的文件描述符,如 meuh 所解释的
    • tee out >&3将其输入(在本例中为脚本中的标准输出)重定向到名为 out 的文件,以及文件描述符3指向的任何位置(在本例中为文件 all)
  • 2> >(tee err >&3)将 stderr ( 2) 重定向到 tee 命令打开并返回的文件描述符,如 meuh 所解释的
    • tee err >&3将其输入(在本例中为脚本中的 stderr)重定向到名为 err 的文件,以及文件描述符3指向的任何位置(在本例中为文件 all)

>>从您的评论来看,我认为让您感到困惑的是,如果您想将输出附加到文件,您希望需要使用重定向运算符。

这里的情况并非如此,因为您实际上所做的就是将 stdout 和 stderr 连接到指向文件 all 的文件描述符。

效果和这样做是一样的:

./my.sh > all 2>&1

首先将 stdout 重定向到文件 all,然后将 stderr 重定向到 stdout 指向的任何位置。

相关内容