Ich habe ein Bash-Shell-Skript, das alle untergeordneten Verzeichnisse (aber keine Dateien) eines bestimmten Verzeichnisses durchläuft. Das Problem ist, dass einige der Verzeichnisnamen Leerzeichen enthalten.
Hier ist der Inhalt meines Testverzeichnisses:
$ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
Und der Code, der die Verzeichnisse durchläuft:
for f in `find test/* -type d`; do
echo $f
done
Hier ist die Ausgabe:
Test / Baltimore Test / Kirsche Hügel Test / Edison Test / Neu York Stadt Test / Philadelphia
Cherry Hill und New York City werden als 2 oder 3 separate Einträge behandelt.
Ich habe versucht, die Dateinamen wie folgt zu zitieren:
for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
echo $f
done
aber ohne Erfolg.
Es muss einen einfachen Weg geben, dies zu tun.
Die Antworten unten sind großartig. Um dies jedoch komplizierter zu machen, möchte ich nicht immer die in meinem Testverzeichnis aufgeführten Verzeichnisse verwenden. Manchmal möchte ich stattdessen die Verzeichnisnamen als Befehlszeilenparameter übergeben.
Ich nahm Charles 'Vorschlag an, das IFS einzurichten, und fand Folgendes:
dirlist="${@}"
(
[[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
for d in $dirlist; do
echo $d
done
)
und dies funktioniert einwandfrei, es sei denn, die Befehlszeilenargumente enthalten Leerzeichen (auch wenn diese Argumente in Anführungszeichen stehen). Wenn Sie das Skript beispielsweise folgendermaßen aufrufen, test.sh "Cherry Hill" "New York City"
wird die folgende Ausgabe erzeugt:
Kirsche Hügel Neu York Stadt
quelle
list="$@"
verwirft die Liste des ursprünglichen Werts vollständig und reduziert sie zu einer Zeichenfolge. Bitte befolgen Sie die Praktiken in meiner Antwort genau wie angegeben - eine solche Aufgabe wird nirgendwo darin empfohlen; Wenn Sie eine Liste von Befehlszeilenargumenten an ein Programm übergeben möchten, sollten Sie diese in einem Array sammeln und dieses Array direkt erweitern.Antworten:
Mach es zuerst nicht so. Der beste Ansatz ist die
find -exec
richtige Verwendung :Der andere sichere Ansatz ist die Verwendung einer NUL-terminierten Liste. Dies setzt jedoch voraus, dass Sie Unterstützung finden
-print0
:Sie können ein Array auch aus find füllen und dieses Array später übergeben:
Wenn Ihr Fund dies nicht unterstützt
-print0
, ist Ihr Ergebnis unsicher. Das Folgende verhält sich nicht wie gewünscht, wenn Dateien vorhanden sind, deren Namen Zeilenumbrüche enthalten (was ja legal ist):Wenn einer der oben genannten
IFS
Punkte nicht verwendet wird, besteht ein dritter Ansatz (der sowohl hinsichtlich der Zeit als auch der Speichernutzung weniger effizient ist, da er die gesamte Ausgabe des Unterprozesses liest, bevor eine Wortaufteilung durchgeführt wird ) darin, eine Variable zu verwenden , die dies nicht tut Enthält kein Leerzeichen. Ausschalten Globbing (set -f
) Strings zu verhindern , enthaltend glob Zeichen wie[]
,*
oder?
aus erweitert:Schließlich sollten Sie für den Fall der Befehlszeilenparameter Arrays verwenden, wenn Ihre Shell diese unterstützt (dh ksh, bash oder zsh):
wird die Trennung aufrechterhalten. Beachten Sie, dass das Zitieren (und die Verwendung von
$@
anstatt$*
) wichtig ist. Arrays können auch auf andere Weise ausgefüllt werden, z. B. durch Glob-Ausdrücke:quelle
Funktioniert jedoch nicht, wenn der Dateiname Zeilenumbrüche enthält. Das Obige ist die einzige Lösung, die ich kenne, wenn Sie den Verzeichnisnamen tatsächlich in einer Variablen haben möchten. Wenn Sie nur einen Befehl ausführen möchten, verwenden Sie xargs.
quelle
find . -type d | while read file; do ls "$file"; done
Hier ist eine einfache Lösung, die Tabulatoren und / oder Leerzeichen im Dateinamen behandelt. Wenn Sie mit anderen seltsamen Zeichen im Dateinamen wie Zeilenumbrüchen umgehen müssen, wählen Sie eine andere Antwort.
Das Testverzeichnis
Der Code, um in die Verzeichnisse zu gelangen
Der Dateiname muss in Anführungszeichen (
"$f"
) stehen, wenn er als Argument verwendet wird. Ohne Anführungszeichen fungieren die Leerzeichen als Argumenttrennzeichen, und dem aufgerufenen Befehl werden mehrere Argumente zugewiesen.Und die Ausgabe:
quelle
alias duc='ls -d * | while read D; do du -sh "$D"; done;'
alias duc='du -sh *(/)'
-print0
und ausführenIFS='' read -r -d '' f
.Dies ist unter Standard-Unix äußerst schwierig, und die meisten Lösungen weisen keine Zeilenumbrüche oder andere Zeichen auf. Wenn Sie jedoch das GNU-Toolset verwenden, können Sie die
find
Option ausnutzen-print0
undxargs
mit der entsprechenden Option-0
(Minus-Null) verwenden. Es gibt zwei Zeichen, die in einem einfachen Dateinamen nicht vorkommen können. das sind Schrägstrich und NUL '\ 0'. Offensichtlich erscheint ein Schrägstrich in Pfadnamen, daher ist die GNU-Lösung, ein NUL '\ 0' zum Markieren des Namensendes zu verwenden, genial und narrensicher.quelle
Warum nicht einfach setzen
vor dem for-Befehl? Dadurch wird das Feldtrennzeichen von <Leerzeichen> <Tabulator> <Nachricht> in nur <Zeitlinie> geändert
quelle
quelle
-d $'\0'
ist genau das Gleiche wie-d ''
- da bash NUL-terminierte Zeichenfolgen verwendet, ist das erste Zeichen einer leeren Zeichenfolge eine NUL, und aus dem gleichen Grund können NULs in C-Zeichenfolgen überhaupt nicht dargestellt werden.ich benutze
Wäre das nicht genug?
Idee von http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
quelle
$(echo ...)
), behandelt Dateinamen mit Glob-Ausdrücken nicht korrekt, behandelt Dateinamen, die$'\b'
Zeichen oder $ '\ n' enthalten, nicht korrekt und konvertiert darüber hinaus mehrere Durchläufe von Leerzeichen in einzelne Leerzeichen auf dem Ausgabeseite wegen falscher Anführungszeichen.Speichern Sie Listen nicht als Zeichenfolgen. Speichern Sie sie als Arrays, um all diese Trennzeichenverwirrung zu vermeiden. Hier ist ein Beispielskript, das entweder in allen Unterverzeichnissen des Tests oder in der Liste in der Befehlszeile ausgeführt wird:
Probieren wir dies nun in einem Testverzeichnis mit ein oder zwei eingeworfenen Kurven aus:
quelle
"$@"
Array, mit ihm anhängtset -- "$@" "$f"
.Sie können IFS (internes Feldtrennzeichen) zeitlich verwenden, indem Sie:
quelle
ps Wenn es nur um Platz in der Eingabe geht, dann haben einige doppelte Anführungszeichen für mich reibungslos funktioniert ...
quelle
Um das zu ergänzen, was Jonathan gesagt hat: Verwenden Sie die
-print0
Option fürfind
in Verbindung mitxargs
wie folgt:Dadurch wird der Befehl
command
mit den richtigen Argumenten ausgeführt. Verzeichnisse mit Leerzeichen werden korrekt in Anführungszeichen gesetzt (dh sie werden als ein Argument übergeben).quelle
Der obige Code konvertiert .mov-Dateien in .avi. Die .mov-Dateien befinden sich in verschiedenen Ordnern und die Ordnernamen haben auch Leerzeichen . Mein obiges Skript konvertiert die .mov-Dateien in die .avi-Datei im selben Ordner. Ich weiß nicht, ob es euch Völkern hilft.
Fall:
Prost!
quelle
echo "$name" | ...
funktioniert nicht, wenn dies der Fallname
ist-n
, und wie es sich mit Namen mit Backslash-Escape-Sequenzen verhält, hängt von Ihrer Implementierung ab. POSIX macht das Verhaltenecho
in diesem Fall explizit undefiniert (während XSI-erweitertes POSIX die Erweiterung von Backslash-Escape-Sequenzen standardmäßig definiert und GNU-Systeme - einschließlich Bash - ohnePOSIXLY_CORRECT=1
den POSIX-Standard durch Implementierung zu brechen-e
(während die Spezifikationecho -e
das Drucken-e
auf der Ausgabe erfordert ).printf '%s\n' "$name" | ...
ist sicherer.Musste mich auch mit Leerzeichen in Pfadnamen befassen. Was ich schließlich tat, war eine Rekursion und
for item in /path/*
:quelle
function
Schlüsselwort nicht - es macht Ihren Code mit POSIX sh inkompatibel, hat aber keinen anderen nützlichen Zweck. Sie können einfach eine Funktion mit definierenrecursedir() {
, die beiden Parens hinzufügen und das Funktionsschlüsselwort entfernen. Dies ist mit allen POSIX-kompatiblen Shells kompatibel.Konvertieren Sie die Dateiliste in ein Bash-Array. Dies verwendet den Ansatz von Matt McClure zum Zurückgeben eines Arrays von einer Bash-Funktion: http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html Das Ergebnis ist ein Weg um eine mehrzeilige Eingabe in ein Bash-Array umzuwandeln.
Dieser Ansatz scheint auch dann zu funktionieren, wenn fehlerhafte Zeichen vorhanden sind, und ist eine allgemeine Methode, um Eingaben in ein Bash-Array zu konvertieren. Der Nachteil ist, dass Sie bei langen Eingaben die Grenzwerte für die Befehlszeilengröße von Bash überschreiten oder viel Speicher belegen können.
Ansätze, bei denen die Schleife, die schließlich an der Liste arbeitet, auch die Liste einleitet, haben den Nachteil, dass das Lesen von stdin nicht einfach ist (z. B. das Bitten des Benutzers um Eingabe), und die Schleife ist ein neuer Prozess, sodass Sie sich möglicherweise fragen, warum Variablen Sie, die innerhalb der Schleife festgelegt wurden, sind nach Beendigung der Schleife nicht mehr verfügbar.
Ich mag es auch nicht, IFS einzustellen, es kann anderen Code durcheinander bringen.
quelle
IFS='' read
in derselben Zeile verwenden, ist die IFS-Einstellung nur für den Lesebefehl vorhanden und entgeht ihr nicht. Es gibt keinen Grund, IFS nicht auf diese Weise einzustellen.Nun, ich sehe zu viele komplizierte Antworten. Ich möchte die Ausgabe des Dienstprogramms find nicht übergeben oder eine Schleife schreiben, da find hierfür die Option "exec" hat.
Mein Problem war, dass ich alle Dateien mit der Erweiterung dbf in den aktuellen Ordner verschieben wollte und einige davon Leerzeichen enthielten.
Ich habe es so angepackt:
Sieht für mich sehr einfach aus
quelle
Ich habe gerade herausgefunden, dass es einige Ähnlichkeiten zwischen meiner und Ihrer Frage gibt . Offenbar, wenn Sie Argumente an Befehle übergeben möchten
um sie in der richtigen Reihenfolge auszudrucken
Beachten Sie, dass das $ @ von doppelten Anführungszeichen umgeben ist, einige Anmerkungen hier
quelle
Ich brauchte das gleiche Konzept, um mehrere Verzeichnisse oder Dateien aus einem bestimmten Ordner nacheinander zu komprimieren. Ich habe es mit awk gelöst, die Liste von ls zu analysieren und das Problem der Leerstelle im Namen zu vermeiden.
Was denken Sie?
quelle
quelle
Für mich funktioniert das und es ist ziemlich "sauber":
quelle
Hatte gerade ein einfaches Variantenproblem ... Konvertiere Dateien von typisiertem .flv in .mp3 (gähnen).
Suchen Sie rekursiv alle Macintosh-Benutzer-Flash-Dateien und wandeln Sie sie in Audio um (Kopieren, kein Transcode)
.
quelle
read
Nachherin
ist ein weiteres Wort in der Liste, über die Sie iterieren. Was Sie gepostet haben, ist eine leicht kaputte Version dessen, was der Fragesteller hatte, was nicht funktioniert. Sie haben vielleicht vorgehabt, etwas anderes zu posten, aber es wird wahrscheinlich trotzdem von anderen Antworten hier abgedeckt.