「cat file | ./binary」と「./binary < file」の違いは何ですか?

「cat file | ./binary」と「./binary < file」の違いは何ですか?

変更できないバイナリファイルがありますが、次のようにできます。

./binary < file

私もできます:

./binary << EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF

しかし、

cat file | ./binary

私にエラーが発生します。パイプではなぜ動作しないのかわかりません。 3つの場合、すべての内容は次のとおりです。文書割り当てられた標準入力バイナリ(他の方法を使用してください):

  1. bashはファイルを読み取り、それをstdinに供給します。バイナリ
  2. bashはstdinから(EOFまで)行を読み取り、それをstdinに供給します。バイナリ
  3. catはファイルラインを読み取り、それをstdoutに保存し、bashはそれをstdinにリダイレクトします。バイナリ

私が知っている限り、バイナリはこれら3つの違いに気付いてはいけません。 3番目のケースが機能しない理由を誰かが説明できますか?

ところで:与えられたエラーバイナリ例:

20170116/125624.689 - U3000011 スクリプトファイル "" を読み取れません。エラーコード「14」。

しかし、私の主な質問は違いは何ですか?すべてのプログラムこの3つのオプションがあります。

詳細は次のとおりです。もう一度試しました。ストレス 実際には少しエラーがありますESPIPE(違法追求)~から探す 続いてEFAULT(無効なアドレス)~から読むエラーメッセージの直前。

一時ファイルを使用せずにRubyスクリプトを使用して制御したいバイナリは次のとおりです。カラピ~から自動(UC4)

答え1

存在する

./binary < file

binarystdin は読み取り専用モードで開かれたファイルです。ファイルはbashまったく読み取られず、実行中のプロセスのファイル記述子0(stdin)を読み取るためにファイルを開きますbinary

存在する:

./binary << EOF
test
EOF

シェルによっては、stdin は、シェルが入れた内容やパイプの読み込み先 ( ; ; とシェルがパイプの反対側に並列に書き込む) をbinary含む削除された一時ファイル (AT&T ksh、zsh、bash...) になります。 。あなたの場合。test\ndashyashtest\nbash

存在する:

cat file | ./binary

シェルに応じて、binarystdinはパイプの読み取り端または書き込み方向がオフになり(ksh93)、もうcat一方の端の内容が書き込まれるソケットペアの一方の端です。file

stdinが通常のファイル(一時または非一時)の場合は検索可能です。binary開始または終了に移動、巻き戻しなどが可能です。また、マッピングしてioctl()sFIEMAP / FIBMAPと同様の操作を実行できます(<>代わりに使用する場合は<切り取り/穴あけなど)。

一方、パイプとソケットのペアはプロセス間通信の手段であり、binaryデータ以外にできることはあまりありません(パイプ関連タスクなどの一部のタスクがありますが、通常のファイルではなくそのファイルに対して実行できます)。readioctl()

ほとんどの場合、seekパイプの使用時にアプリケーションが失敗/苦情を引き起こすのは、この機能が不足しているためです。ただし、通常のファイルでは機能しますが、他の種類のファイルでは機能しない他のシステムコール(たとえば、、、、、mmap()ftruncate()ですfallocate()。 Linuxでは、/dev/stdinfd 0がパイプや通常のファイルで開かれるときの動作にも大きな違いがあります。

処理できるコマンドがたくさんあります。閲覧可能これはファイルですが、この場合、通常は標準入力で開かれたファイルには適用されません。

$ unzip -l file.zip
Archive:  file.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
       11  2016-12-21 14:43   file
---------                     -------
       11                     1 file
$ unzip -l <(cat file.zip)
     # more or less the same as cat file.zip | unzip -l /dev/stdin
Archive:  /proc/self/fd/11
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of /proc/self/fd/11 or
        /proc/self/fd/11.zip, and cannot find /proc/self/fd/11.ZIP, period.

unzipファイルの末尾に保存されているインデックスを読み、ファイル内のアーカイブメンバーを読む必要があります。ただし、ここではファイル(最初の場合は通常のファイル、2番目の場合はパイプ)がパス引数として提供され、呼び出し元が指定したunzipfdunzipを継承する代わりに、それ自体(通常は0ではないfdで)開きます。開けました。標準入力からzipファイルを読みません。 stdin は主にユーザーの対話に使用されます。

binaryリダイレクトせずにターミナルエミュレータで実行される対話型シェルのプロンプトでプログラムを実行すると、binarystdinは呼び出し側シェルから継承され、シェル自体は呼び出し側端末エミュレータから継承され、開かれたptyデバイスになります。読み取り+書き込みモード(同様のもの/dev/pts/n)。

これらのデバイスも検索できません。したがって、binary端末から入力を受け取るのがうまく機能している場合、問題はおそらく検索とは関係ありません。

14がerrno(失敗したシステムコールに設定されたエラーコード)の場合、ほとんどのシステムではこれEFAULT無効な住所)。read()書き込み不可能なメモリアドレスからの読み取りが必要な場合、システムコールはこのエラーによって失敗します。これは、fdが先のとがったパイプから読み取るのか、通常のファイルから読み取るのかとは何の関係もなく、通常Error 1を示します。

