it-swarm.com.de

Wie kann ich mit xargs Dateien kopieren, deren Namen Leerzeichen und Anführungszeichen enthalten?

Ich versuche, eine Reihe von Dateien unter ein Verzeichnis zu kopieren, und einige der Dateien enthalten Leerzeichen und einfache Anführungszeichen im Namen. Wenn ich versuche, find und grep mit xargs zu verbinden, erhalte ich die folgende Fehlermeldung:

find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote

Irgendwelche Vorschläge für eine robustere Verwendung von xargs?

Dies ist auf Mac OS X 10.5. (Leopard) mit BSD xargs.

219
Drew Stephens

Sie können all das in einem einzigen find Befehl kombinieren:

find . -iname "*foobar*" -exec cp -- "{}" ~/foo/bar \;

Dies behandelt Dateinamen und Verzeichnisse mit Leerzeichen. Sie können -name Verwenden, um Ergebnisse zu erhalten, bei denen zwischen Groß- und Kleinschreibung unterschieden wird.

Hinweis: Das an cp übergebene Flag -- Verhindert, dass Dateien, die mit - Beginnen, als Optionen verarbeitet werden.

192
godbyk

find . -print0 | grep --null 'FooBar' | xargs -0 ...

Ich weiß nicht, ob grep--null Unterstützt oder ob xargs-0 Auf Leopard unterstützt, aber auf GNU alles gut.

114

Der einfachste Weg, das zu tun, was das Originalposter will, besteht darin, das Trennzeichen von einem Leerzeichen in ein Zeilenende-Zeichen wie das folgende zu ändern:

find whatever ... | xargs -d "\n" cp -t /var/tmp
78
user87601

Dies ist effizienter, da es nicht mehrmals "cp" ausführt:

find -name '*FooBar*' -print0 | xargs -0 cp -t ~/foo/bar
70
Tometzky

Ich bin auf dasselbe Problem gestoßen. So habe ich es gelöst:

find . -name '*FoooBar*' | sed 's/.*/"&"/' | xargs cp ~/foo/bar

Ich habe sed verwendet, um jede Eingabezeile durch dieselbe Zeile zu ersetzen, die jedoch in doppelte Anführungszeichen eingeschlossen ist. Auf der Manpage sed wird "... Ein kaufmännisches Und (` `& ''), das in der Ersetzung erscheint, durch die Zeichenfolge ersetzt, die mit dem RE ..." - in übereinstimmt dieser Fall, .*, die gesamte Zeile.

Dies löst die xargs: unterminated quote Error.

57
oyouareatubeo

Diese Methode funktioniert unter Mac OS X 10.7.5 (Lion):

find . | grep FooBar | xargs -I{} cp {} ~/foo/bar

Ich habe auch die exakte Syntax getestet, die Sie gepostet haben. Das hat auch am 10.7.5 geklappt.

51
the_minted

Verwenden Sie einfach nicht xargs. Es ist ein ordentliches Programm, aber es passt nicht zu find, wenn es sich um nicht triviale Fälle handelt.

Hier ist eine portable (POSIX) Lösung, d. H. Eine, die keine find, xargs oder cp GNU spezifische Erweiterungen benötigt:

find . -name "*FooBar*" -exec sh -c 'cp -- "[email protected]" ~/foo/bar' sh {} +

Beachten Sie die Endung + statt der üblichen ;.

Diese Lösung:

  • richtig behandelt Dateien und Verzeichnisse mit eingebetteten Leerzeichen, Zeilenumbrüchen oder was auch immer exotischen Zeichen.

  • funktioniert auf allen Unix- und Linux-Systemen, auch auf solchen, die das GNU Toolkit nicht bereitstellen.

  • verwendet kein xargs, was ein nettes und nützliches Programm ist, erfordert aber zu viele Optimierungen und nicht standardmäßige Funktionen, um die find Ausgabe richtig zu handhaben.

  • ist auch effizienter (lesen schneller) als die akzeptierten und die meisten, wenn nicht alle anderen Antworten.

Beachten Sie auch, dass trotz der Angaben in einigen anderen Antworten oder Kommentaren {} ist nutzlos (es sei denn, Sie verwenden die exotische fishShell).

11
jlliagre

Überprüfen Sie die Verwendung der Befehlszeilenoption --null für xargs mit der Option -print0 in find.

8
Shannon Nelson

Für diejenigen, die auf andere Befehle als find angewiesen sind, zB ls:

find . | grep "FooBar" | tr \\n \\0 | xargs -0 -I{} cp "{}" ~/foo/bar

Ich habe festgestellt, dass die folgende Syntax für mich gut funktioniert.

find /usr/pcapps/ -mount -type f -size +1000000c | Perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200

