我对 bash 很熟悉,但是最近我遇到了一个我不知道的替换。
bash 中到底有什么<(command)
?它与 zsh 中的相比如何=(command)
?
我理解这与默认文件描述符有关。在我的电脑中
echo <()
返回/proc/self/fd/11
,我发现它是脚本 STDOUT 的副本,但这对我来说仍然相当令人困惑。
答案1
这称为进程替代。
<(list)
两者都支持该语法,bash
和zsh
。当无法list
使用管道 ( ) 时,它提供了一种将命令 ( ) 的输出传递给另一个命令的方法。例如,当一个命令不支持输入或您需要多个命令的输出时:|
STDIN
diff <(ls dirA) <(ls dirB)
<(list)
如果系统支持,则将的输出list
与 中的文件连接起来/dev/fd
,否则将使用命名管道 (FIFO)(这也取决于系统是否支持;两本手册都没有说明如果不支持这两种机制会发生什么,大概它会因错误而中止)。然后,文件的名称作为命令行上的参数传递。
zsh
另外还支持=(list)
作为的可能替代品<(list)
。=(list)
使用临时文件代替文件/dev/fd
或 FIFO。<(list)
如果程序需要在输出中使用 lseek,它可以用作的替代品。
根据ZSH 手册工作方式上可能还存在其他问题<(list)
:
这种
=
形式很有用,因为 和/dev/fd
的命名管道实现都有<(...)
缺点。在前一种情况下,某些程序可能会在检查命令行上的文件之前自动关闭相关文件描述符,特别是当出于安全原因(例如当程序运行 setuid 时)需要这样做时。在第二种情况下,如果程序实际上没有打开文件,则尝试读取或写入管道的子 shell 将(在典型的实现中,不同的操作系统可能有不同的行为)永远阻塞,必须明确终止。在这两种情况下,shell 实际上都使用管道提供信息,因此期望对lseek(2)
文件进行 lseek(参见手册页)的程序将无法工作。
答案2
注意,这是一个 bash 答案,而不是 zsh。
在 bash 中有些情况不能使用管道:
some_command | some_other_command
因为管道为管道的每个组件引入了子 shell,所以当子 shell 退出时,你所依赖的任何副作用都会消失。例如,这个人为的例子:
cat file | while read line; do ((count++)); done
echo $count
将显示一个空白行,因为该$count
变量在当前shell中不存在。
一场盛宴流程替代允许您像从文件中读取一样从“some_command”输出中读取,从而避免这个难题
while read line; do ((count++)); done < <(cat file)
# ....................................1.2
echo $count # the variable *does* exist in the current shell
(1)是正常的输入重定向。 (2)是进程替换语法的开始<()
。
答案3
=(command)
zsh 中& 的另一个区别<(command)
是同步与异步执行:
date & \
diff =(sleep 4; date) =(sleep 5; date) & \
diff <(sleep 4; date) <(sleep 5; date) &
Sun 29 Nov 2020 08:16:01 AEDT
[1] 41717 done date
$ 1c1
< Sun 29 Nov 2020 08:16:05 AEDT
---
> Sun 29 Nov 2020 08:16:06 AEDT
[3] + 41719 exit 1 diff <(sleep 4; date) <(sleep 5; date)
$ 1c1
< Sun 29 Nov 2020 08:16:05 AEDT
---
> Sun 29 Nov 2020 08:16:10 AEDT
[2] + 41718 exit 1 diff =(sleep 4; date) =(sleep 5; date)
... 虽然不知道这是否是设计使然,因为我无法想到一个令人信服的理由来=()
同步运行。