Problem mit Leerzeichen in Dateinamen

8

Ich möchte wiederholt etwas in einer Liste von Dateien tun. Die fraglichen Dateien haben Leerzeichen in ihren Namen:

david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha
-rw-rw-r-- 1 david david  0 Mai  8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (copy)

Jetzt möchte ich jede dieser Dateien statisieren:

david@david: echo '
for file in $(ls)
do
stat $file
done' | bash

(Ich benutze Echo und eine Pipe, um mehrzeilige Befehle zu schreiben.)

Wenn ich das mache, funktioniert es korrekt bei Dateien, deren Namen keine Leerzeichen enthalten. Aber die anderen ...

stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory

Das Wechseln $(ls)zu "$(ls)"oder $filezu "$file"funktioniert nicht. Was kann ich machen?

Bearbeiten:

echo '
for files in *
do
stat "$files"
done' | bash

macht den Trick! Da ich neu in Bash bin, möchte ich die Dinge so einfach wie möglich halten - also nichts mit dem Versuch, Leerzeichen zu entkommen, oder mit xargsoder der Lösung mit read -r, obwohl sie das Problem lösen.

Wie einige gefragt haben: Ja, dies anstelle zu verwenden stat *ist seltsam. Aber ich wollte nur einen allgemeinen Weg finden, um denselben Befehl mit einer for-Schleife auf eine Reihe von Dateinamen in bash anzuwenden. So statkönnte stehen für gzip, gpgoder rm.

user258532
quelle
1
Was ist los mit stat *? (;-)
Rmano
Ich benutze nur stat als Beispiel. :) Ich möchte Dateinamen mit ls sammeln und dann die Ergebnisse von ls in einer Bash-Schleife verwenden.
user258532

Antworten:

12

Das mehrfache Zitat aus dem echo 'erschwert die Sache.

Sie können einfach verwenden:

for f in *; do stat -- "$f"; done

Aber auch

stat -- * 

... und wenn Sie die Dateien sammeln und dann den Befehl anwenden möchten (warum?), können Sie fortfahren (aber seien Sie vorsichtig mit Dateien, die neue Zeilen enthalten ... (1))

for f in *; do echo "$f"; done | xargs stat --

... und wenn Sie auch versteckte Dateien möchten, verwenden Sie diese einfach * .*als Muster, aber denken Sie daran .und ..sie werden im Set enthalten sein .

Nebenbei sollten Sie die lsAusgabe nicht analysieren .


(1) aber wenn Sie Dateinamen mit Zeilenumbrüchen haben, haben Sie es etwas verdient ... ;-)

Rmano
quelle
"und dann den Befehl anwenden (warum?)" -> stat dient nur als willkürlicher Befehl, da ich versuche, Bash-Schleifen mit Dateinamen auszuführen. Es könnte gpg, gzip oder was auch immer sein.
user258532
@ user258532 unabhängig vom Befehl immer verwenden for f in *; do command "$f"; done. Niemals analysieren ls, niemals in einer for Schleife ausführen und warum verwenden echo?
Terdon
echo: Weil ich die Befehle gerne in mehreren Zeilen schreibe ... :)
user258532
2
@ user258532 Huh? Warum brauchst du dafür Echo? Drücken Sie einfach die Eingabetaste und fahren Sie mit einer neuen Zeile fort. Wenn Sie eine Linie mit einem offenen Zitat Ende oder an einen der do, |, &&etc, können Sie auf die neue Linie fortzusetzen. Entweder das oder Heredocs verwenden. Kein Grund zur Verwendung echound es kann auch Probleme verursachen.
Terdon
"Drücken Sie einfach die Eingabetaste und fahren Sie mit einer neuen Zeile fort." Autsch. :-D Jetzt ist mein IQ offiziell unter 0 - weißt du, ich bin wirklich neu in Bash und solchen Dingen. Aber ich verdiene mein Geld tatsächlich mit R (der Statistiksuite und der Skriptsprache).
user258532
6

EnterNebenbei bemerkt: Sie können lange / komplizierte Befehle auf mehrere Zeilen aufteilen, indem Sie ein Leerzeichen gefolgt von einem Backslash hinzufügen und jedes Mal drücken , wenn Sie mit dem Schreiben in eine neue Zeile beginnen möchten, anstatt mehrere Prozesse mithilfe von zu forken echo [...] | bash. Außerdem sollten Sie $filedoppelte Anführungszeichen einfügen, um zu verhindern, dass statbei Dateinamen, die Leerzeichen enthalten, Fehler auftreten:

for file in $(ls); \
do \
stat "$file"; \
done

Das Problem ist, dass es $(ls)zu einer Liste von Dateinamen mit Leerzeichen erweitert wird, und dasselbe passiert auch mit "$(ls)".

Selbst wenn dieses Problem gelöst wird, wird diese Methode bei Dateinamen mit Backslashes und bei Dateinamen mit Zeilenumbrüchen (wie von terdon hervorgehoben) immer noch unterbrochen.

