Ich habe ein Problem beim Kopieren vieler Dateien in verschiedene Verzeichnisse. Lassen Sie es mich besser erklären:
Nehmen wir an, ich habe Folgendes in einem Verzeichnis:
$ ls
file1 file2 file3 file4 file5 dir1 dir2 dir3
und ich möchte jede Datei* in jedem Verzeichnis* mit einem einzigen Befehl kopieren. Ich habe es mit Folgendem versucht:
echo dir{1..3} | xargs cp file{1..5} '{}' \;
Meine Absicht war, xargs anzuweisen, jedes einzelne Verzeichnis* von Echo zu verarbeiten und alle Dateien in der verarbeiteten Eingabe zu kopieren, aber dies und ähnliches funktionierten nicht. Ich möchte die Verwendung eines Skripts vermeiden, da dies eine Aufgabe ist, die ich für etwa 20 Verzeichnisse wiederholen muss und die Namen der Dateien leicht unterschiedlich sind. Daher würde ich lieber einen Befehl als ein Skript ändern.
Antwort1
echo dir[1-3] | xargs -r -n1 cp file[1-5]
Antwort2
Sie machen einen kleinen Fehler bei der Verwendung von xargs. Ohne den Parameter -n hängt xargs alle Parameter in der Eingabe an den Befehl an und echo dir{1..3}
fügt zwischen den einzelnen Einträgen nicht den erforderlichen Wagenrücklauf ein. Daher betrachtet xargs das Ergebnis als einen Parameter.
xargs hat eine Option -n, mit der die Anzahl der Einträge begrenzt werden kann, und Sie müssen die Verzeichnisse mit einem \n oder \0 als Trennzeichen auflisten:
find . -mindepth 1 -maxdepth 1 -type d -print0 | xargs -0 -n 1 cp file{1..5}
Der Befehl „Find“ sucht nach allen Verzeichnissen im aktuellen und verwendet \0 als Trennzeichen (was besser funktioniert, wenn Sie Verzeichnisse mit Leerzeichen haben) und xargs verwendet \0 als Trennzeichen und startet den Befehl jedes Mal mit nur 1 Parameter.
Antwort3
Wenn sie tatsächlich solche Namen haben, sollte etwa das Folgende funktionieren:
#!/bin/bash
shopt -s nullglob
for dir in dir*/; do
cp file* "$dir"
done
Wenn Sie alle Dateien unabhängig vom Namen in alle Verzeichnisse kopieren möchten:
#!/bin/bash
shopt -s nullglob dotglob
for dir in */; do
for file in *; do
[[ -f $file ]] && cp "$file" "$dir"
done
done
Antwort4
Sie sind fast am Ziel. Sie müssen cp
für jedes Zielverzeichnis einen separaten Befehl ausführen, da cp
nur eine Kopie jeder Quelldatei erstellt wird. Der Hauptzweck von xargs
besteht darin, einen Befehl mit mehreren Argumenten auszuführen, aber Sie können ihm mit der Option sagen, dass er jeweils nur ein einzelnes Element übergeben soll -n
. Außerdem {} ;
ist die Syntax von find … -exec
; Sie benötigen (und können) sie nicht mit xargs
.
echo dir{1..3} | xargs -n 1 cp file{1..5}
Beachten Sie, dass dies nur funktioniert, wenn Ihre Verzeichnisnamen keine Leerzeichen oder die Zeichen enthalten \'"
, da xargs
es keine Möglichkeit gibt, festzustellen, ob diese Zeichen Teil von Dateinamen sind und keine Anführungszeichen und Trennzeichen für Eingabedateien sind. Eine alternative Methode, bei der dieses Problem nicht auftritt, ist die Verwendung einer Shell-Schleife:
for d in dirs{1..3}; do
cp file{1..5} "$d"
done
Dies alles setzt voraus, dass Sie Dateien und Verzeichnisse problemlos anhand ihrer Namen unterscheiden können. Wenn das nicht möglich ist, können Sie Verzeichnisse abgleichen, indem Sie /
am Ende ein hinzufügen. Beispielsweise */
stimmt mit allen Verzeichnissen in den aktuellen Verzeichnissen überein, foo[0-9]*/
stimmt mit allen Verzeichnissen überein, deren Name mit foo
und einer Ziffer beginnt, usw. In den meisten Shells gibt es keine ähnliche Möglichkeit, nur Nicht-Verzeichnisse abzugleichen. Wenn Sie jedoch Verzeichnisse cp
ohne Argument übergeben, um eine rekursive Kopie zu erstellen, werden die Verzeichnisse mit einer Fehlermeldung ignoriert.
for d in */; do
cp * "$d"
done
In zsh können Sie verwendenGlob-Qualifikationum nur bestimmte Dateitypen abzugleichen.
for d in *(/); do
cp *(.) $d
done