Bash

Bash 與 ksh 管道

  • February 15, 2013

我在 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 中,第二次分配 tomsg在父 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。但是,您可能會遇到不兼容的情況。

引用自:https://unix.stackexchange.com/questions/64874