Hintergrund
Unter Linux können Sie:
- Listen Sie eine Gruppe von Dateien mit
ls Filename*
- Entfernen Sie eine Gruppe von Dateien mit
rm Filename*
- Verschieben Sie eine Gruppe von Dateien mit
mv Filename* /New/Directory
- Sie können eine Gruppe von Dateien jedoch nicht kopieren mit:
cp Filename* *.bak
Ändern Sie den Linux- cp
Befehl, um eine Dateigruppe zu kopieren
Ich habe eine Gruppe von Dateien, die ich kopieren möchte, ohne die Namen einzeln einzugeben und den folgenden cp
Befehl zu verwenden:
$ ls gmail-meta3*
gmail-meta3 gmail-meta3-REC-1558392194-26467821
gmail-meta3-LAB-1558392194-26467821 gmail-meta3-YAD-1558392194-26467821
Wie kann ich so etwas wie den alten DOS-Befehl verwenden copy gmail-meta3* *.bak
?
Ich möchte keinen ähnlichen Befehl viermal eingeben:
cp gmail-meta3-LAB-1558392194-26467821 gmail-meta3-LAB-1558392194-26467821.bak
Ich suche ein Skript / eine Funktion / eine App, die Parameter für alte und neue Dateinamengruppen akzeptiert und nicht etwas mit fest codierten Dateinamen. Ein Benutzer kann beispielsweise Folgendes eingeben:
copy gmail-meta3* *.bak
oder sie könnten eingeben:
copy gmail-meta3* save-*
command-line
cp
ms-dos
WinEunuuchs2Unix
quelle
quelle
touch aa ab ba; mkdir bb; cp a* b*; ls *
*
für die Quelldateinamen verwenden können, nicht jedoch für die Zieldateinamen. Als solches muss ein Ersatz-Platzhalter (ich denke, er##
wurde vorgeschlagen, aber ich neige dazu%
) für das Ziel verwendet werden. Ich denke, das ist es, was Sie verstärken? Ich hatte nicht erwartet, dencp
Befehl überhaupt zu ändern . Erstellen Sie einfach ein Wrapper-Skript mit dem Namencopy
, das den DOS-Kopierbefehl (innerhalb eines angemessenen Rahmens) emuliert.Antworten:
Hier ist ein Beispiel für eine atypische Verwendung von
sed
, die für diese Aufgabe gilt:Auf diese Weise
sed
werden die Dateien tatsächlich nicht geändert, da wir keine Befehle bereitgestellt haben.''
Aufgrund der Option-i[suffix]
wird jedoch eine Sicherungskopie jeder Datei erstellt. Ich habe diesen Ansatz bei der Suche gefunden. Gibt es eine Möglichkeit, eine Sicherungskopie einer Datei zu erstellen, ohne den Namen zweimal einzugeben?quelle
$ time sed -i.bak '' gmail-meta3*
real 0m0.069s
real 0m0.037s
. Wenn Dateien gelöscht werden und ein zweites Mal in der Nähe dercp
Geschwindigkeit ausgeführt werden :real 0m0.051s
.sed
es schneller ist, wenn bereits Zieldateien vorhanden sind, abercp
langsamer, wenn Zieldateien vorhanden sind. Ich spüle tatsächlich Caches und Puffer, anstattsync
große Timing-Tests durchzuführen, aber diesmal habe ich beides nicht getan. Da dies zu einer ganz anderen Seifenoper führen kann, die nicht zum Thema gehört, muss ich leider meine Testergebnisse mitteilen :( Ist es zu spät, um so zu tun, als ob diese Konversation nie stattgefunden hätte? Es ist auch keine Festplatte, es ist eine Samsung Pro 960 NVMe SSD auf 4 Kanälen.free
Befehl verwendet wird. Die tatsächlichen Schreibvorgänge auf das Gerät erfolgen zu einem Zeitpunkt, der von einem Algorithmus ausgewählt wird, der das Alter des Caches und den Speicherdruck auf dem Computer berücksichtigt. Wenn Sie mehrere Tests versuchen, werden die ersten Dateilesevorgänge von der Festplatte ausgeführt, die nachfolgenden Lesevorgänge werden jedoch höchstwahrscheinlich direkt aus dem Speicher ausgeführt (siehesync; echo 3 > /proc/sys/vm/drop_caches
).cp
Befehls, aber ich kann nicht sagen, dass es einen signifikanten Leistungsunterschied gibt .Sie können verwenden
find
:Dadurch werden im aktuellen Verzeichnis
.
alle Dateien mit einem Namen gefunden, der mit dem Glob-Muster übereinstimmt (beachten Sie die einfachen Anführungszeichen um das Muster, um ein Globbing der Shell zu verhindern). Für jede gefundene Datei wird siecp
von name zu name.bak ausgeführt. Das \; Am Ende wird sichergestellt, dass jede Datei einzeln ausgeführt wird, anstatt alle auf einmal zu übergeben. Die maximale Tiefe als 1 durchsucht nur das aktuelle Verzeichnis, anstatt nach unten zu rekursieren.quelle
find . -max-depth 1 -name '"$1"'' -exec cp "{}" "{}$2" \;
wenn $ 1 die Quelle und $ 2 die Erweiterung ist?Sie können eine
for
Schleife mit verwendenbash
. Normalerweise würde ich es nur als Einzeiler eingeben, da dies keine Aufgabe ist, die ich oft ausführe:Wenn Sie es jedoch als Skript benötigen:
Die Verwendung ist ähnlich wie
rename
. Zu testen:quelle
$ time for f in gmail-meta3* ; do cp -a "$f" "${f}.bak" ; done
real 0m0.046s
0.046
Sekunden, was 0 Sekunden für die menschliche Wahrnehmung bedeutet. Ich habe nur versucht zu zeigen, wie ich gepostete Antworten testete und interessante Leckerbissen an Zuschauer weitergab, die densed
obigen Befehl betrachteten. Oder zumindest war ich daran interessiert,sed
mitcp
... zu vergleichencp
ist jedoch schneller als die Lösung mitsed
. Es ist also ein Grund zum Feiern :)-a
ist ein nicht standardmäßiger Testbetreiber. Warum nicht verwenden-e
? (2) "Temporäres Verzeichnis kann nicht erstellt werden." ist eine etwas irreführende Fehlermeldung. (3) Warum nicht einfach verwendenmktemp -d
? (4) Sie sollten den Exit-Status testen. Zum Beispiel sollten Sie! mkdir "$FOLDER" && echo "Unable to create temporary directory." && return 1
oder sagenmkdir "$FOLDER" || { echo "Unable to create temporary directory."; return 1;}
. Ebenso fürcp
undrename
(und vielleicht sogarpushd
, wenn Sie vorsichtig sein wollen). … (Fortsetzung)$@
; sagen"$@"
. (5b) Es ist nicht erforderlich,{
und}
wenn Sie Variablen so referenzieren, wie Sie es tun ("${FOLDER}"
,"${PATTERN}"
und"${file}"
); nur tun"$FOLDER"
,"$PATTERN"
und"$file"
. (6) Dies setzt voraus, dass sich die Dateien im aktuellen Verzeichnis befinden.cps 's/$/.bak/' d/foo
Kopieren wirdd/foo
auffoo.bak
im aktuellen Verzeichnis, nichtd/foo.bak
.Das DOS-Paradigma wird wahrscheinlich am nächsten kommen
mcp
(aus demmmv
Paket):Wenn
zsh
verfügbar, ist das dazugehörigezmv
Modul vielleicht etwas näher:Ich würde es trotzdem vermeiden
ls
- eine Variante Ihrer eigenen Antwort , die für Leerzeichen (einschließlich Zeilenumbrüche) sicher ist, wäreoder vielleicht
quelle
mmv
ist das Paket, aber in Kommentaren sagen Sie, der Befehl istmcp
aber dann in dem Befehl, den Sie verwenden,mmv
der auch ein Befehl immmv
Paket ist. Ich mag die Richtung derprintf
Beispiele und in einem polierten Skript würde ich sicherstellen, dass $ 1 und $ 2 übergeben wurden. +1 für den Ball ins Rollen :)mcp
ist nur ein Synonym fürmmv -c
printf
Befehl, die ich nie wirklich benutzt habe. Wollen Sie damit sagen,printf '%s\0' "$1"*
dass es funktionieren würde, wenngmail-meta3
es als Parameter 1 übergeben würde?cps gmail-meta3*
und dannprintf '%s\0
"$ @" | schreiben während ... `in der Funktion. Oder verwenden Sie einfachfor f; do cp -- "$f" "$f.bak"; done
(wie Xiotas Antwort , aber als Funktion)zmv
Sie den "Wildcard-Ersatz" -Modus verwenden können, den ich etwas einfacher zu verstehen finde:zmv -W -C 'gmail-meta3*' '*.bak'
Nur rsync-Lösung
Wenn Sie nur Ihre Dateien sichern möchten, können Sie sie in ein neues Verzeichnis kopieren
rsync /path/to/dir/Filename* /path/to/backupdirectory
Dadurch werden die
Filename
Dateien von/path/to/dir/
nach kopiert/path/to/backupdirectory
.rsync + Dateiname
Wenn Sie möchten, dass Ihre Sicherungsdateien ein Suffix haben, wird es mit
rsync
...rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak
Dies würde die vorhandenen Dateien überschreiben ... mit den vorhandenen Dateien (
-I
), aber nur, wenn sie (-u
) neuer sind (was sie nicht sind) und ein Backup mit einem Suffix erstellen.Sie können dies auch im selben Verzeichnis tun. Schließen Sie vorhandene Backups jedoch besser aus.
rsync -Iu /path/to/dir/Filename* /path/to/dir/Filename* -b --backup-dir=/path/to/backupdirectory --suffix=.bak --exclude '*.bak'
quelle
rsycnc
also habe ich upvoted, aber eine einfachere Methode wäre,cp Filename* /path/to/backup/dir
weil Dateien keinen*.bak
Eindeutiger benötigen würden, wenn sie sich in einem separaten Verzeichnis befinden würden.Dieser sollte wie gewünscht tun:
Verwendung:
Ich habe gewählt,
?
weil#
es zitiert werden muss, wenn es das erste Zeichen eines Musters ist, andernfalls wird es als Beginn eines Kommentars erkannt.Prüfung:
Einige frühere Versionen des Skripts zum Hinzufügen eines Suffix:
Ähnlich wie @ WinEunuuchs2Unix Antwort, aber ich denke flexibler und nicht analysieren
ls
:Setzen Sie dies in Ihre
.bashrc
.Verwendung:
Alternative mit dem Suffix als letztem Argument ( via und via ):
Verwendung:
quelle
copy gmail-meta3* old-meta3*
. In meiner Antwort konnte ich nicht herausfinden, wie ich*
in den*
von der Shell interpretiert wird, sodass die Funktion nichts davon weiß. Sie benötigen ein anderes Zeichen oder zitieren es und ersetzen es dann durch den ursprünglichen Dateinamen innerhalb der Funktion.#
könnte als Ersatz-Platzhalter für verwendet werden*
? Sie könnten also tippencopy filenames# save-#
. Ich denke, Sie möchten, dass das Platzhalterzeichen für Quelle und Ziel identisch ist.Ich habe diesen Einzeiler in meine geschrieben
~/.bashrc
.find
Ich nehme an , es können viel bessere Antworten mit gepostet werden. Noch bessere Antworten könnten in C geschrieben werden. Hoffentlich bringt diese Frage und Antwort den Ball ins Rollen, um bessere Antworten zu erhalten:for f in "$1"*; do
:$1
ist dergmail-meta3
Parameter undf
die Liste der übereinstimmenden Dateien. Kombiniert bedeutet dies für Google Mail-Meta3, Google Mail-Meta3-LAB-9999 usw. Folgendes[[ ! "$f" == *"$2" ]] &&
:$f
ist das gleiche wief
oben.$2
ist der übergebene.bak
Parameter. Kombiniert bedeutet dies, wenn der Dateiname nicht auf endet.bak
(weil wir nicht kopieren.bak
und erstellen möchten.bak.bak
), gehen Sie wie folgt vorcp -a "$f" "$f$2";
Kopieren Sie gmail-meta3 nach gmail-meta3.bak usw.done
: Schleife zurück und nimm den nächsten Dateinamen ingmail-meta3
* Liste.cps gmail-meta3 .bak
BeispielausgabeAnhand der Frage als Beispiel sehen Sie, wie sie in Aktion aussieht:
Hinweis: Hierbei wird das
-a
Flag mit demcp
Befehl verwendet, um Zeitstempel beizubehalten und einen besseren Überblick über Ihre Dateisicherungen zu erhalten.Beachten Sie, dass die Dateikopien genau das gleiche Datum und die gleiche Uhrzeit haben wie die Originale. Wenn der
-a
Parameter weggelassen würde, würden sie das aktuelle Datum und die aktuelle Uhrzeit erhalten und würden nicht wie eine echte Sicherung aussehen, außer dass die Dateigröße gleich wäre.quelle
ls
find
nehme ich an, dass Sie sich der Gefahren des Parsens bewusst sindls
? Aber in Ihrem Fall ist beides nicht notwendig: Tun Sie es einfachfor file in "$1"*; do copy -a "$file" "$file$2"; done
- dies ist völlig sicher und viel einfacher als jede Art von Indirektion überls
oderfind
und einewhile
Schleife.Eine andere Methode, um Ihre Anforderung zu erfüllen, besteht darin, die Dateien in ein temporäres Verzeichnis zu kopieren und sie mit dem
rename
Befehl umzubenennen.Wenn Sie es als Skript benötigen, können Sie es so verwenden
Und Sie können es so verwenden:
Dies ist ein Beispiel
quelle