Bash

管道進入另一個管道未按預期工作的結果

  • March 28, 2022

我是 bash 程式的新手,當我遇到這種讓我很困惑的奇怪行為時,我正在玩一些命令,試圖了解如何使用管道和分組命令傳遞多個參數。我知道實現我想要的其他方法,但是,我試圖理解為什麼會發生這種情況。

我正在嘗試使用write 內置函式程序向連接到我的 SSH 伺服器的使用者(讓我們認為他們的使用者 ID 為 USER,他們的 TTY 為 TTY)發送一條消息。

雖然我能夠使用以下命令很好地發送消息:

$ echo "some message" | write USER TTY

但是當我嘗試使用另一個管道傳遞 USER 和 TTY 時,沒有發送消息:

$ echo "some message" | { echo "USER TTY" | xargs -o write; }

在結果中,似乎 bash 忽略了第一部分 ( $ echo "some message"),需要在執行命令後輸入消息。

請注意,{ echo "USER TTY" | xargs -o write; }並且write USER TTY做同樣的工作(顯然?我懷疑這裡有一個我不知道的區別)。

同樣,我知道有更簡單的方法可以做到這一點,但我只是想了解 bash 在分組命令、管道和將輸入參數傳遞給函式方面是如何工作的。非常感謝對這些領域的任何評論。


對於那些懷疑我要求做作業的人,很高興看到人們關心這些東西,我真的試圖創建一個別名來向我的 ssh 伺服器上的每個使用者發送消息,我發現wall這樣做更容易,雖然發現我在這裡提到的內容很有趣。

您的

echo "some message" | { echo "USER TTY" | xargs -o write; }

命令不能工作,因為它這樣做:

                       |-------------------------------------------|
echo "some message"   --+->   echo "USER TTY"  -->  xargs -o write; |
                       |-------------------------------------------|

的標準輸入xargs是來自的管道echo "USER TTY"。將echo "some message"通過管道傳輸到復合命令 { echo "USER TTY" | xargs -o write; }中,這實際上意味著將echo "some message"通過管道傳輸到echo "USER TTY"; 這意味著該write命令看不到some message.

PS由於您已指定 -o 選項xargs, write的標準輸入設置為終端。如果您想說,這將起作用

echo "USER TTY" | xargs -o write

然後只需鍵入消息,但它不會連接write到最外面的echo.

如果您有一些*Command B輸出USER*和 TTY (但只有一對),您可以嘗試

echo "一些資訊" | 寫 $(*命令 B* )

更新:*Command B*以下可能適用於僅輸出一對 USERand 的假設情況TTY,但可能不適用於您的實際情況。閱讀它,然後再往下看討論。

如果,如您所說,您使用的是 bash,並且您有 GNU xargs (Linux 上的標準;可能可用於其他類 Unix 作業系統),您也可以嘗試

echo "一些資訊" | xargs -a <(*命令 B* ) 寫

正如 rici 在 從使用 xargs 執行的命令中讀取標準輸入中所建議的那樣。

我剛剛意識到您可能擁有 GNU xargs,因為 -o POSIX 未指定該選項。


更多想法:

  • 這可能會解決您的部分問題:副手,我想不出任何例子
*命令 A* | {*命令 B* | *命令 C* ; }

不同於

*命令 A* | *命令 B* | *命令 C*

即,分組產生影響的地方。我希望我忽略了一些明顯的事情,我期待有一個例子向我指出。同樣地,

{*命令 A* | *命令 B* ; } | *命令 C*

將是等效的。

  • 管道如
*命令 A* | *命令 B* | *命令 C*

可能有用。作為一個簡單的例子:

$ echo GET | tr BEG VAC | od -cb
0000000   C   A   T  \n
       103 101 124 012
0000004
  • 像這樣的管道
*命令 A* | *命令 B* | xargs *(選項)*  *命令 Y*

如果您認為它是有用的

{*命令 A* | *命令 B* ; } | xargs *(選項)*  *命令 Y*

即,提供輸入到 . 如果你認為它是*Command A* | *Command B*``xargs

*命令 A* | {*命令 B* | xargs *(選項)*  *命令 Y* ; }

即,*Command B*提供輸入xargs和 *Command A*提供輸入 *Command Y*,由於上面討論的原因,它不起作用:沒有辦法在 *Command A*和 之間建立數據流*Command Y*

  • 然而,
