使用()和$()執行一系列命令的區別
我目前正在嘗試製作一個腳本,該腳本創建將作為輸入傳遞給 netcat 的字節。
這是腳本的想法:
(perl -e "print \"$BYTES\x00\"; cat file; perl -e "print \"More bytes\"x16 . \"\r\n\"";) | netcat ip port
我嘗試同時使用子外殼和命令替換(例如,使用 $())來執行命令。但是我不明白為什麼使用命令替換時腳本的輸出是錯誤的。我懷疑命令替換在執行多個命令時錯誤地管道其輸出。有人可以向我解釋為什麼會這樣嗎?
編輯
這是使用命令替換的變體:
$(perl -e "print \"$BYTES\x00\"; cat file; perl -e "print \"More bytes\"x16 . \"\r\n\"";) | netcat ip port
好的,讓我們分解一下。子shell 在鏈中執行其內容(即,將它們分組)。這實際上具有直覺意義,因為子shell 是通過簡單地將命令鏈用
()
. 但是,除了在執行時將子shell 的內容組合在一起之外,您仍然可以像使用單個命令一樣使用子shell。也就是說,子shell 仍然有一個stdin
,stdout
因此stderr
您可以通過管道將內容傳入子shell 和從子shell 傳出。另一方面,命令替換與簡單地將命令連結在一起並不是一回事。相反,命令替換的作用有點像變數訪問,但帶有函式呼叫。與命令不同,變數沒有標准文件描述符,因此您不能通過管道將任何內容傳入或傳出變數(一般來說),命令替換也是如此。
為了更清楚地說明這一點,以下是一組可能不清楚(但準確)的範例和一組我認為可能更容易理解的範例。
假設該
date -u
命令給出以下內容:Thu Jul 2 13:42:27 UTC 2015
但是,我們想要操縱這個命令的輸出。所以,讓我們把它變成這樣的東西
sed
:user@host~> date -u | sed -e 's/ / /g' Thu Jul 2 13:42:27 UTC 2015
哇,那很有趣!以下內容完全等同於上述內容(除非您可以在有關您的 shell 的手冊頁中閱讀一些環境差異):
user@host~> (date -u) | sed -e 's/ / /g' Thu Jul 2 13:42:27 UTC 2015
這應該不足為奇,因為我們所做的只是 group
date -u
。但是,如果我們執行以下操作,我們會得到一些起初看起來有點奇怪的東西:user@host~> $(date -u) | sed -e 's/ / /g' command not found: Thu
這是因為
$(date -u)
相當於準確地輸入date -u
輸出的內容。所以以上等價於以下內容:user@host~> Thu Jul 2 13:42:27 UTC 2015 | sed -e 's/ / /g'
當然,這會出錯,因為
Thu
它不是命令(至少不是我所知道的);它當然不會通過管道傳輸任何東西stdout
(因此sed
永遠不會得到任何輸入)。但是,由於我們知道命令替換的行為類似於變數,因此我們可以輕鬆解決這個問題,因為我們知道如何將變數的值通過管道傳遞給另一個命令:
user@host~> echo $(date -u) | sed -e 's/ / /g' Thu Jul 2 13:42:27 UTC 2015
但是,與 bash 中的任何變數一樣,您可能應該用
""
.現在,對於可能更簡單的範例;考慮以下:
user@host~> pwd /home/hypothetical user@host~> echo pwd pwd user@host~> echo "$(pwd)" /home/hypothetical user@host~> echo "$HOME" /home/hypothetical user@host~> echo (pwd) error: your shell will tell you something weird that roughly means “Whoa! you tried to have me echo something that isn't text!” user@host~> (pwd) /home/hypothetical
我不知道如何描述它比這更簡單。命令替換就像變數訪問一樣工作,其中子shell 仍然像命令一樣執行。