將 stdout 和 stderr 重定向到 ‘&&’ 鏈中間的 null
TL;博士
我對此進行了研究,您可以在“更多詳細資訊和我的嘗試”部分中查看我發現的內容。
我的命令是
date && echo "hi 1" && echo "1/0" | bc >/dev/null 2>&1 && echo "hi 2" && date +'%s'
我期望/想要的輸出類似於
Tue, Nov 17, 2020 3:13:49 PM hi 1
讓它停下來。我的想法是表達式的“
echo
/bc
”部分(括號表示假定的操作優先級)
<part-before>
(echo "1/0" | bc >/dev/null 2>&1
)&&
<part-after>
應該做到這一點,這樣
<part-after>
就不會達到。但是,我的實際輸出(date
現在的命令)是Tue, Nov 17, 2020 3:13:49 PM hi 1 hi 2 1605651229
問題:我怎樣才能讓它失敗並停止在被零除的問題上而不輸出任何東西?
(在此之後的任何內容都是為了提供更多詳細資訊。)
快速說明:即使沒有重定向,除零失敗也不會停止
&&
鏈,即短路-‘和’-運算符鏈。看哪,bballdave025@MACHINE ~ $ date && echo "hi 1" && echo "1/0" | bc && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:14:47 PM hi 1 Runtime error (func=(main), adr=3): Divide by zero hi 2 1605651287 bballdave025@MACHINE ~ $
我想使用該
bc
方法,主要是為了避免隨後出現的 bash-isms$((
))
。(說到 bash-isms,嘗試&>/dev/null
並沒有改變任何東西。)我想最終得到類似以下範例的內容bballdave025@MACHINE ~ $ date && echo "hi 1" && echo "$((1/0))" >/dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:15:17 PM hi 1 -bash: 1/0: division by 0 (error token is "0") bballdave025@MACHINE ~ $
除了我不希望出現錯誤。只要不輸出除以零的錯誤結果,即不是
echo
-ed (這違背了重定向的目的stdout
) ,我就可以讓錯誤消失,stderr
就像這樣### NOT WHAT I WANT, other than what is output ### bballdave025@MACHINE ~ $ date && echo "hi 1" && ((1/0)) >/dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:15:44 PM hi 1 bballdave025@MACHINE ~ ### NOT WHAT I WANT, other than what is output. ###
在更理論上的說明上:有些東西正在將 0(零)傳遞給
&&
; 它是什麼?重定向不是命令。
我現在不知道該去哪裡找。
編輯:我做了更多的研究。我想補充兩點。
- 我找到了幾個來源。其中一個(已歸檔)我認為可能是關鍵,但我仍然不明白它是如何應用的。另一個(歸檔)描述了短路,我認為這可能很重要。
- 當我不小心替換為 時,我發現了一個奇怪的行為
>/dev/null
,因此在除零問題之後>dev/null
引入了一個錯誤。這是我看到的,bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:41:39 PM hi 1 -bash: dev/null: No such file or directory bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:41:48 PM hi 1 -bash: dev/null: No such file or directory bballdave025@MACHINE ~ $
老實說,這讓我更加困惑,特別是考慮到第一個可能是關鍵來源。
更多細節和我的嘗試
我試圖了解
&&
鍊和重定向,/dev/null
以建立一個包含更大執行檔的更長鏈。我也想更好地理解命令列表分隔符(歸檔),一般來說,無論這些執行檔如何使用&&
,我真的很想了解這裡發生了什麼。在繼續我的真實範例之前,我一直在研究這個玩具範例。我一直在嘗試“倒退”,看看是否能找到問題所在。也許這對弄清楚事情很有用。
bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >/dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:16:41 PM hi 1 hi 2 1605651401 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >/dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:16:52 PM hi 1 hi 2 1605651412 ## LET'S SEE WHAT GETS SENT TO /dev/null bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >abc 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:17:06 PM hi 1 hi 2 1605651426 bballdave025@MACHINE ~ $ cat abc 1 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >abc 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:17:18 PM hi 1 hi 2 1605651438 bballdave025@MACHINE ~ $ cat abc Runtime error (func=(main), adr=3): Divide by zero ## LET'S TRY GIVING IT ANOTHER ERROR RIGHT AFTER bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:24:35 PM hi 1 -bash: dev/null: No such file or directory bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:25:00 PM hi 1 -bash: dev/null: No such file or directory bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >abc 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:25:14 PM hi 1 hi 2 1605651914 bballdave025@MACHINE ~ $ cat abc 1 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >abc 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:25:24 PM hi 1 hi 2 1605651924 bballdave025@MACHINE ~ $ cat abc Runtime error (func=(main), adr=3): Divide by zero bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >abc/def 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:25:41 PM hi 1 -bash: abc/def: Not a directory bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >abc/def 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:25:46 PM hi 1 -bash: abc/def: Not a directory ## GO BACK WITHOUT `bc` and without redirection bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" >/dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:29:03 PM hi 1 hi 2 1605652143 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" >/dev/null 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:29:12 PM hi 1 hi 2 1605652152 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" >abc 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:29:21 PM hi 1 hi 2 1605652161 bballdave025@MACHINE ~ $ cat abc 1/1 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" >abc 2>&1 && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:29:29 PM hi 1 hi 2 1605652169 bballdave025@MACHINE ~ $ cat abc 1/0 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:30:09 PM hi 1 1/1 hi 2 1605652209 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:30:14 PM hi 1 1/0 hi 2 1605652214 bballdave025@MACHINE ~ $ a=1; b=1; c=0; date && echo "hi 1" && echo "hi 2" && date +'%s' Tue, Nov 17, 2020 3:30:26 PM hi 1 hi 2 1605652226 bballdave025@MACHINE ~ $
這是一個混合了 XY 問題、關於管道、輸出和劃分的問題。我會嘗試澄清它們,儘管我覺得沒有一個“單一”的問題需要回答。
外殼語言
分組
echo "before" && echo inside1 && echo inside2 && echo "after"
對多個語句進行分組的方法是使用圓括號或大括號:
echo "before" && ( echo inside1 && echo inside2 ) && echo "after" echo "before" && { echo inside1 && echo inside2; } && echo "after"
()
將在子shell中執行,而{ }
只是在目前環境中形成一個列表。兩者都可用於重定向該部分的輸出。echo "before" && ( echo inside1 && echo inside2 ) > /dev/null && echo "after" echo "before" && { echo inside1 && echo inside2; } > /dev/null && echo "after"
返回值
每個命令在完成時都會返回一個值。它保存在上面,因此您可以在執行後立即
$?
查看它。echo $?
一致認為返回零表示成功,非零表示失敗。無論該計劃可能認為是成功的。and
&&
和 or||
外殼運算符使用此概念。還有幾個方便的小工具被呼叫true
,false
它們總是分別返回零(成功)和非零(失敗)。if true && false; then echo Yes else echo No fi
當您連結一系列 時
&&
,僅當左手返回成功時才執行運算符的右手。例如,您可能想要創建一個名為的文件夾並將
photos
所有文件移到那裡。你可以先試試
mv *.jpg photos
但如果只有一張照片,沒有
photos
文件夾,它會將照片重命名為photos
.如果文件夾創建成功,您可以使用
&&
僅移動圖像:mkdir photos && mv *.jpg photos
如果
mkdir
失敗,mv
則不執行。之所以如此,是因為失敗
mkdir
時將返回非零。但是,如果文件夾
photos
已經存在,mkdir
也會返回錯誤條件(無法創建文件夾)。所以更複雜的檢查可能是:
( test -d photos || mkdir photos ) && mv *.jpg photos/
如果 photos 已經是一個目錄,它不會嘗試創建它,而是會跳轉到移動圖像。如果
photos
不是目錄,但它能夠製作它,它也會成功並移動照片。如果照片不是文件夾(或指向文件夾的符號連結)且未創建,則左側部分
&&
將失敗並且照片不會移動。旁注:對於這個特定的範例,您可以使用更簡單的
mkdir -p photos && mv *.jpg photos/
,因為-p
flag 使 mkdir 創建中間文件夾,並且如果該文件夾已經存在,則不會退出,但它不那麼具有指導意義。管道
當你有一個管道時,預設情況下它的返回值是最右邊的命令之一。如果你執行
command1 | command2 | command3
整個管道的返回值將是
command3
.command1
或command2
返回非零無關緊要 。這可以通過設置選項來更改,pipefail
以便返回值將是管道中返回 non-zero 的最右邊的命令之一。被零除
在您的情況下,
echo 1/0 | bc
不會停止管道,因為bc
它確實返回了 0 程式碼,即使它在螢幕上提供了關於嘗試除以零的診斷。直接做
$((1/0))
是有問題的,因為錯誤發生在擴展上。您可以將其包裝在一個新的 shell 程序中:echo hi1 && bash -c 'echo $((1/0))' 2> /dev/null && echo hi2
甚至只是一個子shell
echo hi1 && ( echo $((1/0))' ) 2> /dev/null && echo hi2
還有實際檢查除數是否為零的基本解決方案。例如:
division() { if [ "$2" -eq 0 ]; then return 1 fi echo "$(( $1 / $2 ))" } echo hi1 && division 1 0 && echo hi2
你甚至可以用一個函式做類似的事情
bc
(雖然它會輸出一個 0 而不是空的):define division(x,y) { if (y == 0) return; return x/y; } division(1,0)
即使沒有重定向,除零失敗也不會停止 && 鏈,即短路的“與”運算符鏈。
GNU 公元前:
$ echo "1/0" | bc Runtime error (func=(main), adr=3): Divide by zero $ echo $? 0
忙箱:
$ echo "1/0" | busybox bc bc: divide by zero $ echo $? 1
名稱
bc - 任意精度算術語言
EXIT STATUS
應返回以下退出值:
0 - 所有輸入文件均已成功處理。
未指定——發生錯誤。
基本原理
錯誤條件的退出狀態未指定有幾個原因:
- bc 實用程序用於互動式和非互動式情況。不同的退出程式碼可能適用於這兩種用途。
- 目前尚不清楚何時應該給出非零退出;被零除、未定義的函式和語法錯誤都是可能的。
- 目前尚不清楚退出狀態具有什麼實用程序
它不會破壞鏈條,因為您的 bc 不認為除以零是值得返回虛假退出狀態的東西。
但是,如果 GNU bc 無法讀取輸入文件,它確實會返回一個虛假的退出狀態。
我想使用 bc 方法,主要是為了避免像 $(( )) 這樣的 bash 主義。
$(( ))
不是 Bashism,它是POSIX shell 語言(算術擴展)的一部分。我不確定這裡的上下文是什麼,除以零與條件和
&&
鏈的關係,以及你最終要做什麼,所以很難提供任何建議。依賴於檢測除以零的替代方法是在首先嘗試除法之前檢查零除數。