Gibt es eine Möglichkeit, $ x in zsh zu durchlaufen, wenn es sich um eine Zeichenfolge handelt und mit Bash kompatibel ist?
Ja!. Eine var-Erweiterung wird in zsh nicht (standardmäßig) aufgeteilt, Befehlserweiterungen jedoch . Daher können Sie sowohl in Bash als auch in zsh Folgendes verwenden:
x="one two three"
for i in $( echo "$x" )
do
echo "$i"
done
In der Tat, der obige Code funktioniert in allen Bourne - Shell Nachkommen (aber nicht in der ursprünglichen Bourne, ändern $(…)
zu , `…`
um es dort arbeiten).
Der obige Code hat immer noch einige Probleme mit Globbing und der Verwendung von Echo. Lesen Sie weiter.
In zsh wird eine var-Erweiterung wie $var
standardmäßig nicht aufgeteilt (auch nicht glob).
Dieser Code hat keine Probleme (wird nicht auf alle Dateien im pwd erweitert) in zsh:
var="one * two"
printf "<%s> " ${var}; echo
Teilt var aber auch nicht durch den Wert von IFS.
Für zsh kann die Aufteilung auf IFS folgendermaßen erfolgen:
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`).
Aber keine dieser Optionen ist portabel für Bash (oder eine andere Shell außer ksh für -A
).
Es read
könnte hilfreich sein , zu einer älteren Syntax zurückzukehren, die von beiden Shells gemeinsam genutzt wird .
Dies kann jedoch nur für Trennzeichen mit einem Zeichen (nicht IFS) und nur dann funktionieren , wenn das Trennzeichen in der Eingabezeichenfolge vorhanden ist:
# ksh, zsh and bash(3.0+)
t1(){ set -f;
while read -rd "$delimiter" i; do
echo "|$i|"
done <<<"$x"
}
Wo $1
ist ein Trennzeichen für ein Zeichen ?
Das leidet immer noch unter der Erweiterung der Globbing-Zeichen ( *
, ?
und [
), daher set -f
ist a erforderlich. outarr
Stattdessen können wir eine Array-Variable festlegen :
# for either zsh or ksh
t2(){ set -f; IFS="$delimiter" read -d $'\3' -A outarr < <(printf '%s\3' "$x"); }
Und die gleiche Idee für Bash:
# bash
t3(){ local -; set -f; mapfile -td "$1" outarr < <(printf '%s' "$x"); }
Der Effekt von set -f
wird in der Bash-Funktion mithilfe von wiederhergestellt local -
.
Das Konzept könnte sogar auf eine begrenzte Shell wie Dash erweitert werden:
# 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
}
Nein <<<
, nein <(…)
und nein read -A
oder readarray
verwendet, aber es funktioniert (für ein Zeichenbegrenzer ) mit Leerzeichen, Zeilenumbrüchen und / oder Steuerzeichen in der Eingabe.
Aber es ist viel einfacher, einfach zu tun:
t9(){ set -f; outarr=( $(printf '%s' "$x") ); }
Leider versteht zsh das nicht local -
, daher muss der Wert von set -f
wie folgt wiederhergestellt werden:
t99(){ oldset=$(set +o); set -f; outarr=( $( printf '%s' "$x" ) ); eval "$oldset"; }
Jede der oben genannten Funktionen kann aufgerufen werden mit:
IFS=$1 delimiter=$1 $2
Dabei ist das erste Argument $
das Trennzeichen (und IFS) und das zweite Argument die aufzurufende Funktion (t1, t2,… t9, t99). Dieser Aufruf setzt den Wert von IFS nur für die Dauer des Funktionsaufrufs, der beim Beenden der aufgerufenen Funktion auf seinen ursprünglichen Wert zurückgesetzt wird.
bash
. Der einzige Vorteil ist , dass es in der Lage sein wird , zu handhaben;
,|
,(
, usw. , aber nicht*
in derx
Variable. Ihre anfängliche Lösung (mit eval) war einfach besser.