In diesem Beispiel suche ich nach den 200 größten Dateien mit mehr als 1.000.000 Bytes im Dateisystem, das unter "/ usr/pcapps" bereitgestellt wird.

Der Perl-Zeilenumbruch zwischen "find" und "xargs" entgeht/setzt jedes Leerzeichen in Anführungszeichen, sodass "xargs" jeden Dateinamen mit eingebetteten Leerzeichen als einzelnes Argument an "ls" übergibt.

5
bill_starr
find | Perl -lne 'print quotemeta' | xargs ls -d

Ich glaube, dass dies für alle Charaktere mit Ausnahme des Zeilenvorschubs zuverlässig funktioniert (und ich vermute, dass Sie schlimmere Probleme haben, wenn Ihre Dateinamen Zeilenvorschübe enthalten). Es benötigt keine GNU findutils, nur Perl, also sollte es praktisch überall funktionieren.

5
mavit

Ich habe versucht, etwas anderes zu machen. Ich wollte meine .txt-Dateien in meinen tmp-Ordner kopieren. Die TXT-Dateinamen enthalten Leerzeichen und Apostrophzeichen. Dies funktionierte auf meinem Mac.

$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/'  | xargs -I{} cp -v {} ./tmp/
2
Moises

Mit Bash (nicht POSIX) können Sie die Prozessersetzung verwenden, um die aktuelle Zeile in einer Variablen abzurufen. Auf diese Weise können Sie Sonderzeichen in Anführungszeichen setzen:

while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
2
StackedCrooked

Beachten Sie, dass die meisten der in anderen Antworten behandelten Optionen auf Plattformen, auf denen die Dienstprogramme GNU) nicht verwendet werden (z. B. Solaris, AIX, HP-UX), nicht zum Standard gehören. Siehe POSIX Spezifikation für 'Standard'-Xargs-Verhalten.

Ich empfinde auch das Verhalten von xargs, bei dem der Befehl mindestens einmal ausgeführt wird, auch ohne Eingabe, als störend.

Ich habe meine eigene private Version von xargs (xargl) geschrieben, um die Probleme mit Leerzeichen in Namen zu lösen (nur Zeilenumbrüche getrennt) enthalten ASCII NUL '\ 0' Zeichen. Mein xargl ist nicht so vollständig, wie es sein müsste, um veröffentlicht zu werden - zumal GNU Einrichtungen hat das sind mindestens so gut.

2

Ich habe ein kleines portables Wrapper-Skript namens "xargsL" um "xargs" erstellt, das die meisten Probleme behebt.

Im Gegensatz zu xargs akzeptiert xargsL einen Pfadnamen pro Zeile. Die Pfadnamen dürfen alle Zeichen außer (offensichtlich) Newline- oder NUL-Bytes enthalten.

In der Dateiliste sind keine Anführungszeichen zulässig oder werden nicht unterstützt. Ihre Dateinamen können alle Arten von Leerzeichen, Backslashes, Backticks, Shell-Platzhalterzeichen und dergleichen enthalten.

Als zusätzliches Bonus-Feature wird xargsL den Befehl nicht einmal ausführen , wenn keine Eingabe erfolgt!

Beachten Sie den Unterschied:

$ true | xargs echo no data
no data

$ true | xargsL echo no data # No output

Alle Argumente, die an xargsL übergeben werden, werden an xargs weitergeleitet.

Hier ist das POSIX-Shell-Skript "xargsL":

#! /bin/sh
# Line-based version of "xargs" (one pathname per line which may contain any
# amount of whitespace except for newlines) with the added bonus feature that
# it will not execute the command if the input file is empty.
#
# Version 2018.76.3
#
# Copyright (c) 2018 Guenther Brunthaler. All rights reserved.
#
# This script is free software.
# Distribution is permitted under the terms of the GPLv3.

set -e
trap 'test $? = 0 || echo "$0 failed!" >& 2' 0

if IFS= read -r first
then
        {
                printf '%s\n' "$first"
                cat
        } | sed 's/./\\&/g' | xargs ${1+"[email protected]"}
fi

Legen Sie das Skript in ein Verzeichnis in Ihrem $ PATH und vergessen Sie nicht

$ chmod +x xargsL

das Skript dort, um es ausführbar zu machen.

1

Ich habe Antwort von Bill Star unter Solaris leicht modifiziert:

find . -mtime +2 | Perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file

Dies wird Anführungszeichen um jede Zeile setzen. Ich habe die Option '-l' nicht verwendet, obwohl dies wahrscheinlich helfen würde.

Die Dateiliste, in die ich mich begeben habe, könnte ein '-' enthalten, aber keine Zeilenumbrüche. Ich habe die Ausgabedatei nicht mit anderen Befehlen verwendet, da ich überprüfen möchte, was gefunden wurde, bevor ich sie einfach über xargs massiv lösche.

