如何「擴展」bash 變數(包含的程式碼適用於 bash 但不適用於 zsh)

如何「擴展」bash 變數(包含的程式碼適用於 bash 但不適用於 zsh)

一個答案如很好地解釋瞭如何控制傳球全部變數到命令。

我想探索如何在每個參數的基礎上做到這一點。觀察(這已在 中進行了測試zsh):

$ program() { echo 1: $1 2: $2 3: $3; }

$ run() { program "$@"; }

$ run2() { echo `run $1`; }

$ run2 'a b' c
1: a b 2: 3:

我想要一種方法來傳遞第一個參數,它是一個包含空格的字串,並將該拼接$@樣式傳遞到另一個命令中。例如run2 "a b" c應該產生1: a 2: b 3:

至此,我已經解決了眼前的問題,因為雖然我的測試程式碼在 zsh 中測試時中斷,但一旦實現到實際的 bash 腳本中,它就可以運作。

這確實表明這可能依賴於字串和參數處理中的一些複雜性,這些不「安全」。因此,這更多的是請求評論以更穩健的方式可靠地實現此行為。

答案1

bash 和 zsh 之間重要的差異在於run2呼叫的方式run,特別是不加引號的效果$1

  • 在 zsh 中,run $1將「remove-if-empty」運算子應用於$1,即run使用傳遞給 的第一個參數進行調用run2,但如果第一個參數為run2空(或如果run2調用時不帶參數),則run調用時不帶任何參數。
  • 在其他 Bourne 風格的 shell(例如 bash)中,run $1將「split+glob」運算子應用於$1,即將第一個參數 分割為run2空格分隔的區塊1,將每個部分解釋為通配符模式2,並替換與一個或多個匹配的每個通配符模式會按匹配清單顯示更多文件。

因此,在 zsh 中使用參數進行run2 'a b' c呼叫(參數原封不動地傳遞),但在 bash 中使用兩個參數進行呼叫(分成以空格分隔的部分)。runa brunab

我想要一種方法來傳遞第一個參數,它是一個包含空格的字串,並將該拼接$@樣式傳遞到另一個命令中。例如run2 "a b" c應該產生1: a 2: b 3:

你的描述和你的例子說的是不同的事情。如果要將第一個參數傳遞給另一個命令,請使用run "$1"確保該參數未拆分。不變地傳遞參數是"$@".

看來您真正想要做的是將第一個參數分解為run2以空格分隔的區塊。在 bash 中,您可以透過關閉通配符擴充功能然後(假設未IFS變更預設值)使用不含引號的擴充來實現此目的。

run2 () ( set -f; run $1; )

echo "$(somecommand)"本質上相當於somecommand在子 shell 中運行,看來這就是您的意思,而不是echo $(somecommand)在命令的輸出上應用 split+glob,因此我刪除了多餘的 echo-command-substitution。)

在 zsh 中,您可以=在參數替換中使用字元來對值執行全域拆分(並且不進行通配)。

run2 () { run $=1; }

zsh 的語法與普通 sh 的語法不相容。如果您想從 zsh 取得 sh 腳本,您可以使用以下emulate建置:

emulate sh -c '. myscript.sh'

用於emulate ksh模擬 ksh(的某些功能)。這不會模擬所有 bash 功能,但它允許您使用陣列。

1更一般地說,基於 的值IFS
²除非已使用 關閉此功能set -f

答案2

歡迎來到引用和引用驚喜的世界。

主要問題是zsh預設不會按 IFS 字元進行拆分。
在那:它與所有其他外殼不同。

為了測試引用如何改變 shell 的工作方式,我們需要測試程式碼的引用版本和未引用版本的程式碼。

您的程式碼(新增幾個變數):

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa()  { program  "$@" ; }
run1()  { echo "`runa  $1 $2 $3 `"; }
run1    'a b' c

讓我詳細闡述細節。

假設您建立一個sh指向所在zsh位置的本地連結:

ln -s "/usr/bin/zsh" ./sh

另外,我們假設您將以下腳本複製到文件中so

使用引號和不帶引號的變數重複每個函數的腳本:

program() { printf '%02d-1: %6s   2: %6s    3: %6s' "$i" "$1" "$2" "$3"; }
runa() { program "$@"; }
runb() { program  $@ ; }

run1() { echo "`runa  "$1" "$2" "$3"`"; }
run2() { echo "`runb  "$1" "$2" "$3"`"; }
run3() { echo "`runa  $1 $2 $3`"; }
run4() { echo "`runb  $1 $2 $3`"; }

for i in `seq 4`; do
    run"$i" 'a b' c
done

然後,在執行時,我們會列印以下內容:

# Any shell (except zsh) results.
01-1:    a b   2:      c    3:
02-1:      a   2:      b    3:      c
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

只有第一個運行 (run1)(所有內容都被引用)保持'a b'連接。

然而,zsh 的行為就好像 all 一直被引用一樣:

# ZSH results.
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:    a b   2:      c    3:
04-1:    a b   2:      c    3:

模擬中的 zsh。

zsh如果稱為sh或 ,則應該模擬較舊的 shell ksh
但實際上這並不總是正確的:

$ ./sh ./so   # emulated sh
01-1:    a b   2:      c    3:
02-1:    a b   2:      c    3:
03-1:      a   2:      b    3:      c
04-1:      a   2:      b    3:      c

第二行與任何其他 shell 的第二行不同。

這是個好主意閱讀這個問題的答案

相關內容