Lassen Sie xargs Dateinamen verarbeiten, die Leerzeichen enthalten

252
$ ls *mp3 | xargs mplayer  

Playing Lemon.  
File not found: 'Lemon'  
Playing Tree.mp3.  
File not found: 'Tree.mp3'  

Exiting... (End of file)  

Mein Befehl schlägt fehl, weil die Datei "Lemon Tree.mp3" Leerzeichen enthält und xargs denkt, dass es sich um zwei Dateien handelt. Kann ich find + xargs mit solchen Dateinamen arbeiten lassen?

Showkey
quelle
Stattdessen ls |grep mp3 |sed -n "7p"können Sie einfach verwenden echo "Lemon Tree.mp3".
Micha Wiedenmann
Diese Frage wird auch von stackoverflow.com/a/33528111/94687
imz - Ivan Zakharyaschev

Antworten:

256

Der xargsBefehl verwendet Leerzeichen (Tabulatoren, Leerzeichen, neue Zeilen) als Trennzeichen. Sie können es nur für die neuen Zeilenzeichen ('\ n') mit der folgenden -dOption eingrenzen:

ls *.mp3 | xargs -d '\n' mplayer

Es funktioniert nur mit GNU xargs. Verwenden Sie für BSD-Systeme die folgende -0Option:

ls *.mp3 | xargs -0 mplayer

Diese Methode ist einfacher und funktioniert auch mit den GNU xargs.

Strahl
quelle
6
Beste Antwort für den allgemeinen Gebrauch! Dies funktioniert auch, wenn Ihr vorheriger Befehl nicht "find" ist
nexayq
28
Leider ist diese Option unter OS X nicht verfügbar.
Thomas Tempelmann
25
@ Thomas Für OS X ist die Flagge -E, zum Beispiel:xargs -E '\n'
30
Unter OS X hatte -E '\ n' keinen Effekt für mich und ich würde es auch nicht erwarten, da es den eofstr und nicht das Datensatztrennzeichen modifizierte. Ich konnte jedoch das Flag -0 als Lösung verwenden, auch wenn der vorherige Befehl nicht 'find' lautet, indem ich den Effekt des Flag-print0 von find in meiner Eingabe simulierte, z. B.: Ls * mp3 | tr '\ n' '\ 0' | xargs -0 mplayer
biomiker
10
Für OS X können Sie "findutils installieren" brauen, wodurch Sie den Befehl "gxargs" erhalten, der den Schalter -d enthält.
Tom De Leu
213

Das Dienstprogramm xargs liest durch Leerzeichen, Tabulatoren, Zeilenumbrüche und Dateiende getrennte Zeichenfolgen aus der Standardeingabe und führt das Dienstprogramm mit den Zeichenfolgen als Argumente aus.

Sie möchten vermeiden, Leerzeichen als Trennzeichen zu verwenden. Dies kann durch Ändern des Trennzeichens für xargs erfolgen. Nach dem Handbuch:

 -0      Change xargs to expect NUL (``\0'') characters as separators,
         instead of spaces and newlines.  This is expected to be used in
         concert with the -print0 function in find(1).

Sowie:

 find . -name "*.mp3" -print0 | xargs -0 mplayer

Um die Frage nach dem Abspielen des siebten MP3 zu beantworten; es ist einfacher zu laufen

 mplayer "$(ls *.mp3 | sed -n 7p)"
Jens
quelle
10
Dies verwendet GNU findund GNU xargs; Nicht alle Versionen dieser Programme unterstützen diese Optionen (obwohl es einen Fall gibt, dass sie sollten).
Jonathan Leffler
1
@ JonathanLeffler s / GNU / FreeBSD / g; POSIX hat leider Angst vor NUL-Zeichen in Textdateien und hatte noch nicht genug Therapie :-) Mein Rat greift tatsächlich auf nicht tragbare Optionen zurück.
Jens
6
Und Mac OS X (ein BSD-Derivat) hat findmit -print0und xargsmit -0. AFAIK, HP-UX, AIX und Solaris jedoch nicht (aber ich muss noch korrigiert werden: HP-UX 11i nicht; Solaris 10 nicht; AIX 5.x nicht; aber es handelt sich nicht um aktuelle Versionen ). Es wäre nicht zu ändern hart sein sed, zum Beispiel ‚Linien‘ mit der Endung zu verwenden , '\0'statt '\n', und das POSIX 2008 getdelim()würde es einfach zu verwalten.
Jonathan Leffler
2
+1 + 1 Trick für die Verwendung mit Dateipfaden, die Listendateien enthalten: cat $ file_paths_list_file | perl -ne 's | \ n | \ 000 | g; print' | xargs -0 zip $ zip_package
Yordan Georgiev
2
Gute Idee, die Zeilenumbrüche durch NUL zu ersetzen - ich musste dies auf einem eingebetteten System tun, das weder GNU find noch GNU xargs noch perl hatte - aber der Befehl tr kann genutzt werden, um dasselbe zu tun: cat $ file_paths_list_file | tr '\ n' '\ 0' | xargs -0 du -hms
joensson
28

