Niemals, niemals benutzenfor foo in $(cat bar)
. Dies ist ein klassischer Fehler, der allgemein als Bash-Fallstricke Nummer 1 bekannt ist . Sie sollten stattdessen verwenden:
while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt
Wenn Sie die laufen for
Schleife, gelten bash wordsplitting zu dem, was es heißt, dass Sinn a strange blue cloud
als gelesen werden a
, strange
, blue
und cloud
:
$ cat files
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt
Vergleichen mit:
$ while IFS= read -r file; do echo "$file"; done < files
a strange blue cloud.txt
Oder sogar, wenn Sie auf der UUoC bestehen :
$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt
Die while
Schleife liest also ihre Eingabe und verwendet die read
, um jede Zeile einer Variablen zuzuweisen. Das IFS=
setzt das Eingabefeldtrennzeichen auf NULL * und die -r
Option , die read
Interpretation von Backslash-Escapezeichen zu verhindern (wird also \t
als Schrägstrich + t
und nicht als Tabulator behandelt). Das --
Nachher mv
bedeutet "Behandle alles nach dem - als Argument und nicht als Option", mit dem du mit Dateinamen umgehen kannst, die -
richtig beginnen.
* Dies ist hier streng genommen nicht erforderlich. Der einzige Vorteil in diesem Szenario besteht darin, read
dass keine führenden oder nachfolgenden Leerzeichen entfernt werden. Es ist jedoch eine gute Angewohnheit, wenn Sie sich mit Dateinamen befassen müssen, die Zeilenumbrüche enthalten, oder im Allgemeinen, wenn Sie in der Lage sein müssen, mit beliebigen Dateinamen umzugehen.
IFS=
hilft nicht bei Zeilenumbrüchen in Dateinamen. Das Format der Datei hier erlaubt einfach keine Dateinamen mit Zeilenumbrüchen.IFS=
wird für Dateinamen benötigt, die mit Leerzeichen oder Tabulatoren enden (oder für andere Zeichen als Zeilenumbruch $ IFS, die zuvor enthalten waren).ls
oderfind
mit Befehlsersetzungen zu analysieren, wenn es bessere und zuverlässigere Möglichkeiten gibt.$(cat file)
wäre in Ordnung, wenn es richtig gemacht würde. Es ist ungefähr so schwierig, es mit einer while-Leseschleife "richtig zu machen" wie mit einer für + $ (...). Siehe meine Antwort.Das
$(cat file_list.txt)
in POSIX-Shells wiebash
im Listenkontext ist der Split + Glob-Operator (zsh
nur der Split- Teil, wie Sie es erwarten würden).Es teilt sich in Zeichen von
$IFS
(standardmäßig SPC , TAB und NL) auf und führt Glob aus, es sei denn, Sie deaktivieren Globbing insgesamt.Hier möchten Sie nur auf Newline teilen und möchten nicht den Glob-Teil, also sollte es sein:
while read
Dies hat auch den Vorteil (über eine Schleife), leere Zeilen zu verwerfen, eine nachfolgende nicht abgeschlossene Zeile beizubehalten und den Standardwert beizubehaltenmv
(der beispielsweise bei Eingabeaufforderungen erforderlich ist).Es hat jedoch den Nachteil, dass der gesamte Inhalt der Datei im Speicher gespeichert werden muss (mehrmals mit Shells wie
bash
undzsh
).Mit einigen Muscheln (
ksh
,zsh
und in geringerem Maßebash
), können Sie es mit Optimierung$(<file_list.txt)
statt$(cat file_list.txt)
.Um das Äquivalent mit einer
while read
Schleife zu machen, benötigen Sie:Oder mit
bash
:Oder mit
zsh
:Oder mit GNU
mv
undzsh
:Oder mit GNU
mv
und GNUxargs
und ksh / zsh / bash:Lesen Sie mehr darüber, was es bedeutet, Erweiterungen bei den Auswirkungen auf die Sicherheit nicht zu zitieren, wenn Sie vergessen, eine Variable in Bash / POSIX-Shells zu zitieren
quelle
$(cat file_list.txt)
die gesamte Datei vor dem Iterieren in den Speicherwhile read ...
gelesen wird , während jeweils nur eine Zeile gelesen wird. Dies kann ein Problem für große Dateien sein.Anstatt ein Skript zu schreiben, können Sie find .. verwenden.
Für den Fall, dass sich die Dateien in der Datei befinden, können Sie Folgendes tun:
der zweite allerdings nicht sicher ..
Viel Glück
quelle
find
, können Sie auchfind
für alles verwenden. Warum hineinbringenxargs
?find -type f -iname '*.txt' -exec mv {} /folder/to/destination/
.-exec
verhält sich perfekt mit beliebigen Dateinamen (einschließlich solcher, die Leerzeichen, Zeilenumbrüche oder andere Verrücktheiten enthalten). Ich weiß, wie esxargs
funktioniert, danke :) Ich sehe keinen Sinn darin, einen separaten Befehl aufzurufen, wennfind
dies bereits für Sie erledigt werden kann. Daran ist nichts auszusetzen, nur ineffizient.xargs
hat auch den Nachteil, dass stdin (dasmv
für seine Eingabeaufforderungen benötigt wird) überlistet wird. Auch ein Problem mit @ terdons Lösung. Mit GNUxargs
und Schalen mit Prozesssubstitution kann mitxargs -0IF -a <(find...) mv F /dest
#! / bin / bash
DATEI =
zenity --title "Pick a Movie" --file-selection
für DATEI in "$ {DATEI [0]}"
mache Omxplayer "$ {FILE [0]}" fertig
! / bin / bash
DATEI =
zenity --title "Pick a Song" --file-selection
für DATEI in "$ {DATEI [0]}"
spiele "$ {FILE [0]}" fertig
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
für DIR in "$ {DIR [0]}"
CD "$ {DIR [0]}" && finden. -type f -name ' .ogg' -o -name ' .mp3' | sort --version-sort | während DIR lesen; spiele "$ DIR" fertig fertig
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
für DIR in "$ {DIR [0]}"
CD "$ {DIR [0]}" && finden. -type f -name ' .ogg' -o -name ' .mp3' | während DIR lesen; spiele "$ DIR" fertig fertig
quelle