1

Ich habe ein wenig damit gespielt, angefangen, über das Modifizieren von XARGs nachzudenken, und festgestellt, dass für die Art von Anwendungsfall, von dem wir hier sprechen, eine einfache Neuimplementierung in Python eine bessere Idee ist.

Zum einen bedeutet das Vorhandensein von ~ 80 Codezeilen für das Ganze, dass es einfach ist, herauszufinden, was vor sich geht, und wenn ein anderes Verhalten erforderlich ist, können Sie es einfach in kürzerer Zeit als nötig in ein neues Skript hacken eine Antwort auf irgendwo wie Stack Overflow.

Siehe https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs und https://github.com/johnallsup/jda-misc-scripts/ blob/master/zargs.py .

Mit yargs wie geschrieben (und Python 3 installiert) können Sie Folgendes eingeben:

find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar

um 203 Dateien gleichzeitig zu kopieren. (Hier ist 203 natürlich nur ein Platzhalter, und die Verwendung einer seltsamen Zahl wie 203 macht deutlich, dass diese Zahl keine andere Bedeutung hat.)

Wenn Sie etwas wirklich schneller und ohne Python wollen, nehmen Sie Zargs und Yargs als Prototypen und schreiben Sie es in C++ oder C um.

1
John Allsup

bill_starrs Perl-Version funktioniert nicht gut für eingebettete Zeilenumbrüche (nur mit Leerzeichen umgehen). Für diejenigen, die z.B. Solaris, wo Sie nicht die GNU Tools haben, könnte eine vollständigere Version sein (mit sed) ...

find -type f | sed 's/./\\&/g' | xargs grep string_to_find

passen Sie die find- und grep-Argumente oder andere Befehle nach Bedarf an, aber das sed repariert Ihre eingebetteten Zeilenumbrüche/Leerzeichen/Tabulatoren.

1
rjb1

Wenn find- und xarg-Versionen auf Ihrem System die Schalter -print0 Und -0 Nicht unterstützen (zum Beispiel AIX find und xargs), können Sie diesen furchtbar aussehenden Code verwenden:

 find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest

Hier kümmert sich sed darum, die Leerzeichen und Zitate für xargs zu umgehen.

Getestet unter AIX 5.3

1
Jan Ptáčník

Frame Challenge - Sie fragen sich, wie Sie XARGs verwenden sollen. Die Antwort lautet: Sie verwenden keine xargs, weil Sie es nicht brauchen.

Das Kommentar von user80168 beschreibt eine Möglichkeit, dies direkt mit cp zu tun, ohne für jede Datei cp aufzurufen:

find . -name '*FooBar*' -exec cp -t /tmp -- {} +

Das funktioniert, weil:

  • mit dem Flag cp -t kann das Zielverzeichnis am Anfang von cp und nicht am Ende angegeben werden. Von man cp:
   -t, --target-directory=DIRECTORY
         copy all SOURCE arguments into DIRECTORY
  • Das Flag -- Weist cp an, alle nachfolgenden Elemente als Dateinamen und nicht als Flag zu interpretieren, sodass Dateien, die mit - Oder -- Beginnen, cp; Sie benötigen dies weiterhin, da die Zeichen -/-- von cp interpretiert werden, während alle anderen Sonderzeichen von der Shell interpretiert werden.

  • Die Variante find -exec command {} + Verhält sich im Wesentlichen wie xargs. Von man find:

   -exec command {} +                                                     
         This  variant  of the -exec action runs the specified command on
         the selected files, but the command line is built  by  appending
         each  selected file name at the end; the total number of invoca‐
         matched  files.   The command line is built in much the same way
         that xargs builds its command lines.  Only one instance of  `{}'
         is  allowed  within the command, and (when find is being invoked
         from a Shell) it should be quoted (for example, '{}') to protect
         it  from  interpretation  by shells.  The command is executed in
         the starting directory.  If any invocation  returns  a  non-zero
         value  as exit status, then find returns a non-zero exit status.
         If find encounters an error, this can sometimes cause an immedi‐
         ate  exit, so some pending commands may not be run at all.  This
         variant of -exec always returns true.

Durch die direkte Verwendung von this in find wird die Notwendigkeit eines Pipe- oder Shell-Aufrufs vermieden, sodass Sie sich keine Gedanken über unangenehme Zeichen in Dateinamen machen müssen.

0
gerrit

Möglicherweise müssen Sie das Foobar-Verzeichnis wie folgt durchsuchen:

find . -name "file.ext"| grep "FooBar" | xargs -i cp -p "{}" .
0
fred