7

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

私の質問は、これら 2 つの操作の違いは何ですか?なぜ異なる結果が得られるのでしょうか? おまけの質問として、「<」記号を使用して入力を指定するための適切な用語はありますか?

アップデート:

私の現在の最善の推測は、実行中のプログラムの内部にある何かがファイル内のシークを利用しているということです。以下の回答は、ファイルポインタと関係があるが、次の小さなテストプログラムを実行していることを示唆しているようです:

#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それにパイプしようとしないでください。うまくいくかもしれませんが、うまくいかないかもしれません。

4

3 に答える 3

2

これら 2 つのコマンドに機能上の違いはありません。確かに、私はあなたが見ているものを再現することはできません:

#! /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 );

これを 3 つの方法で実行します。

(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)、ファイルは、プロセス ID が何であれ、 に/dev/stdin解決されます。これは、シンボリック リンクの動作の結果にすぎません。/proc/$pid/fd/0MC-Annotate/dev/stdin

上記の例でわかるように、パイプ ( |)を使用すると、シェルによって設定された/proc/self/fd/0パイプの読み取り側がポイントされます。catリダイレクト ( <)を使用する/proc/self/fd/0と、シェルによって設定された入力ファイルを直接指します。

この奇妙な動作が見られる理由についてはMC-Annotate、ファイルを開く前にファイルタイプのチェックを行っており、 /dev/stdin が通常のファイルではなく名前付きパイプを指していて、救済されていることがわかります。アウト。これは、のソースコードを読むかMC-Annotate、コマンドを使用してstrace内部で何が起こっているかを確認することで確認できます。

これらの方法は両方とも、Bash では少し回りくどいことに注意してください。ファイル名のみを開くプログラムにプロセスの出力を取得する受け入れられた方法は、プロセス置換を使用することです。

$ MC-Annotate <(cat fess/structures/168d.pdb)

<(...)コンストラクトは、パイプの読み取り側にファイル記述子を返します。パイプは、次のようになります...

(caneylan@faye.sn: tmp)$ echo <(true | grep example | cat)
/dev/fd/63
于 2013-05-10T16:14:47.037 に答える
1

問題は、ファイルが読み取り用に開かれる順序にあります。

/dev/stdin実際のファイルではありません。これは、現在のプロセスが標準入力として使用するファイルへのシンボリック リンクです。典型的なシェルでは、ターミナルにリンクされ、シェルによって開始されたすべてのプロセスに継承されます。MC-Annotate引数として提供されたファイルからのみ読み取ることに注意してください。

パイプの例では、標準入力として継承する/dev/stdinファイルへのシンボリックリンクです: 端末. MC-Annotateおそらく、このファイルを新しい記述子 (3 としましょう。ただし、2 より大きい任意の値にすることができます) で開きます。パイプは の出力catMC-Annotate's標準入力 (ファイル記述子 0) に接続します。標準入力MC-Annotateは、直接開いたファイルを優先して無視し続けます。

リダイレクトの例では、シェルは実行fess/structures/168d.pdbにファイル記述子 0 に直接接続します。起動すると、再び を開こうとしますが、今回は端末の代わりに指しています。 MC-AnnotateMC-Annotate/dev/stdinfess/structures/168d.pdb

したがって、答えは、/dev/stdin実行するプロセスでどのファイルがリンクされているかにありますMC-Annotate。プロセスの開始前にシェルのリダイレクトが設定されます。プロセス開始のパイプライン。

これは機能しますか?

cat fess/structures/168d.pdb | MC-Annotate <( cat /dev/stdin )

似たようなコマンド

echo foo | cat <( cat /dev/stdin )

うまくいくようですが、状況が同じであるとは言いません。


[更新: 機能しません。/dev/stdinパイプラインではなく、ターミナルへのリンクのままです。]

これにより、回避策が提供される場合があります。現在、MC-Annotate現在のシェルではなくサブシェルから標準入力を継承し、サブシェルはcat端末ではなく標準入力として出力を持っています。

cat fess/structures/168d.pdb | ( MC-Annotate /dev/stdin )

単純なコマンド グループも同様に機能すると考えられます。

cat fess/structures/168d.pdb | { MC-Annotate /dev/stdin; }
于 2013-05-10T13:38:15.323 に答える
0

MC-Annotate http://bioinfo.cipf.es/ddufour/doku.php?id=mc-annotateに関するこの情報を見ると 、パイプが機能しない理由は、MC-Annotate がcatファイルからの出力を認識していないためです。タイプの一つとして.pbd

パイプ チェーン コマンドは、最初のコマンドの出力が次のコマンドの入力として使用されます。

「<」(「より小さい」、「左矢印」、「左山括弧」) は、ファイルをコマンドに入力します。

http://tldp.org/LDP/abs/html/io-redirection.html#IOREDIRECTIONREF2

于 2013-05-10T12:50:07.957 に答える