find (1): Wie wird der Stern-Platzhalter implementiert, damit er bei einigen Dateinamen fehlschlägt?


31

In einem Dateisystem mit Dateinamen in UTF-8 habe ich eine Datei mit einem falschen Namen. Es wird angezeigt als :, D�sinstallertatsächlicher Name nach zsh D$'\351'sinstaller:, Latin1 für Désinstaller, selbst eine französische Barbarei für " uninstall ". Zsh würde es nicht mit, [[ $file =~ '^.*$' ]]aber mit einem Globbing vergleichen *- das ist das Verhalten, das ich erwarte.

Jetzt erwarte ich immer noch, dass ich es beim Ausführen finde find . -name '*'- tatsächlich würde ich nie erwarten, dass ein Dateiname diesen Test nicht besteht. Mit wird LANG=en_US.utf8die Datei jedoch nicht angezeigt, und ich muss festlegen LANG=C(oder en_USoder ''), dass sie funktioniert.

Frage: Welche Implementierung steckt dahinter und wie hätte ich dieses Ergebnis vorhersagen können?

Infos: Arch Linux 3.14.37-1-lts, find (GNU findutils) 4.4.2


1
Haben Sie darüber nachgedacht convmv, Dateinamen in utf-8 umzuwandeln?
Strg-Alt-Delor

@richard: Tatsächlich verlasse ich mich darauf, den Dateinamen [[ $file =~ '^.*$' ]]nicht zu verwenden recode, aber ich werde jetzt nachsehen, convmvob dies notwendig ist. Vielen Dank.
Michaël

Antworten:


25

Das ist ein wirklich schöner Fang. Bei einem kurzen Blick auf den Quellcode für GNU find würde ich sagen, dass dies darauf hinausläuft, wie fnmatchsich ungültige Bytesequenzen ( pred_name_commonin pred.c) verhalten :

b = fnmatch (str, base, flags) == 0;
(...)
return b;

Dieser Code testet den Rückgabewert von fnmatchauf Gleichheit mit 0, prüft jedoch nicht auf Fehler. Dies führt dazu, dass Fehler als "nicht übereinstimmen" gemeldet werden.

Es wurde vor vielen Jahren vorgeschlagen, das Verhalten dieser libc-Funktion so zu ändern, dass das *Muster auch bei beschädigten Dateinamen immer "wahr" ist , aber soweit ich das beurteilen kann, muss die Idee zurückgewiesen worden sein (siehe den Thread, der bei https beginnt) : //sourceware.org/ml/libc-hacker/2002-11/msg00071.html ):

Wenn fnmatch ein ungültiges Multibyte-Zeichen erkennt, sollte es auf die Einzelbyte-Übereinstimmung zurückgreifen, damit "*" eine Chance hat, mit einer solchen Zeichenfolge übereinzustimmen.

Und warum ist das besser oder richtiger? Gibt es eine bestehende Praxis?

Wie von Stéphane Chazelas in einem Kommentar und im gleichen Thread von 2002 erwähnt, ist dies nicht mit der Glob-Erweiterung vereinbar, die von Shells ausgeführt wird, die ungültige Zeichen nicht verschlucken. Noch rätselhafter ist die Tatsache, dass beim Umkehren des Tests nur die Dateien gefunden werden, deren Namen nicht korrekt sind (erstellen Sie Dateien in Bash mit touch $'D\351marrer' $'Touch\303\251' $'\346\227\245\346\234\254\350\252\236'):

$ find -name '*'
.
./Touché
./日本語

$ find -not -name '*'
./D?marrer

Um Ihre Frage zu beantworten, hätten Sie dies vorhersagen können, indem Sie das Verhalten fnmatchin diesem Fall kennen und wissen, wie findmit dem Rückgabewert dieser Funktion umgegangen wird. Sie hätten es wahrscheinlich nicht allein durch Lesen der Dokumentation herausfinden können.


Meine Vermutung, warum es keine Lösung für *gibt, ist, dass es dann widersprüchlich wäre D*staller.
Strg-Alt-Delor

7
@richard, die Idee wäre, dass D*stalleres $'D\351sinstaller'genauso gut passt wie in der Glob aller Shells, die ich getestet habe. Da das Verhalten von GNU fnmatch nicht mit dem der GNU-Shell übereinstimmt, würde ich sagen, dass es ein Fehler ist.
Stéphane Chazelas

1
Große tiefe Antwort, dhag; sehr geschätzt. Würde es Ihnen etwas ausmachen, auf die Standardspezifikation hinzuweisen, die fnmatch erfüllt? Ich kann die übliche POSIX-Regexp-Spezifikation finden, die angibt, dass .nur gültige Zeichen in der Codierung übereinstimmen sollen - daher meine Erwartung, dass .*ungültige Zeichenfolgen nicht übereinstimmen -, aber ich kann keine übereinstimmende Spezifikation für den Globbing Star finden.
Michaël

1
Die näheste Spezifikation, die ich online finden kann, befindet sich auf dieser OpenGroup-Seite . Es heißt, Matching soll auf dem Bitmuster basieren, das zum Codieren des Zeichens verwendet wird, nicht auf der grafischen Darstellung des Zeichens. und Das <Sternchen> ist ein Muster, das mit einer beliebigen Zeichenfolge übereinstimmen soll, einschließlich der Nullzeichenfolge. Dies kann wohl als Vorschlag von @ StéphaneChazelas interpretiert werden. 13 Jahre später könnte es Zeit sein, erneut einen Ping-Befehl an den Upstream zu senden :-)
Michaël

@ Michaël, ich könnte auch nichts besseres finden. Vielleicht verhält sich GNU find unter Mac OS zu Vergleichszwecken in einer Weise, die mit dem Globbing der Shell konsistent ist (dh -name '*'mit allen Dateien übereinstimmt, einschließlich defekter Namen), so dass vermutlich fnmatchBSDs Version von , die nicht POSIX.2-Konformität beansprucht, hat im Gegensatz zur GNU-Version eine andere und wohl vernünftigere Interpretation dessen, was mit ungültigen Zeichen geschehen soll.
Dhag

13

Die -name Suchoption verwendet die Shell- Pattern-Matching-Notation , um den Dateinamen abzugleichen. *ist ein Muster, das mit mehreren Zeichen übereinstimmt . Es muss mit einer Zeichenfolge von null oder mehr Zeichen übereinstimmen .

findVerwendet fnmatch , um die Musterübereinstimmung zu überprüfen, sodass Sie ltrace verwenden können , um das Ergebnis zu überprüfen:

$ touch $'\U1212'aa
$ touch D$'\351'sinstaller
$ LC_ALL=en_US.utf8 ltrace -e fnmatch find -name '*'          
find->fnmatch("foo", "foo", 0)                   = 0
find->fnmatch("Foo", "foo", 0)                   = 1
find->fnmatch("Foo", "foo", 16)                  = 0
find->fnmatch("*", ".", 0)                       = 0
.
find->fnmatch("*", "D\351sinstaller", 0)         = -1
find->fnmatch("*", "\341\210\222aa", 0)          = 0
./ሒaa
+++ exited (status 0) +++

Mit D\351sinstaller, fnmatchreturn -1, wird angezeigt, dass die Übereinstimmung fehlgeschlagen ist. Ein gültiges Zeichen wie ሒaawird abgeglichen.

In Ihrem Fall ist das UTF-8Gebietsschema \351ein ungültiges Zeichen, wodurch der Mustervergleich fehlschlägt.


3
Zumindest +1 für die Verwendung von ltrace. Ich wusste es strace, aber es ltraceist neu für mich. Schön!
Michaël
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.