Bash 與 ksh 管道
我在 ksh 中的腳本遇到了一些問題。FWIW 我無法克服的問題是,當我使用這樣的結構時
command | while read VAR1 do many.commands using $VAR1 done
我經常發現我的腳本不會對通過管道傳輸到 while 的每一行執行循環。為了測試這一點,我將結構更改為
command > /tmp/tempfile cat -n /tmp/tempfile >&2 cat /tmp/tempfile | while read VAR1 etc
這證明輸出中有很多行。
此外,我在執行之後立即添加一個額外的行,比如
echo DEBUGGING: $VAR1 >&2
這證明循環只執行一次。我真的很難過。
一種並不總是可行的解決方法是
for X in $(cat /tmp/tempfile ) do ... done
然後這可以正常工作,但除了我討厭這個結構之外,這意味著您在命令行上擴展整個輸入數據(有硬限制)
看來 bash 在處理這種事情方面比 ksh 更好。特別是,這似乎與讀取呼叫失敗有關,但如果循環需要很長時間才能執行,則不會重試。
然而,bash 似乎沒有內置的“讀取”功能,這意味著我的大部分腳本都需要重新編寫。我經常使用大型結構,例如
command1 | command2 | while read SOMEVAR; do awk -F: "... long awk program" | sed "long sed program" ; done | sort -u | tail -1 | read FINAL_ANSWER
問題是 bash 使用 /usr/bin/read ,正如預期的那樣,它會盡快丟棄 FINAL_ANSWER 的結果。明顯的解決方法是更換
| read FINAL_ANSWER
和
> /tmp/final_answer && FINAL_ANSWER="$(cat /tmp/final_answer)"
所以….這裡有任何腳本大師能夠對此有所了解嗎?我故意沒有在此處發布我的真實腳本,因為它們是為客戶開發的敏感解決方案的一部分,並且因為我不希望腳本的實際細節混淆問題。
我經常使用“讀取時”格式。它通常有效。事實上,在 25 年的 shell 腳本編寫中,我從未遇到過問題。現在我遇到了問題。非常令人沮喪。令人困惑。
最初我認為 while read 只是接收或傳遞第一行輸入。但後來我發現了一種情況,當我一遍又一遍地執行腳本時,它會越來越深入地執行到輸入中。具體來說,我有一些東西
command | while read NEXT_ONE DONEFLAG do if [ $DONEFLAG = "yes" ] then echo Already completed work for $NEXT_ONE else dowork $NEXT_ONE && set_flag $NEXT_ONE fi done
事實證明,在每次執行腳本時,它都會執行
dowork
一次。是什麼並不重要dowork
,只要它需要幾秒鐘以上。發生某種外殼管道超時,然後輸入的其餘部分消失。Google告訴我 dtksh 可能會解決這個問題(顯然它會重試讀/寫什麼的,我沒有讀夠)我看到 dtksh 存在於 /usr/st/bin/dtksh
這是誰?我不喜歡使用我不知道的 shell,但是將腳本的一小部分拆分為子腳本,使用 /usr/dt/bin/dtksh 作為解釋器可能是值得的。
有什麼建議嗎?
編輯:提供一個範例,說明為什麼我不能使用 bash 作為 ksh 作為解釋器的替代品:
sol10-primary> # cat test.sh #!/bin/ksh echo hello| read VAR1 echo $VAR1 sol10-primary> # ./test.sh hello sol10-primary> # sed 's/ksh/bash/' <test.sh >test2.sh sol10-primary> # chmod +x test2.sh sol10-primary> # ./test2.sh sol10-primary> #
你的問題有點囉嗦。我將回答您觀察到的 ksh 和 bash 之間的區別,這似乎是核心部分。
當涉及到腳本時,您可能遇到了 ksh 和 bash 之間的 #1 不兼容問題。ATT ksh(ksh88 和 ksh93)和 zsh 在父 shell 的管道中執行最後一個(最右邊的)命令,而其他 shell(Bourne、ash、bash、pdksh、mksh)執行所有命令,包括子 shell 中的最後一個命令.
這是一個簡單的測試程序:
msg="a subshell" true | msg="the parent shell" echo "This shell runs the last command of a pipeline in $msg"
在 ATT ksh 和 zsh 中,第二次分配 to
msg
在父 shell 中執行,因此效果在管道之後可見。在其他 shell 中,此分配在子 shell 中執行,因此第一個分配保留在父 shell 中。一種解決方法是執行管道中的其餘腳本。這是讀取數據並在之後進行一些處理的常見習語:
output_some_stuff | { var= while IFS= read -r line; do var=$(process "$line") done use "$var" }
您似乎遇到了 ksh 錯誤。我建議升級到非錯誤版本。如果這不可能,請嘗試Stephane Chazelas 的解決方法。雖然您可以嘗試在 bash 中執行腳本,但它不是(也不會假裝是)ksh 的直接替代品;有很多 bash 沒有的 ksh 功能(反之亦然)。Bash 和 ksh 僅在其 POSIX 核心和其他一些核心功能(特別是數組、
[[ … ]]
和 聲明的函式中的局部變數typeset
)中兼容。您也可以嘗試 zsh,它在呼叫 as 時的
ksh
行為方式比 bash 更接近 ksh。但是,您可能會遇到不兼容的情況。