Wie kopiere ich mehrere Dateien mit einem Platzhalter?

42

Ich habe einen Ordner mit einer Reihe von Dateien in ABC. * (Es gibt ungefähr 100 solcher Dateien). Ich möchte sie alle in neue Dateien kopieren, deren Namen mit DEF beginnen. *

Also will ich

ABC.Page1
ABC.Page2
ABC.Topic12
...etc

kopiert nach

DEF.Page1
DEF.Page2
DEF.Topic12
...etc

Was ist der einfachste Weg, dies mit einem Batch-Befehl (in BASH oder ähnlichem) zu tun? Ich denke an etwas mit sed oder awk oder xargs, aber ich habe Schwierigkeiten, die Syntax herauszufinden. Ich könnte ein Python-Skript schreiben, aber ich denke, es gibt wahrscheinlich eine Befehlszeilenlösung, die nicht zu kompliziert ist.

Tintenfisch
quelle
Ein ehrwürdiger mcDateimanager bietet eine umfassende Unterstützung für diese Art von Massenumbenennungen.
Oakad
@oakad tarist auch kein Frühlingshuhn. Siehe meine Antwort für ein Beispiel.
mikeserv

Antworten:

41

Wie wäre es mit so etwas in bash:

for file in ABC.*; do cp "$file" "${file/ABC/DEF}";done

Sie können es testen, indem Sie echo vor den Befehl cp stellen:

for file in ABC.*; do echo cp "$file" "${file/ABC/DEF}";done
Magnus
quelle
perfekt. noch einfacher als ich es mir vorgestellt habe.
Octopus
In den meisten Fällen wird es gut genug sein, aber hier ist es entscheidend, das Echo zu überprüfen. Mach das niemals in einem Skript . Für weitere Informationen lesen Sie bash Fallstricke
pawel7318
@ pawel7318, der Link, den Sie angegeben haben, scheint, obwohl er ausgezeichnete Ratschläge enthält, nicht für den oben angegebenen Befehl zu gelten. Die einzigen Randfälle, die ich bei diesem Befehl sehe, sind: (1) Überprüft nicht, ob die Zieldatei bereits vorhanden ist oder ob die Zieldatei bereits als Verzeichnis vorhanden ist (wodurch die Datei in das Verzeichnis kopiert wird , wobei der aktuelle Name beibehalten wird). und (2) wenn es so viele Dateien gibt, dass die Glob-Erweiterung nicht in eine einzelne Befehlszeile passt.
Wildcard
1
Ich bekomme das: cp /usr/local/lib/libboost* /usr/local/lib/libboost*mit dem Echo Check.
Tomáš Zato
8

Um dies effizient mit einer großen Anzahl von Dateien zu tun, ist es besser, zu vermeiden cp, dass für jede Datei ein anderer Prozess gestartet wird. Eine Möglichkeit wäre, sie mit zu kopieren und dann umzubenennen prename( renameist in Debian-basierten Distributionen standardmäßig damit verbunden). Verwendung dieses und des Linux mktemp:

tmp=$(mktemp -d --tmpdir=.)
cp ABC.* "$tmp"
prename "s:$tmp/ABC:DEF:" "$tmp/"*
rmdir "$tmp"

Aktualisieren

Eigentlich paxkönnte es ein besserer Weg sein, hierher zu kommen:

pax -rws '/^ABC/DEF/' ABC.* .

Obwohl paxes sich sowohl um POSIX als auch um Linux Standard Base handelt, wird es derzeit nur von wenigen Distributionen standardmäßig unterstützt.

Graeme
quelle
Nizza Touch mit der: als Trennzeichen, um ein Entweichen zu vermeiden.
Clayton Stanley
5

tar Das erledige ich sehr schnell für Sie.

PRÜFUNG

Zuerst habe ich 2 Verzeichnisse und 10 Dateien erstellt:

% mkdir test1 test2 ; cd test1
% for n in `seq 1 10` ; do touch ABC.file$n ; done
% ls

> ABC.file1   ABC.file2  ABC.file4  ABC.file6  ABC.file8
> ABC.file10  ABC.file3  ABC.file5  ABC.file7  ABC.file9

Dann habe ich sie kopiert:

