我发现了一个神奇的命令这里
./my.sh 3>all 1> >(tee out >&3) 2> >(tee err >&3)
有些地方我很困惑:
- 是否
3>all
意味着为 file 设置文件描述符 3all
? - 做什么
1> >
和2> >
?根据我的理解,命令应该是./my.sh 3>all 1>(tee out >&3) 2>(tee err >&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 指向的任何位置。