我将使用这个问题grep,因为它的用法文本打印到stderr
$ grep
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.
Run Code Online (Sandbox Code Playgroud)
您可以使用流程替换轻松捕获stdout
$ read b < <(echo hello world)
Run Code Online (Sandbox Code Playgroud)
然而,stderr滑过进程替换并打印到控制台
$ read b < <(grep)
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.
Run Code Online (Sandbox Code Playgroud)
我想使用进程替换来捕获stderr.我现在正在使用它
$ grep 2> log.txt
$ read b < log.txt
Run Code Online (Sandbox Code Playgroud)
但我希望避免临时文件.
当我在Bash中发出两个等效命令时,我得到了不同的输出(来自"wc -l"命令),见下文:
root@devel:~# ls /usr/bin -lha | tee >(wc -l) >(head) > /dev/null
total 76M
drwxr-xr-x 2 root root 20K Nov 11 18:58 .
drwxr-xr-x 10 root root 4.0K Oct 8 15:31 ..
-rwxr-xr-x 1 root root 51K Feb 22 2017 [
-rwxr-xr-x 1 root root 96 Jan 19 2017 2to3-3.5
-rwxr-xr-x 1 root root 23K Mar 22 2017 addpart
lrwxrwxrwx 1 root root 26 May 10 2017 addr2line -> x86_64-linux-gnu- addr2line
lrwxrwxrwx 1 root root 6 Dec 13 2016 …Run Code Online (Sandbox Code Playgroud) 我正在尝试将进程替换用于程序的输入文件,但它无法正常工作.是因为某些程序不允许输入文件的进程替换吗?
以下不起作用:
bash -c "cat meaningless_name"
>sequence1
gattacagattacagattacagattacagattacagattacagattacagattaca
>sequence2
gattacagattacagattacagattacagattacagattacagattacagattaca
bash -c "clustalw -align -infile=<(cat meaningless_name) -outfile=output_alignment.aln -newtree=output_tree.dnd"
(Less verbose output, finishing with:
No sequences in file. No alignment!
Run Code Online (Sandbox Code Playgroud)
但以下控件确实有效:
bash -c "clustalw -align -infile=meaningless_name -outfile=output_alignment.aln -newtree=output_tree.dnd"
(Verbose output, finishing with:
CLUSTAL-Alignment file created [output_alignment.aln]
bash -c "cat <(cat meaningless_name) > meaningless_name2"
diff meaningless_name meaningless_name2
(No output: the two files are the same)
bash -c "clustalw -align -infile=meaningless_name2 -outfile=output_alignment.aln -newtree=output_tree.dnd"
(Verbose output, finishing with:
CLUSTAL-Alignment file created [output_alignment.aln]
Run Code Online (Sandbox Code Playgroud)
这表明进程替换本身有效,但clustalw程序本身不喜欢进程替换 - …
如果我跑
$#/bin/bash
for i in `seq 5`; do
exec 3> >(sed -e "s/^/$i: /"; echo "$i-")
echo foo >&3
echo bar >&3
exec 3>&-
done
Run Code Online (Sandbox Code Playgroud)
那么结果是不同步的;它可能是这样的:
1: foo
1: bar
2: foo
2: bar
1-
3: foo
3: bar
2-
3-
4: foo
5: foo
4: bar
5: bar
4-
5-
Run Code Online (Sandbox Code Playgroud)
>(...)在进行下一次迭代之前,如何确保流程替换已完成?
sleep 0.1在exec 3>&-帮助之后插入,但它不优雅,效率低下,并且不能保证总是有效。
编辑:这个例子可能看起来很傻,但它只是为了说明。我正在做的是在循环中读取输入流,将每一行提供给一个在循环中偶尔会改变的进程。在代码中更容易解释:
# again, simplified for illustration
while IFS= read line; do
case $line in
@*)
exec 3>&-
filename=${line:1}
echo "starting $filename" …Run Code Online (Sandbox Code Playgroud) 我有一个bash脚本,我想在标准输出中与用户通信,但也通过文件描述符将命令发送到子进程 - 如下所示:
# ...
# ...
echo "Hello user, behold a cleared gnuplot window"
# pass the string "clear" to gnuplot via file descriptor 3
echo "clear" >&3
Run Code Online (Sandbox Code Playgroud)
所以我认为我可以通过首先启动子进程来"设置它":
#!/bin/bash
# Initiate(?) file descriptor 3, and let it direct to a newly
# started gnuplot process:
exec >3 >( gnuplot )
Run Code Online (Sandbox Code Playgroud)
但这会产生错误:
/dev/fd/63: Permission denied
Run Code Online (Sandbox Code Playgroud)
这是预期的吗?
我不明白发生了什么.(我做错了什么?可能是我的系统有一些特殊的安全设置,不允许我正在尝试做什么?(运行Ubuntu Linux 12.10.))
"解决方法" - 以下似乎与我正在尝试的内容相同,并且可以正常工作:
#!/bin/bash
# open fd 3 and direct to where fd 1 directs to, i.e. std-out
exec 3>&1 …Run Code Online (Sandbox Code Playgroud) 假设我有一个程序输出:
abcd
l33t
1234
Run Code Online (Sandbox Code Playgroud)
我会用它来模拟printf 'abcd\nl33t\n1234\n'.我想同时将这个输出提供给两个程序.我的想法是使用进程替换tee.假设我想将输出的副本提供给grep:
printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]' >&2) | grep '[0-9]'
Run Code Online (Sandbox Code Playgroud)
我使用Bash 4.1.2(Linux,CentOS 6.5)得到以下内容,这很好:
l33t
1234
abcd
l33t
Run Code Online (Sandbox Code Playgroud)
但是如果进程替换没有被重定向到stderr(即没有>&2),就像这样:
printf 'abcd\nl33t\n1234\n' | tee >(grep '[a-z]') | grep '[0-9]'
Run Code Online (Sandbox Code Playgroud)
然后我得到:
l33t
1234
l33t
Run Code Online (Sandbox Code Playgroud)
这就像进程替换的stdout(第一个grep)被管道(第二个grep)之后的进程使用.除了第二个grep已经自己读取东西,所以我想它不应该考虑第一个grep中的东西.除非我弄错了(我肯定是).
我错过了什么?
据我所知,进程替换 <(...) / >(...) 创建了 fd
并将括号中的命令输出存储到生成的 fd 中。
因此,这两个命令是等价的
$ ls -al
$ cat <(ls -al)
Run Code Online (Sandbox Code Playgroud)
在这里,我的问题是,生成的文件描述符保留多长时间?
我读过这篇文章,但似乎我的理解是错误的。
如果进程替换被扩展为函数的参数,在调用函数期间扩展为环境变量,或扩展为函数内的任何赋值,则进程替换将“保持打开”以供函数内的任何命令使用或者它的被调用者,直到设置它的函数返回。如果在被调用者中再次设置相同的变量,除非新变量是本地变量,否则先前的进程替换将关闭并且在被调用者返回时对调用者不可用。
本质上,在函数内扩展为变量的进程替换保持打开状态,直到发生进程替换的函数返回——即使分配给由函数调用者设置的局部变量。动态范围不会保护它们免于关闭。
我最好的猜测是,在阅读之后,创建的 fd 在被使用之前不会被关闭。
由此,我写了一个非常愚蠢的代码,如下所示
#!/bin/bash
test_subs () {
echo "Inside a function"
FD2=<(ls -al)
cat $FD1
cat $FD2
}
FD1=<(ls -al)
test_subs
Result======================================
Inside a function
cat: /dev/fd/63: No such file or directory
cat: /dev/fd/63: No such file or directory
Run Code Online (Sandbox Code Playgroud)
似乎新打开的 fd 在一行命令运行后立即关闭。
生成的fd维持多久,那么进程替换的范围是什么?
出于某种原因,似乎stderr在以下bash脚本中被发送到stdout:
exec > >( while read line; do echo " stdout: $line"; done )
exec 2> >( while read line; do echo " stderr: $line"; done )
echo "rolo"
echo "cholo" >&2
Run Code Online (Sandbox Code Playgroud)
如果你运行它,输出是这样的:
stdout:rolo
stdout:stderr:cholo
有谁知道为什么会这样?据我所知,发生的事情是stderr被发送到stdout,这就是为什么第一行是从第二行捕获输出的原因?
假设我有一个大的gzip压缩文件data.txt.gz,但通常需要将ungzipped版本提供给程序.当然,不是创建独立解压缩,而是data.txt可以使用进程替换语法:
./program <(zcat data.txt.gz)
但是,根据具体情况,这可能很烦人且容易出错.
有没有办法模拟命名的进程替换?也就是说,创建一个伪文件data.txt,zcat data.txt.gz只要访问它就会"展开"到进程替换中.与符号链接不同,将读取操作转发到另一个文件,但在这种情况下,它需要是临时命名管道.
谢谢.
PS.有点类似的问题
我有一个任意字符串数组,例如a=(1st "2nd string" $'3rd\nstring\n' ...).
我想将这些字符串传递给将其参数解释为文件的命令,例如paste.
对于固定数量的变量,我们可以使用流程替换
paste <(printf %s "$var1") <(printf %s "$var2") <(printf %s "$var3")
Run Code Online (Sandbox Code Playgroud)
但这只有在事先知道变量数量的情况下才有效。
对于数组a,我们可以写一些相当安全的东西,比如
eval paste $(printf '<(printf %%s %q) ' "${a[@]}")
Run Code Online (Sandbox Code Playgroud)
出于兴趣:有没有办法在a不使用的情况下处理替换每个条目eval?请记住,a的条目可以包含任何字符(除了\0因为bash不支持它)。