Versuchen

find . -name \*.mp3 -print0 | xargs -0 mplayer

anstatt

ls | grep mp3 
Scott C Wilson
quelle
16

xargs unter MacOS hat keine Option -d, daher verwendet diese Lösung stattdessen -0.

Lassen Sie ls eine Datei pro Zeile ausgeben, übersetzen Sie dann Zeilenumbrüche in Nullen und weisen Sie xargs an, Nullen als Trennzeichen zu verwenden:

ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer

Graeme Pyle
quelle
8
find . -name 'Lemon*.mp3' -print0 | xargs 0 -i mplayer '{}' 

Dies hat in meinem Fall geholfen, verschiedene Dateien mit Leerzeichen zu löschen. Es sollte auch mit mplayer funktionieren. Der notwendige Trick sind die Anführungszeichen. (Getestet unter Linux Xubuntu 14.04.)

Bendel
quelle
7

Dick.Guertins Antwort [1] schlug vor, dass man den Leerzeichen in einem Dateinamen entkommen könnte. Dies ist eine wertvolle Alternative zu anderen hier vorgeschlagenen Lösungen (z. B. die Verwendung eines Nullzeichens als Trennzeichen anstelle von Leerzeichen). Aber es könnte einfacher sein - Sie brauchen nicht wirklich einen einzigartigen Charakter. Sie können die maskierten Leerzeichen einfach direkt hinzufügen lassen:

ls | grep ' ' | sed 's| |\\ |g' | xargs ...

Darüber hinaus ist das grep nur erforderlich, wenn Sie nur Dateien mit Leerzeichen in den Namen möchten. Allgemeiner (z. B. wenn Sie einen Stapel von Dateien verarbeiten, von denen einige Leerzeichen enthalten, andere nicht), überspringen Sie einfach das grep:

ls | sed 's| |\\ |g' | xargs ...

Dann kann der Dateiname natürlich ein anderes Leerzeichen als Leerzeichen haben (z. B. eine Registerkarte):

ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...

Dies setzt voraus, dass Sie ein sed haben, das -r (Extended Regex) wie GNU sed oder neuere Versionen von bsd sed unterstützt (z. B. FreeBSD, das ursprünglich die Option "-E" vor FreeBSD 8 geschrieben hat und aus Kompatibilitätsgründen sowohl -r als auch -E unterstützt mindestens durch FreeBSD 11). Andernfalls können Sie einen einfachen Klammerausdruck für Regex-Zeichenklassen verwenden und die Leerzeichen und Tabulatorzeichen manuell in die []Trennzeichen eingeben .

[1] Dies ist vielleicht besser als Kommentar oder Bearbeitung dieser Antwort geeignet, aber im Moment habe ich nicht genug Ruf, um Kommentare abzugeben, und kann nur Änderungen vorschlagen. Da die oben genannten Formen (ohne Grep) das Verhalten von Dick.Guertins ursprünglicher Antwort ändern, ist eine direkte Bearbeitung möglicherweise ohnehin nicht angebracht.

Juan
quelle
1
verrückte Unix-Typen, die Skripte ausführen, die Dateien benennen, ohne ihre Ausgabe zu berücksichtigen, das ist wer
Andrew Lorien
4

ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}

Beachten Sie, dass im obigen Befehl für jede Datei ein neuer xargsAufruf erfolgt mplayer. Dies kann für unerwünschte mplayer, für andere Ziele jedoch in Ordnung sein.

