
我經常在網路上看到教程,用不同的符號連接各種命令。例如:
command1 | command2
command1 & command2
command1 || command2
command1 && command2
其他人似乎將命令連接到文件:
command1 > file1
command1 >> file1
這些是什麼東西?他們叫什麼?他們在做什麼?還有更多嗎?
答案1
這些被稱為 shell 操作符,是的,它們還有更多。我將簡要概述兩個主要類別中最常見的類別,控制操作員和重定向操作符,以及它們如何在 bash shell 中運作。
A. 控制操作符
在 shell 指令語言中,執行控制功能的標記。
它是以下符號之一:
& && ( ) ; ;; <newline> | ||
在|&
bash中。
A!
是不是一個控制操作符但是保留字。裡面就變成邏輯NOT[否定運算子了]算術表達式以及內部測試結構(同時仍需要空格分隔符號)。
A.1 列出終止符
;
:將在另一個命令完成後執行一個命令,無論第一個命令的結果如何。command1 ; command2
首先command1
是在前台運行,一旦完成,command2
將運行。
不在字串文字中或在某些關鍵字之後的換行符是不是相當於分號運算子。分隔的簡單命令清單;
仍然是清單- 就像在 shell 的解析器中一樣,在執行之前仍然必須繼續讀入;
分隔簡單命令後面的簡單命令,而換行符可以分隔整個命令列表或列表列表。差異很微妙,但很複雜:假設 shell 之前沒有讀取換行符後的資料的命令,則換行符號標記 shell 可以開始評估它已讀入的簡單命令的點,而分號則;
可以不是。
&
:這將在後台運行命令,允許您繼續在同一 shell 中工作。command1 & command2
這裡,command1
在後台啟動並command2
立即開始在前台運行,無需等待command1
退出。
之後的換行符command1
是可選的。
A.2 邏輯運算符
&&
:用於建立 AND 列表,它允許您僅在另一個命令成功退出時才執行一個命令。command1 && command2
在這裡,command2
將在command1
完成後運行僅有的如果command1
成功(如果其退出代碼為 0)。這兩個命令都在前台運行。
這個指令也可以寫成
if command1
then command2
else false
fi
或者只是if command1; then command2; fi
忽略返回狀態。
||
:用於建置 OR 列表,它允許您僅在另一個命令退出失敗時才執行一個命令。command1 || command2
此處,command2
僅在失敗時才運行command1
(如果返回非 0 的退出狀態)。這兩個命令都在前台運行。
這個指令也可以寫成
if command1
then true
else command2
fi
或以更短的方式if ! command1; then command2; fi
。
請注意,&&
和||
是左關聯的;看shell 邏輯運算子 &&、|| 的優先權了解更多。
!
:這是一個保留字,充當「非」運算子(但必須有分隔符號),用於否定命令的返回狀態 - 如果命令返回非零狀態,則返回 0;如果返回狀態 0,則返回 1也是實用程序的邏輯NOTtest
。! command1 [ ! a = a ]
算術表達式中真正的 NOT 運算子:
$ echo $((!0)) $((!23))
1 0
A.3 管道操作符
|
:管道運算符,它將一個命令的輸出作為另一個命令的輸入傳遞。從管道運算子建構的命令稱為管道。command1 | command2
列印的任何輸出
command1
都會作為輸入傳遞給command2
.|&
2>&1 |
:這是bash 和 zsh 中的簡寫。它將一個命令的標準輸出和標準錯誤作為輸入傳遞給另一個命令。command1 |& command2
A.4 其他清單標點符號
;;
僅用於標記一個的結束案例陳述。 Ksh、bash 和 zsh 也支援;&
跳到下一個案例並;;&
(不在 ATT ksh 中)繼續測試後續案例。
(
並)
習慣於群組命令並在子 shell 中啟動它們。{
以及}
群組命令,但不要在子 shell 中啟動它們。看這個答案討論 shell 語法中各種類型的圓括號、中括號和大括號。
B. 重定向運算符
在 shell 指令語言中,執行重定向功能的令牌。它是以下符號之一:
< > >| << >> <& >& <<- <>
這些允許您控制命令的輸入和輸出。它們可以出現在簡單命令中的任何位置,也可以出現在命令之後。重定向按照它們出現的順序從左到右進行處理。
<
:輸入命令。command < file.txt
以上將command
在 的內容上執行file.txt
。
<>
:與上面相同,但文件開啟於讀+寫模式而不是只讀:command <> file.txt
如果該文件不存在,則會建立該文件。
該運算符很少使用,因為命令通常只讀不過,從他們的標準輸入它可以在許多特定情況下派上用場。
>
:將指令的輸出定向到文件中。command > out.txt
上面將把輸出儲存command
為out.txt
.如果該文件存在,則其內容將被覆蓋;如果該文件不存在,則將建立該文件。
該運算符也經常用於選擇是否應將某些內容列印到標準誤或者標準輸出:
command >out.txt 2>error.txt
在上面的範例中,>
將重定向標準輸出並2>
重定向標準錯誤。也可以使用重定向輸出1>
,但是,由於這是預設設置,因此1
通常會省略 ,並且將其簡單地寫為>
.
因此,要運行command
並file.txt
保存其輸出out.txt
以及任何錯誤訊息,error.txt
您將運行:
command < file.txt > out.txt 2> error.txt
>|
: 與 相同>
,但會覆蓋目標,即使 shell 已配置為拒絕覆蓋(使用set -C
或set -o noclobber
)。command >| out.txt
如果out.txt
存在,則 的輸出command
將取代其內容。如果它不存在,它將被創建。
>>
:與 相同>
,只是如果目標檔案存在,則附加新資料。command >> out.txt
如果out.txt
存在,則 的輸出command
將附加到其中已有的內容之後。如果它不存在,它將被創建。
>&
:(根據 POSIX 規範)當被包圍時數位(1>&2
) 或-
右側 (1>&-
) 僅重定向一檔案描述符或關閉它 (>&-
)。
A>&
後跟檔案描述符編號是重定向檔案描述子的可移植方式,也是>&-
關閉檔案描述符的可移植方式。
如果此重定向的右側是文件,請閱讀下一個條目。
>&
、&>
和:(>>&
另&>>
請參閱上文)重定向標準錯誤和標準輸出,分別進行替換或附加。command &> out.txt
的標準錯誤和標準輸出都command
將保存在 中out.txt
,覆蓋其內容或創建它(如果不存在)。
command &>> out.txt
與上面一樣,只是如果out.txt
存在,則 的輸出和錯誤command
將附加到其上。
該&>
變體起源於bash
,而該>&
變體來自 csh (幾十年前)。它們都與其他 POSIX shell 操作符衝突,並且不應該在可移植腳本中使用sh
。
<<
:這裡的文檔。它通常用於列印多行字串。command << WORD Text WORD
在這裡,
command
將採取一切,直到找到下一次出現的WORD
,Text
在上面的範例中,作為輸入 。雖然WORD
通常是EoF
或其變體,但它可以是您喜歡的任何字母數字(而不僅僅是)字串。當 的 任何部分WORD
被引用或轉義時,此處文件中的文字將按字面意思處理,並且不會執行擴展(例如對變數)。如果不加引號,變數將會擴展。有關更多詳細信息,請參閱bash手冊。如果要將 的輸出
command << WORD ... WORD
直接透過管道傳輸到另一個或多個命令中,則必須將管道放在與 相同的行上<< WORD
,不能將其放在終止 WORD 之後或後面的行上。例如:command << WORD | command2 | command3... Text WORD
<<<
:此處字串,與此處文件類似,但用於單行。這些僅存在於 Unix 連接埠或 rc(它的起源地)、zsh、ksh、yash 和 bash 的某些實作中。command <<< WORD
給出的任何內容都會WORD
被擴展,並且其值將作為輸入傳遞給command
.這通常用於將變數的內容作為命令的輸入傳遞。例如:
$ foo="bar"
$ sed 's/a/A/' <<< "$foo"
bAr
# as a short-cut for the standard:
$ printf '%s\n' "$foo" | sed 's/a/A/'
bAr
# or
sed 's/a/A/' << EOF
$foo
EOF
其他一些運算符 ( >&-
, x>&y
x<&y
) 可用於關閉或複製文件描述符。有關它們的詳細信息,請參閱 shell 手冊的相關部分(這裡例如 bash)。
這僅涵蓋了類別 Bourne shell 的最常見操作符。某些 shell 有一些自己的附加重定向運算子。
Ksh、bash 和 zsh 也有結構<(…)
,>(…)
和=(…)
(僅後一個zsh
)。這些不是重定向,而是流程替代。
答案2
關於“>”的警告
剛剛了解 I/O 重定向(<
和>
)的 Unix 初學者經常嘗試類似的事情
命令……輸入檔>同一個文件
或者
命令…<文件 >同一個文件
或者,幾乎等價地,
貓文件|命令…… >同一個文件
(grep
、sed
、cut
、sort
和spell
是人們試圖在此類結構中使用的命令範例。)使用者驚訝地發現這些情況會導致檔案變空。
在其他答案中似乎沒有提到的細微差別可以在第一句中找到重定向的部分重擊(1):
在執行命令之前,其輸入和輸出可能是重定向 使用 shell 解釋的特殊符號。
前五個單字應為粗體、斜體、底線、放大、閃爍、紅色並標有,以強調 shell 執行請求的重定向這一事實
在命令執行之前。也要記住
輸出重定向導致檔案…打開以進行寫入…。如果文件不存在則建立;如果它確實存在,它將被截斷為零大小。
所以,在這個例子中:
sort roster > roster
shell 開啟檔案進行寫入,在程式開始運行
roster
之前截斷它(即丟棄其所有內容) 。sort
當然,無法採取任何措施來恢復資料。人們可能會天真地期望
tr "[:upper:]" "[:lower:]" < poem > poem
可能會更好。因為 shell 處理從左到右的重定向,所以它會先打開
poem
以進行讀取(用於tr
標準輸入),然後再打開它進行寫入(用於標準輸出)。但這沒有幫助。儘管這一系列操作產生兩個檔案句柄,但它們都指向同一個檔案。當 shell 開啟檔案進行讀取時,內容仍然存在,但在程式執行之前它們仍然會被破壞。
那麼,該怎麼辦呢?
解決方案包括:
檢查您正在執行的程式是否具有自己的內部功能來指定輸出的去向。這通常由
-o
(或) 標記表示--output=
。尤其,sort -o roster roster
大致相當於
sort roster > roster
除了在第一種情況下,
sort
程式開啟輸出檔。它足夠聰明,直到後它已讀取所有輸入檔。同樣,至少某些版本
sed
有-i
(編輯我n place) 選項,可用來將輸出寫回輸入檔(同樣,後所有輸入均已讀取)。ed
/ex
、emacs
、pico
和vi
/等編輯器vim
允許使用者編輯文字檔案並將編輯後的文字保存在原始文件中。請注意,ed
(至少)可以非互動地使用。vi
有一個相關的功能。如果您鍵入,它會將編輯緩衝區的內容寫入:%!command
Entercommand
,讀取輸出,並將其插入緩衝區(取代原始內容)。
簡單但有效:
命令……輸入檔>臨時檔案 && MV臨時檔案 輸入檔
這有一個缺點,如果
input_file
是一個鏈接,它將(可能)被一個單獨的文件替換。此外,新檔案將歸您所有,並具有預設保護。特別是,這帶來了文件最終被全世界可讀的風險,即使原始文件input_file
不是。變化:
command … input_file > temp_file && cp temp_file input_file && rm temp_file
這仍然(可能)留下temp_file
世界可讀。更好的是:cp input_file temp_file && command … temp_file > input_file && rm temp_file
這些保留了檔案的連結狀態、擁有者和模式(保護),但可能會付出兩倍 I/O 的代價。 (您可能需要使用類似-a
或-p
on 的選項cp
來告訴它保留屬性。)command … input_file > temp_file &&
cp --attributes-only --preserve=all input_file temp_file &&
mv temp_file input_file
(為了可讀性而分成單獨的行)這保留了文件的模式(如果您是root,則保留文件的所有者),但使其歸您所有(如果您不是root),並使其成為一個新的,單獨的文件。
這個部落格 (文件的「就地」編輯)建議與解釋
{ R M輸入檔 && 命令…… >輸入檔; } <輸入檔
這要求
command
能夠處理標準輸入(但幾乎所有過濾器都可以)。該部落格本身稱這是一種有風險的拼湊行為,並不鼓勵其使用。這還將創建一個新的單獨文件(不連結到任何內容),該文件歸您所有並具有預設權限。moreutils 套件有一個名為的命令
sponge
:命令……輸入檔|海綿同一個文件
看這個答案了解更多。
以下是令我完全驚訝的事: 文法錯誤說:
[大多數這些解決方案]將在只讀檔案系統上失敗,其中「只讀」意味著您的
$HOME
將要可寫,但/tmp
將會只讀(預設)。例如,如果您有 Ubuntu,並且已啟動到故障復原控制台,那麼這種情況很常見。此外,這裡文檔操作符<<<
也不會在那裡工作,因為它/tmp
需要讀/寫 因為它也會向其中寫入一個臨時檔案。
(參見這個問題包括strace
'd 輸出)
在這種情況下,以下方法可能有效:
- 僅適用於進階用戶:
如果您的命令保證產生與輸入相同數量的輸出資料(例如,
sort
或tr
沒有-d
或選項-s
),你可以嘗試命令……輸入檔| dd =同一個文件轉換=notrunc
看這個答案 和這個答案了解更多信息,包括對上述內容的解釋,以及保證您的命令產生與輸入相同數量的輸出數據時可行的替代方案或更少(例如,grep
, 或cut
)。這些答案的優點是它們不需要任何可用空間(或它們需要很少的空間)。上面表格的答案 明確要求系統有足夠的可用空間,以便能夠同時容納整個輸入(舊)文件和輸出(新)文件;對於大多數其他解決方案(例如,和)來說,這顯然也不是正確的。例外:可能需要大量可用空間,因為在寫入任何輸出之前需要讀取其所有輸入,並且它可能會在臨時檔案中緩衝大部分(如果不是全部)資料。command … input_file > temp_file && …
sed -i
sponge
sort … | dd …
sort
- 僅適用於進階用戶:
命令……輸入檔1<>同一個文件
可能相當於dd
上面的答案。該語法開啟文件描述符上的指定文件n<> file
n
對於輸入和輸出 ,而不截斷它——有點像和的組合。注意:某些程式(例如,和)可能會拒絕在這種情況下運行,因為它們可以偵測到輸入和輸出是同一檔案。看n<
n>
cat
grep
這個答案 對於上述內容的討論,以及一個腳本,如果您的命令保證產生與輸入相同數量的輸出數據,則使該答案起作用或更少。
警告:我還沒有測試過彼得的腳本,所以我不保證它。
那麼,問題是什麼?
這是 U&L 上的熱門話題;它透過以下問題得到解決:
- 有沒有辦法就地修改文件?
- 如何用
iconv
轉換後的輸出取代輸入檔? - 為什麼該指令
shuf file > file
留下一個空文件? - 我可以在 Linux 中讀寫同一個檔案而不覆蓋它嗎?
- 重定向到與命令處理的來源文件相同的文件
- 為什麼這個
sort
指令給我一個空文件? - 將
tr
標準輸出重定向到文件 - grep:輸入檔「X」也是輸出
- 重定向運算子是否並行開啟檔案描述符?
- 重定向不覆蓋文件而只是產生一個空白文件
…這還不包括超級使用者或 Ask Ubuntu。我在這個答案中納入了上述問題答案中的許多信息,但不是全部。 (即,如需了解更多信息,請閱讀上面列出的問題及其答案。)
PS我有不與我上面引用的部落格的隸屬關係。
答案3
;
更多關於、&
、(
和 的觀察)
請注意,terdon 的答案中的某些命令可能為空。例如,你可以說
command1 ;
(沒有
command2
)。這相當於command1
(即,它只是
command1
在前台運行並等待其完成。相比之下,command1 &
(沒有)將在背景
command2
啟動,然後立即發出另一個 shell 提示字元。command1
相較之下,
command1 &&
、command1 ||
、 和command1 |
沒有任何意義。如果您鍵入其中一個,shell(可能)會假設該命令繼續到另一行。它將顯示輔助(繼續)shell 提示符,通常設定為>
,並繼續閱讀。在 shell 腳本中,它只會讀取下一行並將其附加到已讀取的內容中。 (請注意:這可能不是您想要發生的情況。)注意:某些 shell 的某些版本可能會將此類不完整的命令視為錯誤。在這種情況下(或者事實上,在任何
\
如果命令很長),可以在行尾添加反斜線 ( ) 來告訴 shell 繼續讀取另一行的命令:command1 && \ command2
或者
find starting-directory -mindepth 3 -maxdepth 5 -iname "*.some_extension" -type f \ -newer some_existing_file -user fred -readable -print
正如 terdon 所說,
(
and)
可用於將指令分組。關於它們與該討論「並不真正相關」的說法值得商榷。 terdon的回答中的一些命令可能是命令團體。例如,( command1 ; command2 ) && ( command3; command4 )
做這個:
- 運行
command1
並等待它完成。 - 然後,無論運行第一個命令的結果如何,都運行
command2
並等待它完成。 那麼,如果
command2
成功的話,- 運行
command3
並等待它完成。 - 然後,無論運行該命令的結果如何,都運行
command4
並等待它完成。
如果
command2
失敗,則停止處理命令列。- 運行
- 運行
在括號外面,
|
結合得非常緊密,所以command1 | command2 || command3
相當於
( command1 | command2 ) || command3
和
&&
比||
結合得更緊;
,所以command1 && command2 ; command3
相當於
( command1 && command2 ) ; command3
即,無論and/or
command3
的退出狀態如何,都將被執行。command1
command2