Der Tag kam, an dem ich ein BASH-Skript schreiben musste, das willkürliche Verzeichnisbäume durchläuft und beliebige Dateien untersucht und versucht, etwas über einen Vergleich zwischen ihnen zu ermitteln. Ich dachte, es wäre eine einfache paar Stunden Tops! Prozess - nicht so!
Mein Aufhängen ist, dass manchmal ein Idiot - ähm! - Entschuldigen Sie, liebender Benutzer wählt Leerzeichen in Verzeichnis- und Dateinamen. Dies führt dazu, dass mein Skript fehlschlägt.
Die perfekte Lösung , abgesehen von der Drohung der Guillotine für diejenigen, die darauf bestehen, Räume an solchen Orten zu benutzen (ganz zu schweigen von den Leuten, die dies in den Betriebssystemcode schreiben!), könnten eine Routine sein das "entkernt" die Datei- und Verzeichnisnamen für uns, ähnlich wie cygwin Routinen hat, um von Unix zu DOS-Dateinamenformaten zu konvertieren. Gibt es so etwas in einer Standard-Unix / Linux-Distribution?
Beachten Sie, dass das einfache Konstrukt for file in *
nicht so gut funktioniert, wenn man versucht, Verzeichnisbäume zu vergleichen, da es ONLY auf "das aktuelle Verzeichnis" arbeitet - und in diesem Fall wie in viele andere, ständig CDing zu verschiedenen Verzeichnissen bringt mit sich seine eigenen Probleme. Also, bei meinen Hausaufgaben, fand ich diese Frage Handle Sonderzeichen in bash for ... in loop und die vorgeschlagene Lösung hängt sich auf Leerzeichen in Verzeichnisnamen auf, kann aber einfach so überwunden werden:
BITTE BEACHTEN SIE: Der obige Code ist nicht besonders schön, weil die Variablen, die innerhalb der while-Schleife verwendet werden, außerhalb dieser while-Schleife nicht zugänglich sind. Dies liegt daran, dass eine implizite Subshell erstellt wird, wenn die Ausgabe des Befehls ls piped ist. Dies ist ein wichtiger Motivationsfaktor für meine Anfrage!
... OK, der Code oben hilft in vielen Situationen, aber "entkommen" die Charaktere wäre auch sehr mächtig. Zum Beispiel könnte dir oben enthalten:
%Vor%Gibt es das schon und ich habe es gerade übersehen?
Wenn nicht, hat jemand einen einfachen Vorschlag, einen zu schreiben - vielleicht mit sed oder lex? (Ich bin weit davon entfernt, mit beiden kompetent zu sein.)
Machen Sie einen wirklich fiesen Dateinamen zum Testen:
%Vor% [Bearbeiten: Wahrscheinlich beabsichtige ich, dass der touch
-Befehl
, die mehr hässliche Zeichen in den Dateinamen einfügt. Die Ausgabe wird ein wenig anders aussehen. ]
Dann führe das aus:
%Vor%Die Ausgabe sollte so aussehen:
%Vor% Dies ist eine verkürzte Version von Pascal Thivents Monster sed
, plus Handhabung für Wagenrückläufe und Zeilenumbrüche und vielleicht ein bisschen mehr.
Beim ersten Durchlauf von sed
werden mehrere Zeilen zu einem durch "\ n" begrenzten Dateinamen für Dateinamen mit Zeilenumbrüchen zusammengeführt. Der zweite Durchlauf ersetzt alle aus einer Liste von Zeichen, denen ein Backslash vorangestellt ist. Der letzte Teil ersetzt Wagenrücklauf mit "\ r".
Beachten Sie, dass, wie Sie wissen, while
Leerzeichen behandelt und for
nicht, sondern indem sie die Ausgabe von find
mit Null-Beendigung und das Trennzeichen von read
auf null setzen, Sie können auch Zeilenumbrüche in Dateinamen bearbeiten. Die Option -r
bewirkt, dass read
Backslashes akzeptiert, ohne sie zu interpretieren.
Bearbeiten:
Eine andere Möglichkeit, die Sonderzeichen zu umgehen, dieses Mal ohne sed
zu verwenden, verwendet die Funktion zum Erstellen von Zitaten und Variablen der Bash printf
builtin (dies veranschaulicht auch die Verwendung von Prozesssubstitution statt einer Pipe):
Die Variable $name
ist außerhalb der Schleife verfügbar, da die Verwendung der Prozesssubstitution das Erstellen einer Subshell um die Schleife verhindert.
Das folgende Snippet behandelt alle Dateinamen (einschließlich Leerzeichen, Anführungszeichen, Zeilenumbrüche, ...):
%Vor%Siehe auch BashFAQ .
Es gibt ein ziemlich ernstes Problem mit der Escape-Methode: Welche Escapes benötigt werden, hängt vom Kontext ab, in dem die Variable expandiert wird. Im Normalfall gibt es kein Entweichen, das funktioniert. Zum Beispiel, wenn Sie etwas Einfaches tun wollen wie:
%Vor% ... es wird nicht funktionieren (ls sucht nach 4 Dateien: "a", "b", "c" und "d"), weil die Shell dem Wort beim Aussprechen keine Aufmerksamkeit schenkt -split $ Dateien. Du könntest eval ls $files
verwenden, aber das würde auf Dingen wie Tabs in den Dateinamen scheitern.
Der while ... read ... done < <(find ... -print0)
-Ansatz fgm vorgeschlagen funktioniert solide (und wegen der Flexibilität der Suche Suchmuster, ist sehr mächtig), aber es ist auch eine ziemlich chaotisch Stapel von Workarounds für verschiedene mögliche Probleme; Wenn Sie die Macht des Findens nicht brauchen, ist es nicht schwer, Dinge mit for
und *
zu erledigen:
Wenn Sie (wie Sie in der Frage erwähnen) daran interessiert sind, die beiden Verzeichnisbäume zu vergleichen, ist das Durchlaufen eines von ihnen nicht ganz das, was Sie wollen; es wäre besser, ihre Inhalte in Arrays zu setzen, so:
%Vor% (Beachten Sie, dass AFAIK das "${pathlist2[@]##*/}"
-Konstrukt nicht Standard ist, aber seit einiger Zeit sowohl in bash als auch in zsh unterstützt wird.)
Ich fand das Wie Datei zu entkommen Namen in Bash-Shell-Skripten beim googeln, die ich unten zitiere:
Nach dem Kampf mit Bash für ganz Irgendwann habe ich herausgefunden, dass die Der folgende Code bietet eine gute Grundlage um Sonderzeichen zu entkommen. Natürlich ist es nicht vollständig, aber die wichtigste Charaktere sind gefiltert.
Wenn jemand eine bessere Lösung hat, lass es mich wissen, bitte. Es funktioniert und es ist lesbar, aber nicht hübsch.
%Vor%
Vielleicht könnten Sie es als Ausgangspunkt verwenden.
Beachten Sie, dass die <( )
-Notation nicht funktioniert, wenn bash als /bin/sh
aufgerufen wird.
Der Befehl find funktioniert manchmal in dieser Situation:
%Vor%zum Beispiel