簡體   English   中英

Bash 中的管道、標准輸入和命令行參數

[英]Pipe, standard input and command line arguments in Bash

考慮:

command1 | command2

command1 的輸出是用作 command2 的標准輸入還是用作 command2 的命令行參數?

例如,

cat test.sh | grep "hehe"

不使用管道的等效形式是什么?

我試過

grep "hehe" $(cat test.sh)

它似乎不正確。

grep "hehe" < test.sh

輸入重定向 - 當然,僅適用於單個文件,而cat適用於任意數量的輸入文件。


考慮符號:

grep "hehe" $(cat test.sh)
grep "hehe" `cat test.sh`

這些在這種情況下是等效的; 在嵌套使用中使用“ $(cmd) ”符號要容易得多,例如:

x=$(dirname $(dirname $(which gcc)))
x=`dirname \`dirname \\\`which gcc\\\`\``

(這為您提供了安裝 GCC 的基本目錄,以防您想知道。)

grep示例中,所發生的情況是讀取test.sh的內容並將其拆分為以空格分隔的單詞,並且每個這樣的單詞都作為參數提供給grep 由於grep處理"hehe"之后的單詞(當然,其中grep沒有看到雙引號 - 在這種情況下不需要它們;作為一般規則,使用單引號而不是雙引號,尤其是在復雜的字符串中,例如經常使用 shell 元字符的正則表達式)...正如我所說, grep"hehe"之后的單詞視為文件名,並嘗試打開每個文件,通常會因為文件不存在而慘淡失敗。 這就是該符號在這種情況下不合適的原因。


在重新審視這個問題之后,還有更多可以說的——還沒有說。

首先,許多 Unix 命令被設計為用作“過濾器”; 他們從一些文件中讀取輸入,以某種方式對其進行轉換,然后將結果寫入標准輸出。 此類命令專為在命令管道中使用而設計。 例子包括:

  • 格雷普
  • 特洛夫和親戚
  • awk(有警告)
  • sed
  • 種類

所有這些過濾器都具有相同的一般行為:它們采用命令行選項來控制它們的行為,然后它們要么讀取指定為命令行參數的文件,要么讀取它們的標准輸入,如果沒有這樣的參數。 有些(如sort )可以有選項來控制它們的輸出而不是標准輸出,但這種情況相對不常見。

有一些純過濾器 - tr就是其中之一 - 嚴格讀取標准輸入並寫入標准輸出。

其他命令有不同的行為。 Eric Raymond 在“ The Art of UNIX Programming ”中提供了命令類型的分類法。

一些命令在標准輸出上生成文件名列表 - 兩個經典命令是lsfind

有時,您希望將文件名生成器的輸出應用為過濾器的命令行參數。 有一個程序可以自動執行此操作 - 它是xargs

通常,您將使用:

find . -name '*.[chyl]' | xargs grep -n magic_name /dev/null

這將生成擴展名為“ .c ”、“. .h ”、“. .y ”和“ .l ”(C 源、頭文件、Yacc 和 Lex 文件)的文件的完整列表。 當列表由xargs讀取時,它將創建命令行,以grep -n magic_name /dev/null開頭,並將每個單詞(由空格分隔)作為參數。

在過去,Unix 文件名不包含空格。 在 Mac 和 Windows 的影響下,這樣的空間現在已經司空見慣。 findxargs的 GNU 版本具有處理此問題的補充選項:

find . -name '*.[chyl]' -print0 | xargs -0 grep -n magic_name /dev/null

' -print0 ' 選項的意思是“打印以 NUL '\\0' 結尾的文件名”(因為不能出現在(簡單)文件名中的唯一字符是 '/' 和 NUL,顯然,'/' 可以出現在路徑名中)。 相應的“ -0 ”告訴xargs查找以 NUL 結尾的名稱,而不是空格分隔的名稱。

另一種重定向形式是進程替換。

grep "hehe" <(cat test.sh)

相當於:

grep "hehe" test.sh

兩者都查看test.sh本身的內容。

雖然,正如已經指出的,這個命令:

grep "hehe" $(cat test.sh)

test.sh查找文件名並將它們用作grep參數。 所以如果test.sh包含:

scriptone
scripttwo

然后grep將在每個文件的內容中查找“hehe”。

使用命令行參數的 bash 管道的等價物是什么?

管道和命令行參數是不同形式的輸入,不可互換。 如果一個程序允許您擁有兩者的等效形式,則只能選擇該程序。 (源代碼,命令行參數顯示為一個可變文本,而管顯示為打開的文件,包括標准輸入和標准輸出。Bash的I / O重定向的語法,這里lateron使用,在技術上屬於命令行參數,即使在命令行上寫在它們旁邊……)

但讓我們學究,也回答這個:

不使用 bash 管道字符的 bash 管道相當於什么?

答案: cat test.sh | grep "hehe" cat test.sh | grep "hehe"相當於

grep "hehe" < <(cat test.sh)

說明:

  • 管道將一個命令的標准輸出重定向到另一個命令的標准輸入。 要設置 stdin 的來源,我們可以使用輸入重定向 ( < … ) 而不是使用管道字符。

  • 但是,僅使用輸入重定向( grep "hehe" < test.sh )並不等同於管道,因為它使用文件作為 stdin 的源,而管道使用輸出命令cat test.sh )。 因此,此外,我們添加了進程替換<(…)以使用從命令到 stdin 的輸入替換從文件到 stdin 的輸入。

  • 當然,我們這里的示例令人困惑,因為這兩個變體具有相同的效果:

     grep "hehe" < test.sh grep "hehe" < <(cat test.sh)

    但從技術上講,來自文件的 stdin 輸入與來自從文件獲取輸入的命令輸出的 stdin 輸入仍然是一種不同的機制。

  • 對於更詳細的解釋,我推薦另外兩個答案: herehere

來源:高級 Bash 腳本手冊,關於進程替換的部分(從“其他一些用法”開始閱讀)。

它用作標准輸入。

嘗試:

grep "hehe" - $(cat test.sh)

那可能是錯誤的; 我不能在這台電腦上測試它。 如果像您嘗試的那樣在沒有管道的情況下執行此操作,grep 會將最后一個參數視為文件名,即查找名為 [test.sh 的內容] 的文件。 如果你給它傳遞一個 - (或者不放最后一個參數),你告訴它使用標准輸入作為文件。

你也可以只通過 grep 一個文件來掃描:

grep "hehe" test.sh

...但您似乎問了更多籠統的 bash 問題,而不是真正的 grep 用法問題,所以這可能沒有太大幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM