Ich schreibe ein Bash-Skript, das hat set -u
, und ich habe ein Problem mit der Erweiterung des leeren Arrays: Bash scheint ein leeres Array während der Erweiterung als nicht gesetzte Variable zu behandeln:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
bash: arr[@]: unbound variable
( declare -a arr
hilft auch nicht.)
Eine übliche Lösung hierfür besteht darin, ${arr[@]-}
stattdessen eine leere Zeichenfolge anstelle des ("undefinierten") leeren Arrays zu verwenden. Dies ist jedoch keine gute Lösung, da Sie jetzt nicht zwischen einem Array mit einer einzelnen leeren Zeichenfolge und einem leeren Array unterscheiden können. (@ -expansion ist etwas Besonderes in Bash, es erweitert sich "${arr[@]}"
in "${arr[0]}" "${arr[1]}" …
, was es zu einem perfekten Werkzeug zum Erstellen von Befehlszeilen macht.)
$ countArgs() { echo $#; }
$ countArgs a b c
3
$ countArgs
0
$ countArgs ""
1
$ brr=("")
$ countArgs "${brr[@]}"
1
$ countArgs "${arr[@]-}"
1
$ countArgs "${arr[@]}"
bash: arr[@]: unbound variable
$ set +u
$ countArgs "${arr[@]}"
0
Gibt es also einen Weg, um dieses Problem zu umgehen, außer die Länge eines Arrays in einem zu überprüfen if
(siehe Codebeispiel unten) oder die -u
Einstellung für dieses kurze Stück zu deaktivieren?
if [ "${#arr[@]}" = 0 ]; then
veryLongCommandLine
else
veryLongCommandLine "${arr[@]}"
fi
Update: Entfernt - bugs
Tag aufgrund Erklärung von Ikegami.
"${arr[@]}"
. Vermisse ich etwas Soweit ich sehen kann, funktioniert es zumindest in5.x
.Laut Dokumentation,
Es wurde keinem Index ein Wert zugewiesen, daher ist das Array nicht festgelegt.
Obwohl die Dokumentation darauf hinweist, dass hier ein Fehler angebracht ist, ist dies seit 4.4 nicht mehr der Fall .
Es gibt eine Bedingung, die Sie inline verwenden können, um das zu erreichen, was Sie in älteren Versionen wollen: Verwenden Sie
${arr[@]+"${arr[@]}"}
anstelle von"${arr[@]}"
.Getestet mit Bash 4.2.25 und 4.3.11.
quelle
[@]+
tatsächlich funktioniert und warum der zweite${arr[@]}
keinen ungebundenen Fehler verursacht.${parameter+word}
Erweitert sich nur,word
wennparameter
es nicht deaktiviert ist.${arr+"${arr[@]}"}
ist kürzer und scheint genauso gut zu funktionieren.unset arr
,arr[1]=a
,args ${arr+"${arr[@]}"}
Vsargs ${arr[@]+"${arr[@]}"}
+
Erweiterung nicht auftritt (nämlich ein leeres Array), die Erweiterung durch nichts ersetzt , was genau das ist, worauf ein leeres Array erweitert wird.:+
ist unsicher, da es auch ein Einzelelement-('')
Array als nicht gesetzt behandelt und sich in ähnlicher Weise zu nichts erweitert, wodurch der Wert verloren geht.@ ikegamis akzeptierte Antwort ist auf subtile Weise falsch! Die richtige Beschwörung ist
${arr[@]+"${arr[@]}"}
:quelle
bash-4.4.23
:arr=('') && countArgs "${arr[@]:+${arr[@]}}"
produziert1
. Mit${arr[@]+"${arr[@]}"}
form können Sie jedoch zwischen leeren und nicht leeren Werten unterscheiden, indem Sie einen Doppelpunkt hinzufügen oder nicht hinzufügen.arr=('') && countArgs ${arr[@]:+"${arr[@]}"}
->0
,arr=('') && countArgs ${arr[@]+"${arr[@]}"}
->1
.Es stellte sich heraus, dass die Array-Behandlung in der kürzlich veröffentlichten Bash 4.4 (16.09.2016) geändert wurde (zum Beispiel in Debian Stretch verfügbar).
Jetzt gibt die Erweiterung für leere Arrays keine Warnung aus
quelle
bash-4.4.12
"${arr[@]}"
würde ausreichen.Dies kann eine weitere Option für diejenigen sein, die es vorziehen, arr [@] nicht zu duplizieren, und in Ordnung sind, eine leere Zeichenfolge zu haben
zu testen:
quelle
for
möchten, wird dies zu einer einzelnen leeren Zeichenfolge führen, wenn das Array undefiniert / als leer definiert ist, wo Sie möglicherweise den Schleifenkörper möchten nicht ausgeführt werden, wenn das Array nicht definiert ist.${arr[@]+"${arr[@]}"}
Status des leeren Arrays korrekt bei.@ ikegamis Antwort ist richtig, aber ich halte die Syntax für
${arr[@]+"${arr[@]}"}
schrecklich. Wenn Sie lange Array-Variablennamen verwenden, sieht es schneller als gewöhnlich spaghettiartig aus.Versuchen Sie stattdessen Folgendes:
Es sieht so aus, als ob der Bash-Array-Slice-Operator sehr nachsichtig ist.
Warum hat Bash die Behandlung des Randfalls von Arrays so schwierig gemacht? Seufzer. Ich kann nicht garantieren, dass Ihre Version einen solchen Missbrauch des Array-Slice-Operators zulässt, aber es funktioniert gut für mich.
Vorsichtsmaßnahme: Ich verwende
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Ihre Laufleistung kann variieren.quelle
"${arr[@]:0}"
gibt-bash: arr[@]: unbound variable
.arr=("_dummy_")
und die Erweiterung${arr[@]:1}
überall zu verwenden. Dies wird in anderen Antworten erwähnt, die sich auf Sentinel-Werte beziehen."Interessante" Inkonsistenz in der Tat.
Außerdem,
Obwohl ich damit einverstanden bin, dass das aktuelle Verhalten möglicherweise kein Fehler in dem Sinne ist, wie @ikegami es erklärt, können wir IMO sagen, dass der Fehler in der Definition (von "set") selbst liegt und / oder in der Tatsache, dass er inkonsistent angewendet wird. Der vorhergehende Absatz in der Manpage sagt
Das stimmt völlig mit dem überein, was es über die Erweiterung von Positionsparametern in sagt
"$@"
. Nicht, dass es keine anderen Inkonsistenzen im Verhalten von Arrays und Positionsparametern gibt ... aber für mich gibt es keinen Hinweis darauf, dass dieses Detail zwischen den beiden inkonsistent sein sollte.Auch weiterhin,
Ist
arr[]
es also nicht so ungebunden, dass wir keine Zählung seiner Elemente (0) oder eine (leere) Liste seiner Schlüssel erhalten können? Für mich sind diese sinnvoll und nützlich - der einzige Ausreißer scheint die${arr[@]}
(und${arr[*]}
) Erweiterung zu sein.quelle
Ich ergänze @ ikegami (akzeptiert) und @ kevinarpe Antworten (auch gut).
Sie können
"${arr[@]:+${arr[@]}}"
das Problem umgehen. Die rechte Seite (dh danach:+
) stellt einen Ausdruck bereit, der verwendet wird, falls die linke Seite nicht definiert / null ist.Die Syntax ist arkan. Beachten Sie, dass die rechte Seite des Ausdrucks einer Parametererweiterung unterzogen wird. Daher sollte besonders auf ein konsistentes Anführungszeichen geachtet werden.
Wie bei @kevinarpe erwähnt, besteht eine weniger arkane Syntax darin, die Array-Slice-Notation
${arr[@]:0}
(bei Bash-Versionen>= 4.4
) zu verwenden, die ab Index 0 auf alle Parameter erweitert wird. Außerdem sind weniger Wiederholungen erforderlich. Diese Erweiterung funktioniert unabhängig davonset -u
, sodass Sie sie jederzeit verwenden können. Die Manpage sagt (unter Parametererweiterung ):Dies ist das von @kevinarpe bereitgestellte Beispiel mit alternativer Formatierung, um die Ausgabe als Beweis zu platzieren:
Dieses Verhalten variiert je nach Version von Bash. Möglicherweise haben Sie auch bemerkt, dass der Längenoperator unabhängig davon
${#arr[@]}
immer nach0
leeren Arrays auswertetset -u
, ohne einen "ungebundenen Variablenfehler" zu verursachen.quelle
:0
schlägt die Redewendung in Bash 4.2 fehl, sodass dies kein sicherer Ansatz ist. Siehe meine Antwort .Hier sind einige Möglichkeiten, um so etwas zu tun: eine mit Sentinels und eine mit bedingten Anhängen:
quelle
Interessante Inkonsistenz; Auf diese Weise können Sie etwas definieren, das "nicht als gesetzt betrachtet" wird und dennoch in der Ausgabe von angezeigt wird
declare -p
UPDATE: Wie bereits erwähnt, wurde in 4.4 behoben, nachdem diese Antwort veröffentlicht wurde.
quelle
echo ${arr[@]}
(aber vor Bash 4.4 wird immer noch ein Fehler angezeigt).echo $arr[@]
hätten, hätten Sie gesehen, dass die Fehlermeldung anders ist.Der einfachste und kompatibelste Weg scheint zu sein:
quelle