Scharfsinn
quelle
1
Eine nützliche Ergänzung zu den vorhandenen Antworten, aber es ist erwähnenswert, dass dies dazu führt mplayer, dass für jede Datei ein neuer Aufruf erfolgt. Es ist wichtig, wenn Sie versuchen, z. B ... | xargs -I{} mplayer -shuffle {}.: Dies wird trotz allem in einer völlig deterministischen Reihenfolge abgespielt -shuffle.
1
Es ist wahrscheinlich normalerweise nicht die Absicht. xargswird meistens mit Befehlen verwendet, die eine Liste von Dateinamen akzeptieren (einfaches Beispiel :) rm, und versucht, so viele Dateinamen zu übergeben, wie in jeden Aufruf passen, und wird nur bei Bedarf in mehrere Aufrufe aufgeteilt. Sie können den Unterschied erkennen, wenn Sie einen Befehl verwenden, bei dem jeder Aufruf sichtbar ist, z. B. echo(Standardeinstellung): seq 0 100000 | xargsGibt alle Zahlen von 0 bis 23695 (plattformspezifisch, aber genau das passiert auf meinem System) in der ersten Zeile bis 45539 aus in Zeile 2 usw. Und Sie haben Recht, für die meisten Befehle spielt es keine Rolle.
4

Wenn Sie unter macOS 10.12.x (Sierra) Leerzeichen in Dateinamen oder Unterverzeichnissen haben, können Sie Folgendes verwenden:

find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l
Byron Formwalt
quelle
2

Es hängt davon ab, (a) wie sehr Sie an die Nummer 7 gebunden sind, im Gegensatz zu beispielsweise Zitronen, und (b) ob einer Ihrer Dateinamen Zeilenumbrüche enthält (und ob Sie bereit sind, diese umzubenennen, wenn dies der Fall ist).

Es gibt viele Möglichkeiten, damit umzugehen, aber einige davon sind:

mplayer Lemon*.mp3

find . -name 'Lemon*.mp3' -exec mplayer {} ';'

i=0
for mp3 in *.mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

for mp3 in *.mp3
do
    case "$mp3" in
    (Lemon*) mplayer "$mp3";;
    esac
done

i=0
find . -name *.mp3 |
while read mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

Die readSchleife funktioniert nicht, wenn Dateinamen Zeilenumbrüche enthalten. Die anderen funktionieren auch mit Zeilenumbrüchen in den Namen (geschweige denn Leerzeichen) korrekt. Wenn Sie für mein Geld Dateinamen haben, die eine neue Zeile enthalten, sollten Sie die Datei ohne die neue Zeile umbenennen. Die Verwendung der doppelten Anführungszeichen um den Dateinamen ist der Schlüssel für die ordnungsgemäße Funktion der Schleifen.

Wenn Sie GNU findund GNU xargs(oder FreeBSD (* BSD?) Oder Mac OS X) haben, können Sie auch die Optionen -print0und verwenden -0, wie in:

find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer

Dies funktioniert unabhängig vom Inhalt des Namens (die einzigen zwei Zeichen, die in einem Dateinamen nicht vorkommen können, sind Schrägstrich und NUL, und der Schrägstrich verursacht keine Probleme in einem Dateipfad, sodass die Verwendung von NUL als Namensbegrenzer alles abdeckt). Wenn Sie jedoch die ersten 6 Einträge herausfiltern müssen, benötigen Sie ein Programm, das mit NUL endende 'Zeilen' anstelle von Zeilenumbrüchen behandelt ... und ich bin mir nicht sicher, ob es welche gibt.

Der erste ist bei weitem der einfachste für den vorliegenden Fall; Es kann jedoch sein, dass es nicht verallgemeinert wird, Ihre anderen Szenarien abzudecken, die Sie noch nicht aufgelistet haben.

Jonathan Leffler
quelle
2

Ich weiß, dass ich die xargsFrage nicht direkt beantworte , aber es lohnt sich, finddie -execOption zu erwähnen .

Gegeben das folgende Dateisystem:

