In allen mir bekannten Shells werden rm [A-Z]*
alle Dateien entfernt, die mit einem Großbuchstaben beginnen. Mit bash werden jedoch alle Dateien entfernt, die mit einem Buchstaben beginnen.
Da dieses Problem unter Linux und Solaris mit bash-3 und bash-4 besteht, kann es sich nicht um einen Fehler handeln, der durch einen fehlerhaften Mustervergleich in libc oder eine falsch konfigurierte Gebietsschemadefinition verursacht wird.
Ist dieses seltsame und riskante Verhalten beabsichtigt oder handelt es sich nur um einen Fehler, der seit vielen Jahren nicht mehr behoben wurde?
locale
ausgegeben? Ich kann das nicht reproduzieren (touch foo; echo [A-Z]*
gibt das wörtliche Muster, nicht "foo", in einem ansonsten leeren Verzeichnis aus).# echo [A-Z]* ; export LC_COLLATE=C ; echo [A-Z]*
A b B z ZABZAntworten:
LC_COLLATE
ist eine Variable, die die Sortierreihenfolge bestimmt, die beim Sortieren der Ergebnisse der Pfadnamenerweiterung verwendet wird, und das Verhalten von Bereichsausdrücken, Äquivalenzklassen und Sortierfolgen innerhalb der Pfadnamenerweiterung und des Mustervergleichs bestimmt.Folgendes berücksichtigen:
Beachten Sie, dass beim Aufrufen des Befehls
echo [a-z]
als Ausgabe alle Dateien mit Kleinbuchstaben erwartet werden. Außerdem werden mitecho [A-Z]
Dateien mit Großbuchstaben erwartet.Standardkollatierungen mit Gebietsschemas
en_US
haben die folgende Reihenfolge:a
undz
(in[a-z]
) stehen ALLE Großbuchstaben, mit Ausnahme vonZ
.A
undZ
(in[A-Z]
) stehen ALLE Kleinbuchstaben, mit Ausnahme vona
.Sehen:
Wenn Sie die
LC_COLLATE
Variable in ändern ,C
sieht dies wie erwartet aus:Es ist also kein Fehler , es ist ein Kollationsproblem .
Anstelle von Bereichsausdrücken können Sie POSIX-definierte Zeichenklassen wie
upper
oder verwendenlower
. Sie funktionieren auch mit verschiedenenLC_COLLATE
Konfigurationen und sogar mit Akzentzeichen :quelle
tr
also habe ich dies zuerst überprüft.LC_COLLATE
was auch im Handbuch dokumentiert ist.[A-Z]
Inbash
Übereinstimmungen werden alle Sortierelemente (Zeichen, aber auch Zeichenfolgen wieDsz
in ungarischen Gebietsschemata) angezeigt, die nachA
und vor sortierenZ
. Sortiert in Ihrem Gebietsschemac
wahrscheinlich zwischen B und C.So
c
oder soz
würde es passen[A-Z]
, aber nichtẐ
odera
.In der Ländereinstellung C wäre die Reihenfolge:
So
[A-Z]
würde passenA
,B
,C
,Z
, aber nichtÇ
und noch nichtẐ
.Wenn Sie in Großbuchstaben (in einem Skript) übereinstimmen möchten, können Sie
[[:upper:]]
stattdessen verwenden. Es gibt keinen eingebauten Weg,bash
um nur Großbuchstaben in der lateinischen Schrift abzugleichen (außer indem sie einzeln aufgelistet werden ).Wenn Sie das übereinstimmen soll
A
aufZ
Englisch Buchstaben ohne diakritische Zeichen, können Sie entweder[A-Z]
oder[[:upper:]]
aber in derC
locale (die Daten unter der Annahme nicht in Zeichensätze wie BIG5 oder GB18030 codiert , die mehrere Zeichen , deren Codierung hat enthält die Kodierung dieser Briefe) oder Liste sie einzeln ([ABCDEFGHIJKLMNOPQRSTUVWXYZ]
).Beachten Sie, dass zwischen den Schalen einige Unterschiede bestehen.
Für
zsh
,bash -O globasciiranges
(seltsam benannte Option in bash-4.3 eingeführt),schily-sh
undyash
,[A-Z]
Ergebnisse auf dem aktuelle Zeichen Punkt , dessen Code zwischen dem vonA
und derZ
würde so sein , entspricht das Verhaltenbash
in der C - locale.Für Asche, MKSH und alte Muscheln, wie
zsh
oben, jedoch auf Einzelbyte-Zeichensätze beschränkt. Das heißt, in einem UTF-8-Gebietsschema[É-Ź]
würde dies beispielsweise nicht mit "on" übereinstimmenÓ
, aber da dies so ist[<c3><89>-<c5><b9>]
, würde dies mit den Bytewerten 0x89 bis 0xc5 übereinstimmen!ksh93
Verhalten wie mit derbash
Ausnahme, dass es als Sonderfallbereiche behandelt wird, deren Enden beide mit Kleinbuchstaben oder Großbuchstaben beginnen. In diesem Fall werden nur Sortierelemente berücksichtigt, die zwischen diesen Enden sortiert sind, bei denen es sich jedoch (oder bei Sortierelementen mit mehreren Zeichen um das erste Zeichen) auch um Kleinbuchstaben (bzw. Großbuchstaben) handelt. So[A-Z]
würde es passen aufÉ
, aber nicht aufe
soe
tut Art zwischenA
undZ
ist aber nicht groß geschrieben wieA
undZ
.Bei
fnmatch()
Mustern (wie infind -name '[A-Z]'
) oder regulären Systemausdrücken (wie ingrep '[A-Z]'
) hängt dies vom System und der Ländereinstellung ab. Zum Beispiel[A-Z]
stimmt auf einem GNU-System hier nichtx
dasen_GB.UTF-8
Gebietsschema überein , sondern das Gebietsschemath_TH.UTF-8
. Es ist mir unklar, welche Informationen verwendet werden, um dies festzustellen, aber es basiert anscheinend auf einer Nachschlagetabelle, die aus den Gebietsschemadaten von LC_COLLATE abgeleitet wurde .Alle Verhaltensweisen werden von POSIX zugelassen, da POSIX das Verhalten von Bereichen in anderen Gebietsschemas als dem C-Gebietsschema nicht angibt. Jetzt können wir über die Vorteile jedes Ansatzes streiten.
bash
Der Ansatz von ist sehr sinnvoll, da[C-G]
wir die Zeichen zwischenC
und haben wollenG
. Und die Sortierreihenfolge des Benutzers zu verwenden, um zu bestimmen, was dazwischen liegt, ist der logischste Ansatz.Das Problem ist nun, dass es die Erwartungen vieler Menschen verletzt, insbesondere derjenigen, die an das traditionelle Verhalten vor Unicode gewöhnt sind, sogar an Tage vor der Internationalisierung. Während von einem normalen Benutzer, kann es sinnvoll ist , das
[C-I]
umfassth
der alsh
Brief zwischenC
undI
und das[A-g]
beinhaltet nichtZ
, dann ist es eine andere Sache für die Menschen seit Jahrzehnten nur mit ASCII behandelt zu haben.Dieses
bash
Verhalten unterscheidet sich auch von der[A-Z]
Bereichsübereinstimmung in anderen GNU-Werkzeugen wie in regulären GNU-Ausdrücken (wie ingrep
/sed
...) oderfnmatch()
infind -name
.Dies bedeutet auch, dass die
[A-Z]
Übereinstimmungen mit der Umgebung, dem Betriebssystem und der Version des Betriebssystems variieren. Die Tatsache, dass[A-Z]
Á, aber nicht matches übereinstimmt, ist ebenfalls suboptimal.Für
zsh
/ verwendenyash
wir eine andere Sortierreihenfolge. Anstatt sich auf die Zeichenreihenfolge des Benutzers zu verlassen, verwenden wir die Zeichenpunktcodewerte. Das hat den Vorteil, dass es leicht zu verstehen ist, aber außerhalb von ASCII ist es aus praktischer Sicht nicht sehr nützlich.[A-Z]
Entspricht den 26 US-englischen Großbuchstaben und den[0-9]
Dezimalstellen. Es gibt Codepunkte in Unicode, die der Reihenfolge einiger Alphabete folgen, aber nicht verallgemeinert sind und nicht verallgemeinert werden können, da sich verschiedene Personen, die dasselbe Skript verwenden, nicht notwendigerweise auf die Reihenfolge der Buchstaben einigen.Bei traditionellen Shells und mksh, dash, ist es defekt (da die meisten Leute jetzt Multi-Byte-Zeichen verwenden), aber vor allem, weil sie noch keine Multi-Byte-Unterstützung haben. Das Hinzufügen von Multi-Byte-Unterstützung zu Shells wie
bash
undzsh
war eine enorme Anstrengung und dauert noch an.yash
(eine japanische Shell) wurde ursprünglich von Anfang an mit Multi-Byte-Unterstützung entwickelt.Der Ansatz von ksh93 hat den Vorteil, dass er mit den regulären Ausdrücken oder fnmatch () des Systems konsistent ist (oder zumindest auf GNU-Systemen zu sein scheint). Dort wird die Erwartungshaltung einiger Leute nicht verletzt, da
[A-Z]
Kleinbuchstaben,[A-Z]
IncludesÉ
(und Á, aber nicht Ź) nicht enthalten sind. Es ist nicht konsistent mitsort
oder allgemeinerstrcoll()
Ordnung.quelle
mksh
(beide von pdksh abgeleitet).posh -c $'case Ó in [É-Ź]) echo yes; esac'
gibt nichts zurück.sort
weilbash
Globs auf der Sortierreihenfolge der Zeichen basieren. Ich habe derzeit keinen Zugriff auf eine so alte Version vonbash
, kann sie aber später überprüfen. War es damals anders?\xFF
es das Byte 0xFF gibt, nicht das Zeichen U + 00FF (ÿ
selbst codiert als 0xC3 0xBF).\xFF
Alleine bildet kein gültiges Zeichen, daher kann ich nicht erkennen, warum es mit übereinstimmen sollte[É-Ź]
.Es ist beabsichtigt und dokumentiert in der
bash
Dokumentation, Abschnitt Mustervergleich . Der Bereichsausdruck enthält[X-Y]
alle Zeichen zwischenX
und unterY
Verwendung der Sortierfolge und des Zeichensatzes des aktuellen Gebietsschemas:Sie können sehen,
b
sortiert zwischenA
undZ
inen_US.utf8
Gebietsschema.Sie haben einige Möglichkeiten, um dieses Verhalten zu verhindern:
oder aktivieren
globasciiranges
(mit bash 4.3 und höher):quelle
Ich habe dieses Verhalten auf einer neuen Amazon EC2-Instanz beobachtet. Da das OP kein MCVE angeboten hat , werde ich eines posten:
LC_*
Wenn mein Set nicht aktiviert ist, führt die Veröffentlichung von Bash 4.1.2 (1) unter Linux zu einem anscheinend merkwürdigen Verhalten. Ich kann das seltsame Verhalten zuverlässig umschalten, indem ich die jeweiligen Gebietsschemavariablen ein und aussetze. Es überrascht nicht, dass dieses Verhalten beim Exportieren konsistent erscheint:Während ich Bash sehe, wie Stéphane "Shellshock" Chazelas antwortete , denke ich, dass die Bash-Dokumentation zum Pattern Matching fehlerhaft ist:
Ich las diesen Satz (Hervorhebung meiner) als "Wenn die relevanten Gebietsschemavariablen nicht gesetzt sind, wird bash standardmäßig das Gebietsschema C verwenden". Bash scheint das nicht zu tun. Stattdessen scheint die Standardeinstellung ein Gebietsschema zu sein, in dem die Zeichen in Wörterbuchreihenfolge mit diakritischer Faltung sortiert sind:
Ich denke, es wäre gut für Bash, zu dokumentieren, wie es sich verhält, wenn
LC_*
(speziellLC_CTYPE
undLC_COLLATE
) undefiniert sind. Aber in der Zwischenzeit werde ich einige Weisheiten teilen :und
Update Schauen wir uns anhand des @ G-Man-Kommentars genauer an, was passiert:
Ah, ha! Das erklärt die zuvor gesehene Zusammenstellung. Lassen Sie uns alle Gebietsschemavariablen entfernen:
Na, bitte. Jetzt arbeitet bash konsequent in Bezug auf die Dokumentation auf diesem Linux-System. Wenn eine der locale Variablen gesetzt sind (
LANGUAGE
,LANG
,LC_COLLATE
,LC_CTYPE
,LC_ALL
, etc.) verwendet dann heftiger Schlag diejenigen entsprechend seiner Anleitung. Andernfalls fällt bash auf C zurück.Die Wooledge-Bash-FAQ enthält Folgendes :
Das offensichtliche Problem sowohl bei der Bedienung als auch bei der Dokumentation kann also durch Betrachten der Gesamtsumme aller lokalen Fahrvariablen erklärt werden.
quelle
C
Gebietsschema dokumentiert verhält , ist dies ein Fehler.env | grep LANG
oder hinzufügenecho "$LANG"
.LANG
. Mit diesem Hinweis wird alles erklärt.Das Gebietsschema kann ändern, nach welchen Zeichen gesucht wird
[A-Z]
. Verwendenden Einfluss zu beseitigen. (Ich habe eine Subshell verwendet, um die Änderung zu lokalisieren.)
quelle
export LC_ALL=C
zuerst.Wie bereits gesagt, handelt es sich hierbei um eine "Sammelbestellung".
Der Bereich az kann in einigen Ländereinstellungen Großbuchstaben enthalten:
Die richtige Lösung seit Bash 4.3 ist das Setzen der Option
globasciiranges
:um bash so zu machen, als ob
LC_COLLATE=C
es in globalen Bereichen eingestellt wäre.quelle
Es scheint, dass ich die richtige Antwort auf meine eigene Frage gefunden habe:
Bash ist fehlerhaft, da es sein eigenes Gebietsschema nicht verwaltet. Das Setzen von LC_ * in einem Bash-Prozess hat in diesem Shell-Prozess keine Auswirkung.
Wenn Sie LC_COLLATE = C setzen und dann eine weitere Bash starten, funktioniert das Globbing im neuen Bash-Prozess wie erwartet.
quelle
export
richtig gemacht.