binary標準入力(を使用して)で開かれたファイルの種類を確認できます。fstat()通常のファイルでもttyデバイスでもない場合はエラーが発生します。

アプリについてもっと知らないと言うのは難しいです。これを(またはシステムでそれに対応するもの)実行すると、ここで失敗した場合にシステムコールが何であるかを理解するのに役立ちstraceます。trusstusc


想像したシナリオ1つマシュー・アイブあなたの質問についてのコメントはここで合理的に聞こえます。彼の言葉を引用するには:

データを読み取るためのバッファサイズを取得するためにファイルの終わりを見て、クエリが機能しないことを誤って処理し、負のサイズを割り当てようとするようです(間違ったmallocを処理しません)。与えられたバッファのどのエラーが無効かを読み取るためにバッファを渡します。

答え2

以下は、説明する簡単なサンプルプログラムです。Stefan Chazerasの答え使用lseek(2)入力時:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int c;
    off_t off;
    off = lseek(0, 10, SEEK_SET);
    if (off == -1)
    {
        perror("Error");
        return -1;
    }
    c = getchar();
    printf("%c\n", c);
}

テスト:

$ make seek
cc     seek.c   -o seek
$ cat foo
abcdefghijklmnopqrstuwxyz
$ ./seek < foo
k
$ ./seek <<EOF
> abcdefghijklmnopqrstuvwxyz
> EOF
k
$ cat foo | ./seek
Error: Illegal seek

パイプは見つかりません。これは、プログラムがパイプについて文句を言うことができる場所です。

答え3

パイプとリダイレクトは異なる動物であると言えば十分です。here-docリダイレクト(<<)またはリダイレクトstdinを使用すると、< テキストは突然表示されません。これは実際にはファイル記述子(または必要に応じて一時ファイル)に入り、バイナリのstdinが指す場所です。

bash's具体的には、ソースコードredir.cファイル(バージョン4.3)からの抜粋です。

/* Create a temporary file holding the text of the here document pointed to
   by REDIRECTEE, and return a file descriptor open for reading to the temp
   file.  Return -1 on any error, and make sure errno is set appropriately. */
static int
here_document_to_fd (redirectee, ri)

したがって、リダイレクトは基本的にファイルとして扱うことができるため、バイナリはそれらをブラウズしたり、ファイルをseek()簡単にブラウズしてファイル内のすべてのバイトに移動したりできます。

パイプは64KiBバッファ(少なくともLinuxでは)で、4096バイト以下の書き込みは原子性が保証されているため検索できません。つまり、自由にナビゲートできず、順次読み取りのみ可能です。 Pythonでコマンドを実装しましたtail。リダイレクトすると、マイクロ秒以内に2,900万行のテキストを見つけることができますが、catパイプラインで編集するとできることはあまりありませんので、すべてを順番に読む必要があります。

別の可能性は、バイナリがファイルを具体的に開き、パイプから入力を受け取らないようにすることができるということです。通常はfstat()システムコールを介して行われ、入力が特定のS_ISFIFO種類のファイル(パイプ/名前付きパイプを意味)から来ていることを確認します。

あなたの特定のバイナリはそれが何であるかわからないので、パイプを見つけようとしますが、見つかりません。エラーコード14の正確な意味を理解するには、そのマニュアルを参照することをお勧めします。

ノート/bin/sh:dash(Debian Almquist Shell、Ubuntuのデフォルト)など、一部のシェルはhere-docリダイレクトを実装します。内部配管そのため、検索できない場合があります。ポイントは同じです。パイプラインは連続的で簡単にナビゲートできず、ナビゲートしようとするとエラーが発生します。

答え4

主な違いはエラー処理です。

次の状況では、エラーが報告されます。

$ /bin/cat < z.txt
-bash: z.txt: No such file or directory
$ echo $?
1

次の場合、エラーは報告されません。

$ cat z.txt | /bin/cat
cat: z.txt: No such file or directory
$ echo $?
0

Bashを使用しても、PIPESTATUSを引き続き使用できます。

$ cat z.txt | /bin/cat
cat: z.txt: No such file or directory
$ echo ${PIPESTATUS[0]}
1

ただし、コマンドを実行した直後にのみ使用できます。

$ cat z.txt | /bin/cat
cat: z.txt: No such file or directory
$ echo $?
0
$ echo ${PIPESTATUS[0]}
0
# oops !

バイナリの代わりにシェル関数を使用すると、別の違いがあります。では、パイプラインの一部である関数はサブシェルで実行されます(オプションが有効で非対話型のbash場合は最後のパイプラインコンポーネントを除く)。したがって、変数の変更は親シェルには影響しません。lastpipebash

$ a=a
$ b=b
$ x(){ a=x;}
$ y(){ b=y;}

$ echo $a $b
a b

$ x | y
$ echo $a $b
a b

$ cat t.txt | y
$ echo $a $b
a b

$ x | cat
$ echo $a $b
a b

$ x < t.txt
$ y < t.txt
$ echo $a $b
x y

関連情報