Warum bewerten Bash und Zsh [-1] als 1?

Warum bewerten Bash und Zsh [-1] als 1?

Mir ist dieses überraschende Verhalten aufgefallen und ich kann nicht ganz verstehen, was da los ist:

$ bash -c 'echo [-1]'
1

$ zsh -c 'echo [-1]'
1

Es scheint, als ob der Befehlsparser versucht, einen Ausdruck auszuwerten:

$ echo [1,2,3]
1
$ echo [123-33]
1

Denn wenn der Ausdruck in Anführungszeichen gesetzt wird, oder darauf hingewiesen wird, dass es sich bei einer Klammer nur um eine Klammer handelt, dann unterbleibt die seltsame Auswertung.

$ echo \[123-33]
[123-33]

$ echo [123-33\]
[123-33]

$ echo '[123-33]'
[123-33]

Auf der Suche nach Hinweisen stieß ich auf dieses Zitat:

https://github.com/mvdan/sh

Vorbehalte

Verwenden Sie beim Indizieren assoziativer Bash-Arrays immer Anführungszeichen. Andernfalls muss der statische Parser davon ausgehen, dass der Index ein arithmetischer Ausdruck ist.

$ echo '${array[spaced string]}' | shfmt
1:16: not a valid arithmetic operator: string
$ echo '${array[dash-string]}' | shfmt
${array[dash - string]}

Ich bin nicht sicher, was ich davon halten soll, da es im Zusammenhang mitecho [1-1]

Dies könnte natürlich durch etwas in meiner eigenen Umgebung verursacht werden, aber wenn ich nachschaue, scheint es nicht daran zu liegen:

$ env -i HOME=$(mktemp -d) bash --noprofile --norc
bash-5.1$ echo [1-1]
1

$ env -i HOME=$(mktemp -d) /bin/bash-4.4  --noprofile --norc
bash-4.4-4.4$ echo [1-1]
1

Auf einer Ubuntu-Box erhalte ich jedoch ein anderes Ergebnis:

$ bash --version | sed 1q; echo [1-1]
GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
[1-1]

Das Deaktivieren aller Optionen ändert dieses Verhalten nicht.

for i in `shopt | awk '/on$/ { print $1}'`; do shopt -u $i;done

Haben Sie eine Idee, warum dieser Klammer-Ausdruck ausgewertet wird und warum das Ergebnis 1 ist?

Antwort1

Dies geschieht nur, wenn Sie eine Datei mit dem Namen 1im aktuellen Verzeichnis haben.

$ bash -c 'echo [-1]'
[-1]
$ touch 1
$ bash -c 'echo [-1]'
1
$ rm 1
$ bash -c 'echo [-1]'
[-1]

Warum? Die Shell führt Globbing durch, [-1]das mit Dateien namens -oder übereinstimmt 1. Wenn also eine Datei mit einem solchen einstelligen Namen existiert, stimmt der Glob überein und Sie erhalten den Dateinamen. Wenn keine solche Datei existiert, bleibt der Glob unverändert.

PS: Es funktioniert auch mit -:

$ touch ./-
$ bash -c 'echo [-1]'
-

verwandte Informationen