Eine Lösung für beide Probleme wäre, die Ausgabe von findan eine whileSchleife weiterzuleiten, die read -rso ausgeführt wird, dass bei jeder Iteration read -reine findAusgabezeile gespeichert wird in $file:

find . -maxdepth 1 -type f | while read -r file; do \
    stat "$file"; \
done
kos
quelle
1
und versteckte Dateien? :)
AB
5
Bei Dateinamen mit Zeilenumbrüchen schlägt dies weiterhin fehl. Nur nicht analysieren ls. Je.
Terdon
4
@ user258532 nein, tut es nicht. Im Ernst, analysierels einfach nicht . Es gibt bessere und robustere Wege. Vielleicht möchten Sie auch Folgendes lesen: Warum * nicht * `ls` analysieren? für mehr Details.
Terdon
1
Das Problem hier ist nicht ls, es ist for- foriteriert über (IFS getrennte) Wörter, die nach dem inSchlüsselwort angegeben werden`
Glenn Jackman
1
@glennjackman na ja, es ist die Kombination von forund ls. for f in *wäre zum Beispiel in Ordnung.
Terdon
3

Verwenden Sie das gute alte find, arbeitet mit versteckten Dateien, Zeilenumbrüchen und Leerzeichen.

find . -print0 | xargs -I {} -0 stat {}

oder irgendein anderes statt stat

find . -print0 | xargs -I {} -0 file {}
find . -print0 | xargs -I {} -0 cat {}
AB
quelle
1

Als R-Typ habe ich bereits eine Problemumgehung in R gefunden:

filenames <- dir(); # reads file names into an array.
                    # It works also recursively
                    # with dir(recursive=TRUE)
for (i in 1:length(filenames)) {
system(     # calls a system function
 paste(     # join stat and the file name
  "stat",
  filenames[i]
 )
)
}

Ich weiß, es ist verrückt. Ich wünschte, die Ausgabe von lswäre einfacher zu analysieren ... R kann mit Leerzeichen umgehen, da dir () einen Zeichenwert in Anführungszeichen zurückgibt. Alles zwischen den Anführungszeichen ist dann ein gültiger Dateiname mit Leerzeichen.

user258532
quelle
3
Mach dir keine Sorgen (aber +1 für Mühe! :). Verwenden Sie einfach for f in *, drücken Sie die Eingabetaste und fahren Sie mit einer neuen Zeile fort : do, drücken Sie erneut die stat "$f"Eingabetaste, erneut die doneEingabetaste , geben Sie die Eingabetaste ein. Das ist ein Befehl, der gut in 4 Zeilen aufgeteilt ist und bei keinem Dateinamen kaputt geht.
Terdon
1

Ich bin auf andere Instanzen von Leerzeichenproblemen in for-Schleifen gestoßen, daher verwende ich im Allgemeinen den folgenden (imo robusteren) Befehl. Es passt auch gut in Rohre.

$ ls | while read line; do stat "$line"; done;

Sie können dies mit kombinieren grepoder findstattdessen verwenden:

$ find ./ -maxdepth 2 | grep '^\./[/a-z]+$' | while read line; do stat "$line"; done;
Tyzoid
quelle
Ihre erste Antwort schlägt bei Dateinamen fehl, die Backslash oder führende oder nachfolgende Leerzeichen enthalten. Ihre zweite Antwort schlägt vollständig fehl, es sei denn, Sie fügen die -EOption hinzu grep, ohne die sie +in einem regulären Ausdruck nicht erkannt wird . Selbst dann ist es ein Swing und ein Miss bei dieser Frage, da Sie grep Dateinamen mit Leerzeichen entfernen . Außerdem werden Dateinamen entfernt, die Ziffern (Ziffern) und Satzzeichen (z. B. Klammern) enthalten, wie dies in den Beispielen in der Frage der Fall ist. Dabei werden nicht einmal Dateinamen erwähnt, die Zeilenumbrüche enthalten oder mit -(Bindestrich) beginnen.
Scott
-1

Diese Antwort löst das Problem des Parsens lsund kümmert sich um die Backspaces und neuen Zeilen

Versuchen Sie dies, es wird Ihr Problem mit Internal Field Separator IFS lösen.

IFS="\n" for f in $(ls); do   stat "$f"; done

Aber Sie können es auch problemlos lösen, ohne die Ausgabe mit analysieren zu müssen

for f in *; do   stat "$f"; done
Maythux
quelle
1
funktioniert nicht für versteckte Dateien.
AB
2
OP hat nicht nach versteckten Dateien
gefragt
1
Ich verstehe nicht, warum Sie IFShier etwas ändern müssen : Das Zitieren der Variablen sollte ausreichen, um eine Wortaufteilung zu verhindern.
Steeldriver
zum Parsen ls .
Maythux
Für was ist das Downvote !!!
Maythux
-1

Stattdessen können Sie Ihre Dateien umbenennen und das Leerzeichen durch ein anderes Zeichen wie Unterstrich ersetzen, um dieses Problem zu beheben:

Führen Sie dazu einfach den folgenden Befehl aus:

for file in * ; do mv "$f" "${f// /_}" ; done
Maythux
quelle