Seltsames Verhalten von nicht initialisierten Arrays und nicht gesetzten Arrays

7

Ich schreibe ein Skript und habe ein unerwartetes Verhalten von nicht initialisierten und nicht gesetzten Array-Variablen entdeckt, das ich nicht verstehe.

Zuallererst die Länge:

$ echo ${#notset[@]}
0
$ uninitialized=
$ echo ${#uninitialized[@]}
1

Warum ist die uninitializedLänge 1? Sollte es nicht Null sein? Liegt es daran, dass eine Nullvariable als Array eines Nullelements betrachtet wird?

Diese Tatsache führt zu einigen Problemen. Angenommen, ich möchte ein Array erstellen und eine bestimmte Anzahl von Dingen basierend auf den Benutzerbefehlszeilenargumenten einfügen. Ich dachte, ich könnte so etwas wie (+) machen :

myarray=

if [ some-condition ]
then
    myarray[${#myarray[@]}]=some-value
fi

if [ some-condition2 ]
then
    myarray[${#myarray[@]}]=some-value2
elif [ some-condition3 ]
then
    myarray[${myarray[@]}]=some-value3
    myarray[${myarray[@]}]=some-value4
fi

Dies lässt jedoch den ersten Slot auf null, was mir nicht gefällt, und bricht auch einen von mir geschriebenen Code (*) . An dieser Stelle möchte ich sehen, ob das Array ein Element enthält. Wie soll ich das machen

[ -z "${myarray[@]}" ]

Löst einen Fehler aus, wenn das Array mehr als ein Element enthält.

[ -z "$myarray" ]

Schlägt fehl, weil das erste Element null ist, auch wenn das Array nicht leer ist.

Wie soll ich also steuern, dass ein Array nicht initialisiert ist?

Und könnte jemand erklären, was genau passiert, wenn es um Arrays und nicht festgelegte - nicht initialisierte Variablen geht?


(+) Ich weiß, dass ich es vermeiden könnte, die Variable zu "deklarieren", und es würde funktionieren, aber dieses Skript wird von einem Professor überprüft, und er mag es nicht, wenn Variablen an zufälligen Stellen definiert werden.

(*) Bevor ich dieses Ding ausprobierte, behielt ich die Länge des Arrays in einer anderen Variablen bei und hatte daher keine Probleme. Ich möchte jedoch vermeiden, diese Hilfsvariablen zu definieren, da ich weiß, dass ich die Länge ohne sie erhalten kann.

Bakuriu
quelle
Beachten Sie, dass uninitializedist nicht nicht initialisiert; Das erste Element ist die leere Zeichenfolge. foound ${foo[0]}sind fast austauschbar.
Chepner

Antworten:

11

Sie können den Unterschied sehen mit declare -p:

unset foo
declare -a foo
declare -p foo
# prints declare -a foo='()'
foo=
declare -p foo
# prints declare -a foo='([0]="")'

Wenn Sie ein leeres Array initialisieren möchten, ist die Ausgabe des ersten declare -pein guter Hinweis darauf, wie Sie es am besten deklarieren können:

declare -a array='()'

(Das declare -aTeil ist wahrscheinlich optional, ein einfaches array=()sollte genauso gut funktionieren.)

Wenn Sie testen möchten, ob ein Array 0 Elemente enthält, verwenden Sie den numerischen Vergleich für ${#array[@]}. Versuchen Sie nicht, eine test -zErweiterung durchzuführen, da dies in vielen Fällen nicht zum richtigen Ergebnis führt.

jw013
quelle
Ich wusste es nicht declare -p. Vielen Dank, dass Sie meine Zweifel gelöst haben. Das einzige, was mir nicht klar ist, ist dein letzter Satz. Ich habe immer die ${#array[@]}Notation verwendet. Warum hast du mir gesagt, dass ich die andere nicht verwenden soll?
Bakuriu
1
@ Bakuriu Ich habe nur versucht zu sagen, dass es einfacher ist zu testen, ob ein Array leer ist, um zu testen, ob die von ${#array[@]}0 bereitgestellte Anzahl 0 ist, als zu versuchen, den Array-Inhalt zu erweitern und zu testen, ob die resultierende Zeichenfolge leer ist.
jw013
8

Verwenden Sie zum Initialisieren eines leeren Arrays

array=()

Verwenden Sie, um dem Array einen Wert hinzuzufügen

array+=(value)
Choroba
quelle
Großartig, ich kannte diese Syntax nicht. Das einzige ist, dass dies verwendet werden muss, um declare -a varzu vermeiden, dass die Null am Anfang steht.
Bakuriu
1
@ Bakuriu: Initialisiere es als array=().
Choroba