% tar -cf - ./* |\ 
    tar -C../test2 --transform='s/ABC/DEF/' -xf -
% ls ../test2

> DEF.file1   DEF.file2  DEF.file4  DEF.file6  DEF.file8
> DEF.file10  DEF.file3  DEF.file5  DEF.file7  DEF.file9

VERWANDELN

Also wird GNU tarein sed --transform=EXPRESSIONzum Umbenennen von Dateien akzeptieren . Dies kann sogar nur einige der Dateien umbenennen. Zum Beispiel:

% tar -cf - ./* |\ 
    tar -C../test2 --transform='s/ABC\(.*[0-5]\)/DEF\1/' -xf -
% ls ../test2

> ABC.file6  ABC.file8  DEF.file1   DEF.file2  DEF.file4
> ABC.file7  ABC.file9  DEF.file10  DEF.file3  DEF.file5

Das ist also ein Vorteil.

STROM

Bedenken Sie auch, dass dies nur zwei tarProzesse sind - und dass sich dies unabhängig von der Anzahl Ihrer Dateien nicht ändert.

tar | tar

tarist so optimiert, wie Sie es sich wünschen könnten. Dies wird niemals problematische Argumente oder außer Kontrolle geratene untergeordnete Prozesse haben. Dies ist nur A> B erledigt.

ARGUMENTE

Ich benutze hier 7 verschiedene Argumente, die zwischen meinen beiden tarProzessen kombiniert sind. Das wichtigste ist hier zuerst aufgeführt:

-stdout / stdin - Dies informiert, tardass es entweder seine Eingabe oder Ausgabe streamen wird, zu oder von stdin/stdoutder es richtig interpretiert wird, abhängig davon, ob es ein Archiv erstellt oder extrahiert.

-ccreate - tarhiermit wird das Archiv erstellt. Das nächste tarerwartete Argument ist ...

-ffile - wir geben an, dass tarmit einem fileObjekt gearbeitet wird und nicht mit einem Bandgerät oder was auch immer. Und die Datei, mit der es arbeiten wird, ist stdin/stdout- mit anderen Worten - unsere |pipe.

./*alle $ PWD / -Dateien - hier nicht zu viel zu erklären, außer dass das Archivierungsargument an erster Stelle steht, also -dann ./*.

... und auf der anderen Seite des |pipe...

-Cchange directory ( Verzeichnis wechseln) - Hiermit wird mitgeteilt, tardass das von mir angegebene Verzeichnis geändert werden muss, bevor eine andere Aktion ausgeführt wird, und zwar direkt cd ../test2vor dem Extrahieren.

--transform='s/ed/EXPR/'- Wie bereits erwähnt, wurde dies umbenannt. Aber die Dokumente zeigen an, dass es einen beliebigen sedAusdruck oder annehmen kann //flag.

-xExtrahieren - Nach tarÄnderungen in unserem Zielverzeichnis und dem Erhalt unserer Umbenennungsanweisungen weisen wir es an, alle Dateien aus der -f - |pipeArchivdatei in sein aktuelles Verzeichnis zu extrahieren . Kein Rätsel.

mikeserv
quelle
1
Hier ist die paxLösung für diese Frage. Ich würde es meiner Antwort hinzufügen, aber ich denke, es ist hier besser geeignet -pax -rws '/^ABC/DEF/' ABC.* .
Graeme
@Graeme - woah - das ist viel besser! Mann, es schlägt die Hölle aus der Perlenumbenennung auch heraus! Sie sollten dies in Ihre Antwort einfügen - paxist POSIX angegeben - daher der pax.
mikeserv
paxkann POSIX sein, wird aber leider nur selten standardmäßig installiert.
Graeme
4

Wenn es Ihnen nichts ausmacht, ein weniger standardmäßiges Tool zu verwenden, empfehle ich die Verwendung von mmv (Mass Move), das für diese Art von Aufgaben geschrieben wurde. Mit mcp(Massenkopie), die Teil der Suite ist, könnten Sie einfach tun

mcp "ABC.*" "DEF.#1"

Es ist nicht ganz einfach, Download-Pakete zu finden. Das aktuelle Debian-Paket (das Arch auch in AUR verwendet), um es zu Hause zu kompilieren, finden Sie hier .

jonny
quelle
wird sofort auf Ubuntu 15.10 installiert,apt-get install mmv
Thufir
2

Fügen Sie in zsh die folgenden Zeilen ein .zshrc:

autoload -U zmv
alias zmv='noglob zmv'
alias zcp='zmv -C'
alias zln='zmv -L'

Dann können Sie in der Befehlszeile ausführen

zcp 'ABC(.*)' 'DEF$1'

oder einfach

zcp -W ABC.* DEF.*

In einer anderen Shell, solange Sie zsh installiert haben:

zsh -c 'autoload zmv; noglob zmv -CW ABC.* DEF.*'
Gilles 'SO - hör auf böse zu sein'
quelle
1

Sie könnten dies tun:

for i in ABC.*; do cp {ABC,DEF}."${i#*.}"; done
Sara Fauzia
quelle
Vielleicht sehe ich etwas nicht, aber wie lege ich mehrere Ziele fest? Ich benutze die for-Schleife, um jeden zu bekommen. Ich habe den Code vorher getestet und er hat wie erwartet funktioniert, daher würde ich mich über Ihre Erklärung freuen.
Sara Fauzia
Das ist mein schlechtes, ich habe deine Antwort falsch verstanden. :-)
Chris Down
Wie soll ich das "${i#*.}"bisschen lesen ?
Dumbledad