*命令 A* | xargs *(選項)*   **-a <(***命令 B* **)**  *命令 Y*

有問題的原因不同: xargs這裡的工作是*Command Y*(可能)多次執行。但是沒有*Command A*多次執行的機制,所以它不能提供 *Command Y*多次輸入。 如果你真的想執行多次,你可以這樣做*Command A* | *Command Y*

*命令 B* | xargs *(選項)* sh -c '*命令 A* | *命令 Y* ' sh

例如,

$ echo S F u o n b | xargs -n2 sh -c 'date | tr "$1" "$2"; sleep 2' sh
Fun, Mar 27, 2022 11:11:07 AM
Son, Mar 27, 2022 11:11:09 AM
Sub, Mar 27, 2022 11:11:11 AM

這執行

date | tr S F
date | tr u o
date | tr n b

您可以看到它實際上執行date了 3 次,因為秒數不同(07和 09 )  11

這將適用於您的情況。(請注意,在我上面的範例中,一次*Command Y**Command B*兩個參數中獲取參數,就像您真正想要/需要做的那樣write。)如果 *Command B*產生看起來像

(例如,)的輸出,那麼您可以這樣做*name1* *tty1* *name2* *tty2* *name3* *tty3* …``fred tty17 wilma tty42 barney ttyS23

*命令 B* | xargs -n2 *(可能是其他選項)* sh -c 'echo " *some message* " | 寫“$1”“$2”' sh
  • 但是,如果您不需要多次執行*Command A*(在您的情況下, )(因為它每次都會產生相同的輸出),您可以利用這一事實,並執行一次,保存輸出:echo "some message"``*Command A*
回顯“*一些消息*”> tmpfile;\
  *命令 B* | xargs -n2 *(可能是其他選項)* sh -c 'write "$1" "$2" < tmpfile' sh; \
  rm tmp 文件
  • 因此,上述解決方案避免了*Command A*多次執行,但它仍然為每個使用者執行一次 shell。這是不可取的。我沒有看到任何方法來處理這種情況,使用xargs, 無需多次啟動 shell。(再次,我希望有人向我指出。)根本不使用 shell 就很難處理它*,* 因為xargs只能處理“簡單”命令——沒有重定向(<)或管道(|)。  但是我們可以在沒有 的情況下處理它xargs,更有創意地使用 shell。假設每行*Command B*產生一對輸出(例如,(newline) (newline) (newline)  ),那麼您可以這樣做*namen* *ttyn*``fred tty17 wilma tty42 barney ttyS23
回顯“*一些消息*”> tmpfile;\
  *命令 B* | 朗讀時;寫 "$u" "$t" < tmpfile; 完畢; \
  rm tmp 文件

深入研究並解決問題中的幾個微點:

echo "*一些資訊*" | { 迴聲“*使用者* *TTY* ”| xargs -o 寫;}

外殼本身實際上並沒有忽略該部分*。*  但是,正如我在答案的第一部分中所描述的那樣,它實際上是通過管道將其導入元件中。並且 不讀取其標準輸入;因此,未處理。 echo "*some message*"``echo "*USER* *TTY*"``echomessage

  • >

請注意{ echo "USER TTY" | xargs -o write; }write USER TTY 做同樣的工作(顯然?我懷疑這裡有一個我不知道的區別)。

好的,如果這還不清楚:兩個變體都write使用參數呼叫程序,USER並且 TTY.  是一個簡單的命令,它不會修改其標準 I/O 流,而只是從現有的標準輸入中讀取。這可能是 tty、管道或文件。該 變體將’s stdin 重定向為 be ,因為這就是這樣做的。通常(如果您不指定 ),的標準輸入是要執行的命令的參數來源,因此無法訪問較大環境中的主流標準輸入(這將是 消息的來源for  ) — 因此它不能做到這一點write *USER* *TTY*``xargs``write``/dev/tty``-o``-a``xargs``xargs``write可用於執行程序的數據流。因此,如果您不指定-aor  -o,  xargs則將執行程序的標準輸入設置為 /dev/null.

  • 注意
*命令 B* | xargs -n2 -o 寫

會有點工作,但它需要您為每個使用者鍵入一次輸入消息,因為它write為每個使用者呼叫一次,而沒有任何方法在迭代之間保存消息。如果你想向不同的使用者發送不同的消息,理論上你 可以使用它,但這並不實用,因為它不會給你任何提示或回饋來告訴你正在與哪個使用者交談。

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