[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush

0 directories, 4 files

Der Befehl find kann verwendet werden, um den Platz in Dream Theatre und King's X zu verwalten. So finden Sie die Schlagzeuger jeder Band mit grep:

[root@localhost]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

In der -execOption {}steht für den Dateinamen einschließlich Pfad. Beachten Sie, dass Sie nicht entkommen oder in Anführungszeichen setzen müssen.

Der Unterschied zwischen -execden Terminatoren ( +und \;) besteht darin, dass +so viele Dateinamen wie möglich in einer Befehlszeile zusammengefasst werden. Während \;der Befehl für jeden Dateinamen ausführen.

Also find bands/ -type f -exec grep Drums {} +ergibt sich:

grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"

und find bands/ -type f -exec grep Drums {} \;führt zu:

grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"

In diesem Fall grephat dies den Nebeneffekt, dass entweder der Dateiname gedruckt wird oder nicht.

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

Natürlich grep‚s Optionen -hund -Hsteuert , ob der Dateiname gedruckt wird , unabhängig davon , wie grepgenannt wird.


xargs

xargs kann auch steuern, wie sich Man-Dateien in der Befehlszeile befinden.

xargsStandardmäßig werden alle Argumente in einer Zeile zusammengefasst. Um das gleiche zu tun, was -exec \;verwendet wird xargs -l. Beachten Sie, dass die -tOption anweist xargs, den Befehl zu drucken, bevor er ausgeführt wird.

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n' -l -t grep Drums
grep Drums ./bands/Dream Theater 
Drums:Mike Mangini
grep Drums ./bands/Rush 
Drums: Neil Peart
grep Drums ./bands/King's X 
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth 
Drums:Dirk Verbeuren

-lStellen Sie sicher, dass die Option xargs anweist, grep für jeden Dateinamen auszuführen.

Versus der Standardeinstellung (dh keine -lOption):

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren

xargshat eine bessere Kontrolle darüber, wie viele Dateien sich in der Befehlszeile befinden können. Geben Sie der -lOption die maximale Anzahl von Dateien pro Befehl.

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -l2 -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
grep Drums ./bands/King's X ./bands/Megadeth 
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[root@localhost bokeh]# 

Siehe, dass grepmit zwei Dateinamen wegen ausgeführt wurde -l2.

Spitzmaus
quelle
1

In Anbetracht des spezifischen Titels dieses Beitrags ist hier mein Vorschlag:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'

Die Idee ist, Leerzeichen in ein eindeutiges Zeichen wie '<' umzuwandeln und dieses dann in '\' zu ändern, einen Backslash gefolgt von einem Leerzeichen. Sie können dies dann in einen beliebigen Befehl einfügen, z. B.:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo

Der Schlüssel liegt hier in den Befehlen 'tr' und 'sed'; und Sie können jedes Zeichen außer '<' verwenden, z. B. '?' oder sogar ein Tabulatorzeichen.

Dick.Guertin
quelle
Was ist der Zweck des Umweges über tr? Warum nicht einfach ls *.mp3 | sed -n '7!b;s/\([[:space:]]\)/\\\1/g;p'?
Tripleee
1
Ich habe festgestellt, dass "tr '' '?'" Die Notwendigkeit für "sed" beseitigt. Der Single "?" Das Zeichen ist nicht leer, stimmt jedoch mit JEDEM einzelnen Zeichen überein. In diesem Fall: leer. Die Wahrscheinlichkeit, dass es sich um etwas anderes handelt, ist recht gering und akzeptabel, da Sie versuchen, ALLE Dateien zu verarbeiten, die auf .mp3 enden: "ls | grep '' | tr '' '?' | xargs -L1 GetFileInfo "
Dick Guertin
Sie können auch gleichzeitig "tab" verarbeiten: tr '\ t' '??' behandelt beide.
Dick Guertin
1

Alternative Lösungen können hilfreich sein ...

Sie können mit Perl auch ein Nullzeichen am Ende Ihrer Zeilen einfügen und dann die -0Option in xargs verwenden. Im Gegensatz zu den xargs -d '\ n' (in genehmigter Antwort) funktioniert dies überall, einschließlich OS X.

Zum Beispiel, um MPEG3-Dateien, die Leerzeichen oder andere lustige Zeichen enthalten können, rekursiv aufzulisten (auszuführen, zu verschieben usw.) - würde ich verwenden:

find . | grep \.mp3 | perl -ne 'chop; print "$_\0"' | xargs -0  ls

(Hinweis: Zum Filtern bevorzuge ich die leichter zu merkende "| grep" -Syntax gegenüber den Argumenten "find's" --name.)

Charlie Dalsass
quelle