在 zsh 和 Bash 中循環字元串
我想轉換這個 Bash 循環:
x="one two three" for i in ${x} do echo ${i} done
以這種方式同時使用 Bash 和 zsh
此解決方案有效:
x=( one two three ) for i in ${x[@]} do echo ${i} done
無論如何,我正在
x
從字元串修改為數組。
$x
當它是一個字元串並且以與 Bash 兼容的方式時,有沒有辦法在 zsh 中循環?我知道 zsh可以
setopt shwordsplit
模擬 Bash,但我不能為循環設置它,因為它在 Bash 中不起作用。`
if type emulate >/dev/null 2>/dev/null; then emulate ksh; fi
在 zsh 中,這會啟動使其與 ksh 和 bash 更兼容的選項,包括
sh_word_split
. 在其他 shell 中,emulate
不存在,所以這什麼都不做。
當它是一個字元串並且以與 Bash 兼容的方式時,有沒有辦法在 zsh 中循環 $x?
是的!。var 擴展在 zsh 中不拆分(預設情況下),但命令擴展是. 因此,在 Bash 和 zsh 中,您都可以使用:
x="one two three" for i in $( echo "$x" ) do echo "$i" done
事實上,上面的程式碼在所有 Bourne shell 後代中的工作方式都是一樣的(但在原始的 Bourne 中不一樣,更改
$(…)
為…
讓它在那里工作)。上面的程式碼在 globbing 和 echo 的使用方面仍然存在一些問題,請繼續閱讀。
$var
在 zsh 中,預設情況下不會拆分(也不是 glob)這樣的 var 擴展。此程式碼在 zsh 中沒有問題(不會擴展到 pwd 中的所有文件):
var="one * two" printf "<%s> " ${var}; echo
但也不會按 IFS 的值拆分 var。
對於 zsh,可以通過以下方式在 IFS 上進行拆分:
1. Call splitting explicitly: `echo ${=var}`. 2. Set SH_WORD_SPLIT option: `set -y; echo ${var}`. 3. Using read to split to a list of vars (or an array with `-A`).
但是這些選項都不能移植到 bash (或除 ksh for 之外的任何其他 shell
-A
)。使用兩個 shell 共享的舊語法:
read
可能會有所幫助。但這僅適用於一個字元定界符(不是 IFS),並且僅當定界符存在於輸入字元串中時:
# ksh, zsh and bash(3.0+) t1(){ set -f; while read -rd "$delimiter" i; do echo "|$i|" done <<<"$x" }
$1
單字元分隔符在哪裡。這仍然會受到萬用字元 (
*
,?
和[
) 擴展的影響,因此 aset -f
是必需的。而且,我們可以設置一個數組變數outarr
:# for either zsh or ksh t2(){ set -f; IFS="$delimiter" read -d $'\3' -A outarr < <(printf '%s\3' "$x"); }
對於 bash 也有同樣的想法:
# bash t3(){ local -; set -f; mapfile -td "$1" outarr < <(printf '%s' "$x"); }
的效果
set -f
在 bash 函式中通過使用local -
.這個概念甚至可以擴展到像破折號這樣的有限外殼:
# valid for dash and bash t4(){ local -; set -f; while read -r i; do printf '%s' "$i" | tr "$delimiter"'\n' '\n'"$delimiter"; echo done <<-EOT $(echo "$x" | tr "$delimiter"'\n' '\n'"$delimiter") EOT }
No
<<<
, no<(…)
和 noread -A
orreadarray
使用,但它適用於輸入中的空格、換行符和/或控製字元(對於一個字元分隔符)。但是簡單地做起來要容易得多:
t9(){ set -f; outarr=( $(printf '%s' "$x") ); }
可悲的是, zsh 不理解
local -
,因此set -f
必須將 的值恢復為:t99(){ oldset=$(set +o); set -f; outarr=( $( printf '%s' "$x" ) ); eval "$oldset"; }
上面的任何函式都可以通過以下方式呼叫:
IFS=$1 delimiter=$1 $2
其中第一個參數
$
是分隔符(和 IFS),第二個參數是要呼叫的函式(t1, t2, … t9, t99)。該呼叫僅在函式呼叫期間設置 IFS 的值,當被呼叫的函式退出時,該函式呼叫將恢復為其原始值。