Während ich BASH seit mehreren Jahren benutze, sind meine Erfahrungen mit BASH-Skripten relativ begrenzt.
Mein Code ist wie folgt. Es sollte die gesamte Verzeichnisstruktur aus dem aktuellen Verzeichnis heraus abrufen und in dieses replizieren $OUTDIR
.
for DIR in `find . -type d -printf "\"%P\"\040"`
do
echo mkdir -p \"${OUTPATH}${DIR}\" # Using echo for debug; working script will simply execute mkdir
echo Created $DIR
done
Das Problem ist, hier ist ein Beispiel meiner Dateistruktur:
$ ls
Expect The Impossible-Stellar Kart
Five Iron Frenzy - Cheeses...
Five Score and Seven Years Ago-Relient K
Hello-After Edmund
I Will Go-Starfield
Learning to Breathe-Switchfoot
MMHMM-Relient K
Beachten Sie die Leerzeichen: -S Und for
nimmt Parameter wortweise an, sodass die Ausgabe meines Skripts ungefähr so aussieht:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Learning"
Created Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot"
Created Breathe-Switchfoot
Aber ich brauche es, um ganze Dateinamen (eine Zeile nach der anderen) aus der Ausgabe von zu holen find
. Ich habe auch versucht, find
jeden Dateinamen in doppelte Anführungszeichen zu setzen. Aber das hilft nicht.
for DIR in `find . -type d -printf "\"%P\"\040"`
Und mit dieser geänderten Zeile ausgeben:
Creating directory structure...
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"""
Created ""
mkdir -p "/myfiles/multimedia/samjmusicmp3test/"Learning"
Created "Learning
mkdir -p "/myfiles/multimedia/samjmusicmp3test/to"
Created to
mkdir -p "/myfiles/multimedia/samjmusicmp3test/Breathe-Switchfoot""
Created Breathe-Switchfoot"
Jetzt brauche ich eine Möglichkeit, die ich auf diese Weise durchlaufen kann, da ich auch einen komplizierteren Befehl ausführen möchte, der gstreamer
für jede Datei eine ähnliche Struktur aufweist. Wie soll ich das machen?
Bearbeiten: Ich benötige eine Codestruktur, mit der ich für jedes Verzeichnis / jede Datei / Schleife mehrere Codezeilen ausführen kann. Entschuldigung, wenn ich unklar war.
Lösung: Ich habe anfangs versucht:
find . -type d | while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done
Dies hat zum größten Teil gut funktioniert. Später stellte ich jedoch fest, dass, da die Pipe dazu führt, dass die while-Schleife in einer Subshell ausgeführt wird, alle in der Schleife gesetzten Variablen später nicht mehr verfügbar waren, was die Implementierung eines Fehlerzählers ziemlich schwierig machte. Meine endgültige Lösung (aus dieser Antwort auf SO ):
while read DIR
do
mkdir -p "${OUTPATH}${DIR}"
echo Created $DIR
done < <(find . -type d)
Dies erlaubte mir später, Variablen innerhalb der Schleife bedingt zu inkrementieren, die später im Skript verfügbar bleiben würden.
/
und nicht druckbare Zeichen erlauben . Aber alles ist erlaubt, außer/
und\0
du musst es ihnen erlauben.Antworten:
Sie müssen das
find
in einewhile
Schleife leiten.Sie müssen
-printf
in diesem Fall auch nicht verwenden.Sie können diesen Beweis gegen Dateien mit Zeilenumbrüchen im Namen erbringen, indem Sie ein Nullbyte-Trennzeichen verwenden (dies ist das einzige Zeichen, das in einem * nix-Dateipfad nicht vorkommen kann):
Sie werden auch feststellen, dass die Verwendung
$()
von Backticks anstelle von Backticks vielseitiger und einfacher ist. Sie können viel einfacher verschachtelt werden und Zitate können viel einfacher erstellt werden. Dieses erfundene Beispiel wird diese Punkte veranschaulichen:Versuchen Sie das mit Backticks.
quelle
"$dir"
die Verwendung vorzuziehen"${dir}"
- es ist einfach, den Unterschied zwischen $ {dir} name und $ {dirname} zu erkennen, aber $ dirname kann so oder so interpretiert werden.read
eine ganze Zeile eingelesen wird${dir}
, sodass das IFS keine Rolle spielt.find … -print0 | xargs -0 …
weil das verwendete Trennzeichen genau dem einzigen Zeichen entspricht, das in POSIX-Pfadnamen nicht zulässig ist: NUL (U + 0000).while
. @ Chris Johnsen: Stimmt, aber selbst Musik-Ripping-Programme neigen nicht dazu, Zeilenvorschübe in ihre Dateinamen einzufügen. Und wenn ja, möchte ich wissen (dh, etwas läuft schief) und sie sofort loswerden ...In dieser Antwort, die ich vor ein paar Tagen geschrieben habe, finden Sie ein Beispiel für ein Skript, das Dateinamen mit Leerzeichen behandelt.
Es gibt jedoch eine etwas gewundenere (aber präzisere) Möglichkeit, das zu erreichen, was Sie versuchen:
-print0
teilt find mit, die Argumente durch eine Null zu trennen; das -0 bis xargs weist es an, Argumente zu erwarten, die durch Nullen getrennt sind. Dies bedeutet, dass Leerzeichen problemlos verarbeitet werden können.-I {}
Weist xargs an, die Zeichenfolge{}
durch den Dateinamen zu ersetzen . Dies impliziert auch, dass nur ein Dateiname pro Befehlszeile verwendet werden sollte (xargs stopft normalerweise so viele Dateien, wie in die Zeile passen).Der Rest sollte offensichtlich sein.
quelle
Das Problem, auf das Sie stoßen, ist, dass die for-Anweisung als separate Argumente auf die Suche reagiert. Der Leerzeichenbegrenzer. Sie müssen die IFS-Variable von bash verwenden, um keine Aufteilung nach Speicherplatz vorzunehmen.
Hier ist ein Link , der erklärt, wie das geht.
Stellen Sie Ihren Fund so ein, dass Ihr Feldbegrenzer nach dem% P ausgegeben wird, und stellen Sie Ihr IFS entsprechend ein. Ich habe Semikolon gewählt, da es sehr unwahrscheinlich ist, dass es in Ihren Dateinamen gefunden wird.
Die andere Alternative ist, mkdir direkt aus dem Find aufzurufen, indem
-exec
Sie die for-Schleife überspringen. Dies ist der Fall, wenn Sie kein zusätzliches Parsing durchführen müssen.quelle
/
auf POSIX- und:
DOS-Dateisystemen auswählen . Es gibt unzulässige Zeichen für verschiedene Dateisysteme, die Sie für das IFS auswählen können. Alles, was komplizierter ist, und Sie sind besser dran, Perl zu verwenden.find
Dateinamen mit Pfaden einschließlich Schrägstrichen zurückgibt. Ändern Sie das Semikolon in Ihrem Skript in einen Schrägstrich und das Echo gibt das Verzeichnis und den Dateinamen in separaten Zeilen aus.while
Option gegangen , aber das sieht auch ganz brauchbar aus. Ja, in meiner ähnlichen Struktur musste ich später weiter analysieren. (Der Eingabedateiname wäre .ogg, der wiefilesrc
in der gst-Pipeline übergeben wird, aber eine entsprechende Endung in .mp3 basierend auf dem Ausgabeverzeichnis würde generiert und auch an die Pipeline übergeben werdenfilesink
, und dies muss natürlich erfolgen für jede Datei, zusammen mit einigenecho
an den Benutzer.)Wenn der Rumpf Ihrer Schleife mehr als ein einzelner Befehl ist, können Sie mit xargs ein Shell-Skript ausführen :
Stellen Sie sicher, dass Sie den abschließenden Gedankenstrich (oder ein anderes 'Wort') einfügen, wenn die Shell der Sorte Bourne / POSIX angehört (es wird verwendet, um $ 0 im Shell-Skript festzulegen). Vorsicht ist auch beim Zitieren geboten, da das Shell-Skript nicht direkt an der Eingabeaufforderung, sondern in einer Zeichenfolge in Anführungszeichen geschrieben wird.
quelle
in deiner aktualisierten frage hast du
das sollte sein
quelle
quelle
oder um das Ganze viel unkomplizierter zu machen:
Dadurch wird die Verzeichnisstruktur von SRC in die Sommerzeit repliziert.
quelle
while
...do
...done
Struktur verwenden, um eine ähnliche Verarbeitung von find durchzuführen, bei der mehrere Codezeilen für jede Datei ausgeführt werden müssten (Modifizieren von Zeichenfolge, Echo, GST-Start usw.). ) undrsync
würde dies nicht erreichen. Aus diesem Grund habe ich angegeben, dass ich in der Lage sein muss, einen komplizierteren Satz von Befehlen in einer ähnlichen Struktur auszuführen. Mein Skript verwendet diese Schleifenstruktur zweimal. Für die Frage habe ich also die mit weniger Rohheit in der Mitte gepostet.Wenn Sie GNU Parallel http: // www.gnu.org/software/parallel/ installiert haben, können Sie dies tun:
Sehen Sie sich das Einführungsvideo für GNU Parallel an, um mehr zu erfahren: http://www.youtube.com/watch?v=OpaiGYxkSuQ
quelle