Bash
Bash:使用 tee 的 for 循環中的變數範圍
考慮:
numbers="1 111 5 23 56 211 63" max=0 for num in ${numbers[@]}; do [ $num -gt $max ]\ && echo "old -> new max = $max -> $num"\ && max=$num done | tee logfile echo "Max= $max"
如果我刪除
| tee logfile
max 變數正確列印為 211,但如果我把它留在我得到Max= 0
.到底是怎麼回事?
管道的每一側都在一個子shell 中執行。子shell 是原始shell 程序的副本,它以相同的狀態開始,然後獨立進化¹。在子 shell 中設置的變數不能轉義回父 shell。
在 bash 中,您可以使用程序替換,而不是使用管道。這與管道的行為幾乎相同,除了循環在原始 shell 中
tee
執行並且只在子 shell 中執行。for num in "${numbers[@]}"; do if ((num > max)); then echo "old -> new max = $max -> $num" max=$num fi done > >(tee logfile) echo "Max= $max"
當我這樣做時,我在您的腳本中更改了一些內容:
- 除非您知道為什麼需要將它們排除在外,否則請始終在變數替換周圍使用雙引號。
&&
當你的意思是不要使用if
。如果使用命令的返回狀態,它的可讀性較差並且沒有相同的行為。- 由於我使用的是特定於 bash 的構造,因此我不妨使用算術語法。
請注意,管道解決方案和 subshell 解決方案之間存在細微差別:管道命令在兩個命令都退出時完成,而具有程序替換的命令在主命令退出時完成,而無需等待程序替換中的程序。這意味著當您的腳本完成時,日誌文件可能沒有完全寫入。
另一種方法是使用其他通道從子shell 到原始shell 程序進行通信。您可以使用臨時文件(靈活,但更難做正確 - 將臨時文件寫入適當的目錄,避免名稱衝突(使用
mktemp
),即使出現錯誤也將其刪除)。或者您可以使用另一個管道來傳達結果並在命令替換中獲取結果。max=$({ { for … done; echo "$max" >&4 } | tee logfile >&3; } 4>&1) 3&>1
的輸出
tee
轉到文件描述符 3,該文件描述符被重定向到腳本的標準輸出。的輸出echo "$max"
轉到文件描述符 4,該文件描述符被重定向到命令替換。