簡體   English   中英

shell 腳本進程替換 <() 在命令替換 $() 中

[英]shell scripting process substitution <() inside command substitution $()

我有一個 output 為 1 的命令,類似於:

cmp -bl <(cmp -bl <(echo ABCDEF) <(echo ABDCEF)| wc -l) <(echo 0) |wc -l

我需要將命令 output 與 if 語句中的值進行比較:

if [ $(cmp -bl <(echo ABCDEF) <(echo ABDCEF)| wc -l) = "1" ]; then ...

我遇到的錯誤與語法有關:意外標記附近的語法錯誤('

[和之前]之后留一個空格。 [是一個類似於test的實際命令。 您需要將命令與其參數分開。 ]不是命令,但[命令參數的語法規則也要求將其分開。

if [ $(cmp -bl <(cmp -bl <(echo ABCDEF) <(echo ABDCEF)| wc -l) <(echo 0) |wc -l) = "1" ]; then ...

編輯:使用新版本的問題,加上您所做的評論,答案很簡單。 您的代碼(您問題的當前版本中的代碼)完全可以。 它在我所有使用 bash 的計算機上都能正常工作。 但它是bash。 不是噓。

如果您將它與 /bin/sh 一起使用(正如您在其中一條評論中所說的那樣),那么您得到的錯誤是

/bin/sh: 1: Syntax error: "(" unexpected

這里出乎意料的括號是<(...)語法之一。 這是 bash 特定語法。 /bin/sh 不能使用 pip/進程替換。

了解 <(command) 語法是一種特殊的替換。 它在另一個進程中運行命令,output 重定向到 pipe,其文件名被替換為 <(command) 部分。 echo <(ls)例如顯示類似/dev/fd/63的內容,它是一個名為 pipe 的“文件”,其“內容”是ls的結果(即使它不是文件,也沒有內容.但是讀/dev/fd/63就是讀ls的output。還是蠻有用的,即使大多數時候,你也不需要它。但它是最近的(好吧,也許一兩年。但是當我學習 sh/bash 時它不存在),而不是 /bin/sh 的一部分。

所以答案是:你需要 bash。 你不能在 /bin/sh 中這樣做

如果您想將 cmp 與兩個命令的 output 一起使用,並且您真的不能只使用#!/bin/bash而不是#!/bin/sh ,更簡單(但效率較低)的解決方案是使用臨時文件首先存儲命令的結果(希望這些命令不會產生千兆字節的輸出),然后比較它們。

或者,您可以使用命名管道重現非常相似的行為。

#!/bin/sh
mknod /tmp/pipe1.$$ p
mknod /tmp/pipe2.$$ p
echo ABCDEF > /tmp/pipe1.$$ &
echo ABCxEF > /tmp/pite2.$$ &
if [ $(cmp -bl /tmp/pipe1.$$ /tmp/pite2.$$ | wc -l) = "1" ]; then
    echo One and only one diff
fi
rm /tmp/pipe1.$$
rm /tmp/pipe2.$$

它的作用大致相同 The echo stuff > /tmp/pipe1.$$ &用於echo stuff進程(將 echo 替換為您想要的任何命令)。 Whouse output 被重定向到名為 pipe /tmp/pipe1.$$ 我們在第一行創建的。 (.$$ 后綴,只是為了確保沒有其他進程可以使用相同的 /tmp/pipe1 名稱,因為 $$ 是我們進程的 pid)然后你有一個非常經典的if [ $(cmp /tmp/pipe1.$$ /tmp/pipe2.$$ | wc -l) ]其中, cmp 只是用兩個文件名調用,它們恰好是命名管道(畢竟與 <(...) 完全相同)

但同樣,只有當您有充分的理由想要一個純 sh(而不是 bash)解決方案時。 在這種情況下,您還應該編輯您的問題以刪除“bash”標簽。

================= 舊答案 ==========================

這對我來說似乎很令人費解。 例如,使用 cmp foo <(echo 0) 將 foo 的內容(foo 本身是 cmp 上的 wc -l 的結果)與 0 進行比較。但我想你已經明白了,你明白這是一個 Rube-Goldberg 裝置而不是“== 0”,並且您了解每個 <() 派生一個進程,並從該進程返回一個管道名稱(當您需要傳遞包含執行命令的結果)。

如果不是,你應該解釋你真正想要做什么,因為,即使用真正的命令替換 <(echo...) (我也推測這些只是例子),cmp 和 wc 的這幾個階段是不可能的是正確的方法。

話雖這么說,只需在 [ 和 ] 周圍添加一個空格,它就會完全按照您的意圖進行。 (編輯:正如其他人在更快的回復和評論中所說的那樣。我輸入的答案太長......我不會刪除這個答案,因為序言可能是相關的)

編輯:回復更多關於使用部分

  • 正如評論中所見,不需要兩層 wc -l (一個已經是一種奇怪的方式,但如果它有效......)
  • 小心你如何比較事物。
    • 我的(在評論中),使用==僅在 linux 中有效,因為 wc output 是一串數字,僅此而已。 由於標題空格,它不在mac上。 所以11不同。
    • 你的,也是一樣。 但是由於您使用[而不是[[[是和外部命令,而[[是 bash 語法( [[更好:沒有進程的分叉),在將參數傳遞給[命令的過程中,空格1會丟失
    • 最干凈的解決方案可能是使用 -eq 來比較整數,而不管它們是如何打印的。

所以,總的來說

if [[ $(cmp -bl <(echo string1) <(echo string2) | wc -l) -eq 1 ]]
then
   echo one and only one difference
fi

應該管用。 同樣,不確定它是最快的,也不是最短的,也不是最優雅的解決方案。 但只要沒有人提供更好的...

暫無
暫無

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

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