我想知道如何理解以下内容:
将命令的标准输出通过管道传输到另一个命令的标准输入是一种强大的技术。但是,如果您需要通过管道传输多个命令的标准输出怎么办?这就是过程替换的用武之地。
换句话说,进程替换可以做任何管道可以做的事情吗?
进程替换可以做什么,而管道不能?
我试图在这个特定示例的上下文中理解命名管道。
我输入<(ls -l)我的终端并得到输出,bash: /dev/fd/63: Permission denied.
如果我输入cat <(ls -l),我可以看到目录内容。如果我更换cat用echo,我觉得我得到的终端名称(或者是什么呢?)。
echo <(ls -l)给出输出为/dev/fd/63。
此外,这个示例输出我不清楚。
ls -l <(echo "Whatever")
lr-x------ 1 root root 64 Sep 17 13:18 /dev/fd/63 -> pipe:[48078752]
Run Code Online (Sandbox Code Playgroud)
但是,如果我给出,ls -l <()它会列出目录内容。
在命名管道的情况下会发生什么?
一些 shell,如bash,支持进程替换,这是一种将进程输出呈现为文件的方法,如下所示:
$ diff <(sort file1) <(sort file2)
Run Code Online (Sandbox Code Playgroud)
但是,此构造不是POSIX,因此不可移植。如何处理替代的实现POSIX -友好的方式(即其中一个在工作/bin/sh)?
注意:问题不是问如何区分两个排序的文件——这只是一个人为的例子来演示进程替换!
有时,过程替换不会按预期工作。下面是一个例子:
输入:
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)
今天我通过这篇文章学习了一些关于 fifo 的东西:命名管道简介,其中提到了cat <(ls -l).
我使用 做了一些实验sort < (ls -l),它弹出一个错误:
-bash: syntax error near unexpected token `('`
Run Code Online (Sandbox Code Playgroud)
然后我发现我在命令中错误添加了一个额外的空格。
但是,为什么这个额外的命令会导致这个失败呢?为什么重定向符号必须靠近(?
我希望我的 shell 脚本在使用它们执行的命令失败时失败。
通常我这样做:
set -e
set -o pipefail
Run Code Online (Sandbox Code Playgroud)
(通常我set -u也会添加)
问题是上述方法都不适用于进程替换。此代码打印“ok”并以返回代码 = 0 退出,而我希望它失败:
#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)
Run Code Online (Sandbox Code Playgroud)
除了进程替换之外,是否有任何等同于“pipefail”的东西?有没有其他方法可以将命令的输出作为文件传递给命令,但只要这些程序中的任何一个失败就会引发错误?
一个穷人的解决方案是检测这些命令是否写入 stderr(但某些命令在成功的情况下写入 stderr)。
另一个更符合 posix 的解决方案是使用命名管道,但我需要将那些使用进程替换的命令作为从编译代码动态构建的单行程序启动,并且创建命名管道会使事情复杂化(额外的命令,捕获错误删除它们等)
在 中bash,我可以使用进程替换并将进程的输出视为保存在磁盘上的文件:
$ echo <(ls)
/dev/fd/63
$ ls -lAhF <(ls)
lr-x------ 1 root root 64 Sep 17 12:55 /dev/fd/63 -> pipe:[1652825]
Run Code Online (Sandbox Code Playgroud)
不幸的是,dash.
Process Substitution在破折号中模拟的最佳方式是什么?
我不想将输出保存为某个地方的临时文件 ( /tmp/) 然后必须删除它。有没有替代方法?
我期望cat <(cat)并cat | cat做同样的事情:将行从标准输入复制到标准输出。我的理解是,两者都将cat在 subshell 中执行 a ,将 subshellcat的 stdout重定向到临时命名管道,然后cat在当前 shell 中执行另一个并将其 stdin 重定向到管道。
相反,cat <(cat)让我在终端上输入,但没有任何输入行被复制并且^D无法发出信号EOF;cat | cat虽然按预期工作。
作为进一步的实验,我检查了是否cat =(cat)有与 类似的困难cat <(cat),但它按我的预期工作:直到 a 的所有标准输入^D都被一次性复制到标准输出。
任何人都可以帮助我了解 zsh 在幕后做了什么?
我正在尝试针对几百个文件的列表运行 grep:
$ head -n 3 <(cat files.txt)
admin.php
ajax/accept.php
ajax/add_note.php
Run Code Online (Sandbox Code Playgroud)
但是,即使我正在搜索我知道在文件中找到的字符串,以下内容也不会搜索文件:
$ grep -i 'foo' <(cat files.txt)
$ grep -i 'foo' admin.php
The foo was found
Run Code Online (Sandbox Code Playgroud)
我熟悉从文件中-f读取模式的标志。但是如何读取输入文件?
我曾考虑过将文件复制到cp似乎支持该<(cat files.txt)格式的临时目录的可怕解决方法,然后从那里 grep 文件。雪莉还有更好的办法。
当我像这样运行 Centos 7 Docker 镜像时
docker run -it centos:7 bash
Run Code Online (Sandbox Code Playgroud)
运行使用Process Substitution 的东西是没问题的(正如预期的那样,Bash 从一开始就支持 Process Substitution - 实际上是 Bash 1.4.x)。
例如:
while IFS= read -r test; do echo $test; done < <(cat anaconda-post.log)
Run Code Online (Sandbox Code Playgroud)
但是当我切换到/bin/sh时,相同的代码不再起作用
/bin/sh
while IFS= read -r test; do echo $test; done < <(cat anaconda-post.log)
sh: syntax error near unexpected token `<'
Run Code Online (Sandbox Code Playgroud)
虽然/bin/sh看起来是Bash
/bin/sh --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version …Run Code Online (Sandbox Code Playgroud)