Shell

“cat 文件 | ./binary”和“./binary < 文件”有什麼區別?

  • October 6, 2020

我有一個二進製文件(我無法修改),我可以這樣做:

./binary &lt; file

我也可以這樣做:

./binary &lt;&lt; EOF
> "line 1 of file"
> "line 2 of file"
...
> "last line of file"
> EOF

cat file | ./binary

給我一個錯誤。我不知道為什麼它不適用於管道。在所有 3 種情況下,文件的內容都被提供給二進制的標準輸入(以不同的方式):

  1. bash 讀取文件並將其提供給二進制的標準輸入
  2. bash 從標準輸入讀取行(直到 EOF)並將其提供給二進制標準輸入
  3. cat 讀取文件行並將其放入標準輸出,bash 將它們重定向到二進制標準輸入

據我了解,二進製文件不應該注意到這 3 個之間的區別。有人可以解釋為什麼第三種情況不起作用嗎?

順便說一句:二進制給出的錯誤是:

20170116/125624.689 - U3000011 無法讀取腳本文件“”,錯誤程式碼“14”。

但我的主要問題是,任何具有這 3 個選項的程序有什麼不同。

以下是一些進一步的細節:我用strace再次嘗試 ,實際上有一些錯誤ESPIPE(非法搜尋)來自lseek ,然後是錯誤消息之前讀取的EFAULT (錯誤地址) 。

我嘗試使用 ruby​​ 腳本(不使用臨時文件)控制的二進製文件是Automic (UC4)的**callapi的一部分。

./binary &lt; file

binary的標準輸入是以只讀模式打開的文件。請注意,bash它根本不讀取文件,它只是打開它以讀取它執行的程序的文件描述符 0(stdin)binary

在:

./binary &lt;&lt; EOF
test
EOF

根據外殼,binary的標準輸入將是一個已刪除的臨時文件(AT&T ksh、zsh、bash …),其中包含test\n外殼放置的內容或管道的讀取端(dash, ; 並且外殼並行yash寫入test\n在管道的另一端)。在您的情況下,如果您正在使用bash,它將是一個臨時文件。

在:

cat file | ./binary

根據外殼,binary的標準輸入將是管道的讀取端,或者是寫入方向已關閉(ksh93)並cat正在另一端寫入內容的套接字對file的一端。

當 stdin 是正常文件(臨時文件或非臨時文件)時,它是可搜尋的。binary可能會轉到開頭或結尾,倒帶等。它也可以映射它,做一些ioctl()s像 FIEMAP/FIBMAP (如果使用&lt;&gt;而不是&lt;,它可以截斷/打孔等)。

另一方面,管道和套接字對是一種程序間通信方式,除了數據binary之外沒有太多可以做的事情read(儘管也有一些操作,比如一些特定於管道的操作ioctl(),它可以對它們執行,而不是對正常文件執行) .

大多數情況下,缺少的功能seek會導致應用程序在使用管道時失敗/抱怨,但它可能是對正常文件有效但對不同類型文件無效的任何其他系統呼叫(如mmap(), ftruncate(), fallocate()) . /dev/stdin在 Linux 上,當fd 0 在管道或正常文件上打開時,行為也有很大差異。

有許多命令只能處理可搜尋的文件,但在這種情況下,通常不適用於在其標準輸入上打開的文件。

$ unzip -l file.zip
Archive:  file.zip
 Length      Date    Time    Name
---------  ---------- -----   ----
      11  2016-12-21 14:43   file
---------                     -------
      11                     1 file
$ unzip -l &lt;(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需要讀取儲存在文件末尾的索引,然後在文件內查找以讀取歸檔成員。但是在這裡,文件(在第一種情況下是正常的,在第二種情況下是管道)作為 的路徑參數給出unzip,並unzip自行打開它(通常在 fd 上而不是 0 上),而不是繼承呼叫者已經打開的 fd。它不會從其標準輸入中讀取 zip 文件。stdin 主要用於使用者互動。

如果您binary在終端仿真器中執行的互動式 shell 提示下執行您的程序而不進行重定向,則binary’s stdin 將從其呼叫者 shell 繼承,後者本身將從其呼叫者終端仿真器繼承它,並將成為pty 設備以讀寫模式打開(類似於/dev/pts/n)。

這些設備也不可搜尋。因此,如果binary在從終端獲取輸入時工作正常,則問題可能與搜尋無關。

如果這 14 是一個 errno(由失敗的系統呼叫設置的錯誤程式碼),那麼在大多數係統上,這將是EFAULT錯誤地址)。read()如果要求讀取不可寫的記憶體地址,系統呼叫將失敗並出現該錯誤。這與 fd 是否從指向管道或正常文件讀取數據無關,並且通常會指示錯誤1。

binary可能確定在其標準輸入(帶有 )上打開的文件類型,fstat()並在它既不是正常文件也不是 tty 設備時遇到錯誤。

如果不了解有關應用程序的更多資訊,很難說。strace在(或truss/您的系統上的等效項)下執行它tusc可以幫助我們查看系統呼叫是什麼,如果這裡有任何失敗。


1 Matthew Ife在對您的問題的評論中設想的場景在這裡聽起來很合理。引用他的話:

我懷疑它正在尋找文件末尾以獲取用於讀取數據的緩衝區大小,錯誤地處理了尋找不起作用的事實並嘗試分配負大小(不處理錯誤的 malloc)。傳遞緩衝區以讀取給定緩衝區無效的哪些故障。

引用自:https://unix.stackexchange.com/questions/337739