我正在尝试将信息传递给不接受来自stdin的输入的程序。为此,我使用/ dev / stdin作为参数,然后尝试输入我的输入。我注意到如果我用管道字符做这个:
[pkerp@comp ernwin]$ cat fess/structures/168d.pdb | MC-Annotate /dev/stdin
我没有输出。但是,如果我使用左插入符字符做同样的事情,它可以正常工作:
[pkerp@plastilin ernwin]$ MC-Annotate /dev/stdin < fess/structures/168d.pdb
Residue conformations -------------------------------------------
A1 : G C3p_endo anti
A2 : C C3p_endo anti
A3 : G C3p_endo anti
我的问题是,这两项行动有何不同,为什么会有不同的结果呢?作为一个额外的问题,是否有一个适当的术语来指定输入使用'&lt;'符号
更新
我目前最好的猜测是,正在运行的程序内部使用的是文件中的搜索。下面的答案似乎表明它与文件指针有关,但运行以下小测试程序:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *f = fopen(argv[1], "r");
char line[128];
printf("argv[1]: %s f: %d\n", argv[1], fileno(f));
while (fgets(line, sizeof(line), f)) {
printf("line: %s\n", line);
}
printf("rewinding\n");
fseek(f, 0, SEEK_SET);
while (fgets(line, sizeof(line), f)) {
printf("line: %s\n", line);
}
fclose(f);
}
表示在fseek
函数调用之前,所有内容都相同:
[pete@kat tmp]$ cat temp | ./a.out /dev/stdin
argv[1]: /dev/stdin f: 3
line: abcd
rewinding
===================
[pete@kat tmp]$ ./a.out /dev/stdin < temp
argv[1]: /dev/stdin f: 3
line: abcd
rewinding
line: abcd
使用过程替换正如Christopher Neylan建议的那样导致上面的程序挂起甚至没有读取输入,这看起来有点奇怪。
[pete@kat tmp]$ ./a.out /dev/stdin <( cat temp )
argv[1]: /dev/stdin f: 3
查看strace输出确认我怀疑尝试了一个在管道版本中失败的搜索操作:
_llseek(3, 0, 0xffffffffffd7c7c0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
在重定向版本中取得成功。
_llseek(3, 0, [0], SEEK_CUR) = 0
故事的寓意:不要随便尝试用/dev/stdin
替换一个论点并尝试去管它。它可能会起作用,但也可能不行。
答案 0 :(得分:1)
问题在于打开文件进行阅读的顺序。
/dev/stdin
不是真正的文件;它是当前进程用作标准输入的文件的符号链接。在典型的shell中,它链接到终端,并由shell启动的任何进程继承。请记住,MC-Annotate
只会读取作为参数提供的文件。
在管道示例中,/dev/stdin
是MC-Annotate
作为标准输入继承的文件的符号链接:终端。它可能在一个新的描述符上打开这个文件(比方说3,但它可以是任何大于2的值)。管道将cat
的输出连接到MC-Annotate's
标准输入(文件描述符0),MC-Annotate
继续忽略该输出以支持它直接打开的文件。
在重定向示例中,shell在运行 fess/structures/168d.pdb
之前将MC-Annotate
直接连接到文件描述符0 。当MC-Annotate
启动时,它会再次尝试打开/dev/stdin
,这次指向fess/structures/168d.pdb
而不是终端。
所以答案在于哪个文件/dev/stdin
是执行MC-Annotate
的进程中的链接;在进程启动之前设置 shell重定向; 流程启动后的管道
这有用吗?
cat fess/structures/168d.pdb | MC-Annotate <( cat /dev/stdin )
类似命令
echo foo | cat <( cat /dev/stdin )
似乎有效,但我不会声称情况是相同的。
[更新:不起作用。 /dev/stdin
仍然是终端的链接,而不是管道。]
这可能提供一种解决方法。现在,MC-Annotate
从子shell继承其标准输入,而不是当前shell,并且子shell的输出来自cat
作为其标准输入,而不是终端。
cat fess/structures/168d.pdb | ( MC-Annotate /dev/stdin )
它认为一个简单的命令组也可以运行:
cat fess/structures/168d.pdb | { MC-Annotate /dev/stdin; }
答案 1 :(得分:1)
这两个命令之间应该没有功能差异。实际上,我无法重现您所看到的内容:
#! /usr/bin/perl
# test.pl
# this is a test Perl script that will read from a filename passed on the command line, and print what it reads.
use strict;
use warnings;
print $ARGV[0], " -> ", readlink( $ARGV[0] ), " -> ", readlink( readlink($ARGV[0]) ), "\n";
open( my $fh, "<", $ARGV[0] ) or die "$!";
while( defined(my $line = <$fh>) ){
print "READ: $line";
}
close( $fh );
以三种方式运行:
(caneylan@faye.sn: tmp)$ cat input
a
b
c
d
(caneylan@faye.sn: tmp)$ ./test.pl /dev/stdin
/dev/stdin -> /proc/self/fd/0 -> /dev/pts/0
this is me typing into the terminal
READ: this is me typing into the terminal
(caneylan@faye.sn: tmp)$ cat input | ./test.pl /dev/stdin
/dev/stdin -> /proc/self/fd/0 -> pipe:[1708285]
READ: a
READ: b
READ: c
READ: d
(caneylan@faye.sn: tmp)$ ./test.pl /dev/stdin < input
/dev/stdin -> /proc/self/fd/0 -> /tmp/input
READ: a
READ: b
READ: c
READ: d
首先请注意/dev/stdin
是什么:
(caneylan@faye.sn: tmp)$ ls -l /dev/stdin
lrwxrwxrwx 1 root root 15 Apr 21 15:39 /dev/stdin -> /proc/self/fd/0
(caneylan@faye.sn: tmp)$ ls -l /proc/self
lrwxrwxrwx 1 root root 0 May 10 09:44 /proc/self -> 27565
它始终是/proc/self/fd/0
的符号链接。 /proc/self
本身是指向当前进程/proc
下目录的特殊链接。所以/dev/stdin
将始终指向当前进程的fd 0。因此,当您运行MC-Annotate
时(或者,在我的示例中,test.pl
),文件/dev/stdin
将解析为/proc/$pid/fd/0
,无论进程ID为MC-Annotate
是。这只是/dev/stdin
的符号链接如何工作的结果。
正如您在我的示例中所见,当您使用管道(|
)时,/proc/self/fd/0
将指向由cat
设置的管道的读取端。贝壳。当您使用重定向(<
)时,/proc/self/fd/0
将直接指向由shell设置的输入文件。
至于为什么你会看到这种奇怪的行为 - 我猜想MC-Annotate
在打开它之前对文件类型进行了一些检查,并且它看到/ dev / stdin指向一个命名管道而不是一个普通文件,并正在拯救。您可以通过阅读MC-Annotate
的源代码或使用strace
命令来查看内部发生的情况来确认这一点。
请注意,这两种方法在Bash中都有点过时。将流程输出导入只打开文件名的程序的可接受方法是使用process substitution:
$ MC-Annotate <(cat fess/structures/168d.pdb)
<(...)
构造将文件描述符返回到来自...
所有内容的管道的读取端:
(caneylan@faye.sn: tmp)$ echo <(true | grep example | cat)
/dev/fd/63
答案 2 :(得分:0)
查看有关MC-Annotate http://bioinfo.cipf.es/ddufour/doku.php?id=mc-annotate的信息管道无效的原因是因为MC-Annotate未将文件中的cat
输出识别为{{1}类型之一}
管道链命令将第一个输出用作下一个输出。
'&lt;' ('小于','左箭头','左尖括号')将文件输入命令。
http://tldp.org/LDP/abs/html/io-redirection.html#IOREDIRECTIONREF2