为什么 BASH 进程替换不适用于某些命令?

Lot*_*ney 32 linux command-line shell bash process-substitution

有时,过程替换不会按预期工作。下面是一个例子:

输入:

gcc <(echo 'int main(){return 0;}')
Run Code Online (Sandbox Code Playgroud)

输出:

/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)

输入:

但是当与不同的命令一起使用时,它会按预期工作:

grep main <(echo 'int main(){return 0;}')
Run Code Online (Sandbox Code Playgroud)

输出:

int main(){return 0;}
Run Code Online (Sandbox Code Playgroud)

我注意到与其他命令类似的失败(即期望来自进程替换的文件的命令不能使用/dev/fd/63或类似)。这次失败gcc只是最近的一次。是否有一些我应该注意的一般规则来确定进程替换何时会以这种方式失败并且不应该使用?

我在 Ubuntu 12.04 上使用这个 BASH 版本(我也在 arch 和 debian 中看到过这个):
GNU bash,版本 4.3.11(1)-release (i686-pc-linux-gnu)

Cel*_*ada 37

进程替换会生成一个特殊文件(如/dev/fd/63您的示例中所示),其行为类似于命名管道的读取端。该文件可以打开和读取,但不能写入,也不能搜索。

将参数视为纯流的命令可以工作,而期望在给定(或写入)文件中查找的命令将不起作用。可以工作的命令类型通常被认为是过滤器:cat, grep, sed, gzip, awk, 等等... 一个不能工作的命令的例子是类似编辑器vi或类似的文件操作mv

gcc希望能够对其输入文件执行随机访问以检测它们是用什么语言编写的。 如果您提供gcc有关输入文件语言的提示,则很高兴流式传输文件:

gcc -x c <(echo 'int main(){return 0;}')
Run Code Online (Sandbox Code Playgroud)

没有进程替换的更简单更直接的形式也适用:

echo 'int main(){return 0;}' | gcc -x c -
Run Code Online (Sandbox Code Playgroud)

请注意,这不是特定于bash. 所有支持进程替换的 shell 的行为方式都相同。

  • 请注意,`zsh` 支持使用临时文件的第 3 种进程替换形式:`gcc =(echo 'int main(){return 0;}')` (10认同)
  • @terndon 肯定 `&lt;()` 会生成一个文件 *name*(该构造在我的系统上扩展为 `/proc/self/fd/something`)。这个名字,当打开时,就像一个命名管道(`S_IFIFO`)的读端,而不是一个支持`read()`和其他但不支持`seek()`的常规文件(`S_IFREG`)。 (5认同)
  • GCC 不会“对其输入文件执行随机访问以检测它们是用什么语言编写的”。它只查看文件名的扩展名。如果文件名没有扩展名(或者如果它有一个无法识别的扩展名),GCC 会假定该文件是一个目标文件或链接描述文件,并将其传递给 `ld`(它会检测目标格式)。`-x` 不是提示;这是一个声明。如果您指定`-x f95`,GCC 将把文件传递给 Fortran-95 编译器,而不管其名称或内容如何。见 https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Overall-Options.html (5认同)