この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
zshが文字列で、Bashと互換性のある方法でzshで繰り返す方法はありますか?
zshがBashをエミュレートできることを知っていますが、setopt shwordsplit
設定することはできません。アドホックforループはBashでは動作しないからです。
`
答え1
$xが文字列でBashと互換性がある場合、zshで$xを繰り返す方法はありますか?
はい! 。 var拡張はzshでは(デフォルトでは)分割されませんが、コマンド拡張子は次のとおりです。。したがって、Bashとzshの両方で次のものを使用できます。
x="one two three"
for i in $( echo "$x" )
do
echo "$i"
done
実際、上記のコードはすべてのBourneシェルの子孫で同じように機能します(ただし、元のBourneでは機能しないため、変更して動作するようにしてください)$(…)
。`…`
上記のコードには、まだワイルドカードとエコーの使用に関するいくつかの問題があります。読んでください。
zshでは、var拡張のようなものはデフォルトで$var
は分割されません(グローブも同じです)。
このコードはzshで問題なく動作します(pwdのすべてのファイルには展開されません)。
var="one * two"
printf "<%s> " ${var}; echo
ただし、varはIFS値に分割されません。
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を除く他のシェル-A
)に移植することはできません。
両方のシェルで共有する古い構文を使用すると役に立ちますread
。
ただし、これは以下にのみ適用されます。文字区切り記号(IFSではありません)入力文字列に区切り文字がある場合にのみ、次のようになります。
# ksh, zsh and bash(3.0+)
t1(){ set -f;
while read -rd "$delimiter" i; do
echo "|$i|"
done <<<"$x"
}
$1
一つはどこにありますか?キャラクター分離記号。
*
?
それでもワイルドカード文字(、および)拡張が適用されるため、[
これが必要です。set -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
}
いいえ<<<
、いいえ、いいえ<(…)
、read -A
またはreadarray
使用されていますが、動作します(例:キャラクター入力にスペース、改行、および/または制御文字を含む区切り文字。
しかし、単にこれを行う方がはるかに簡単です。
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)であり、2番目のパラメータは呼び出す関数(t1、t2、... t9、t99)です。この呼び出しは、関数呼び出し中にIFS値のみを設定し、呼び出された関数が終了したときに元の値として返します。
答え2
if type emulate >/dev/null 2>/dev/null; then emulate ksh; fi
zshsh_word_split
ではemulate
。
答え3
eval(= evil)を使用するのが怖くない場合:
x="one two three"
eval "x=($x)"
for i in ${x[@]}; do
echo $i
done