Comment copier plusieurs fichiers avec un caractère générique?

42

J'ai un dossier avec un certain nombre de fichiers dans ABC. * (Il existe environ 100 fichiers de ce type). Je souhaite les dupliquer dans de nouveaux fichiers dont le nom commence par DEF. *

Donc je veux

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

copié dans

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

Quel est le moyen le plus simple de faire cela avec une commande batch (en BASH ou similaire)? Je pense à quelque chose impliquant sed ou awk ou xargs, mais j'ai du mal à comprendre la syntaxe. Je pourrais écrire un script Python, mais je pense qu'il existe probablement une solution en ligne de commande qui n'est pas trop compliquée.

Poulpe
la source
Un vénérable mcgestionnaire de fichiers supporte très largement ce type de renommage en masse.
oakad
@ oakad tarn'est pas un poulet printanier non plus. Voir ma réponse pour un exemple.
mikeserv

Réponses:

41

Que diriez-vous de quelque chose comme ça en bash:

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

vous pouvez le tester en mettant echo devant la commande cp:

for file in ABC.*; do echo cp "$file" "${file/ABC/DEF}";done
Magnus
la source
parfait. encore plus simple que ce que je pensais.
Octopus
Ce sera suffisant dans la plupart des cas, mais vérifier avec l'écho est crucial ici. Ne faites jamais cela dans un script . Pour plus d'informations, lisez les pièges bash
pawel7318
@ pawel7318, le lien que vous avez donné, bien que d'excellents conseils, ne semble pas s'appliquer à la commande donnée ci-dessus. Les seuls cas limites que je vois avec cette commande sont les suivants: (1) Ne vérifie pas si le fichier cible existe déjà, ni si le fichier cible existe déjà sous forme de répertoire (ce qui copierait le fichier dans le répertoire en conservant son nom actuel), et (2) s'il y a tellement de fichiers que l'extension globale ne peut pas tenir dans une seule ligne de commande.
Wildcard
1
Je reçois ceci: cp /usr/local/lib/libboost* /usr/local/lib/libboost*avec le contrôle d'écho.
Tomáš Zato
8

Pour ce faire efficacement avec un grand nombre de fichiers, il est préférable d’éviter de lancer un cpprocessus différent pour chacun d’eux. Un moyen serait de copier puis de les renommer en utilisant prename( renameest lié symboliquement à cela par défaut sur les distributions basées sur Debian). En utilisant ceci et le Linux mktemp:

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

Mise à jour

En fait, paxpeut-être une meilleure façon d’aller ici:

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

Malheureusement, bien qu’ils paxsoient à la fois POSIX et Linux Standard Base, peu de distributions l’incluent actuellement par défaut.

Graeme
la source
toucher agréable en utilisant le: comme séparateur pour éviter de s'échapper.
Clayton Stanley
5

tar le fera pour vous très vite.

TESTER

J'ai d'abord créé 2 répertoires et 10 fichiers:

% 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

Puis je les ai copiés:

% 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

TRANSFORMER

Donc, GNU taracceptera un sed --transform=EXPRESSIONchangement de nom de fichier. Cela peut même ne renommer que certains fichiers. Par exemple:

% 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

C'est donc un avantage.

COURANT

Notez également qu'il ne s'agit que de deux tarprocessus - et cela ne changera pas, quel que soit le nombre de fichiers.

tar | tar

tarest aussi optimisé que vous le souhaitez. Cela n'aura jamais de compte d'arguments problématiques ni de processus enfants fugitifs. Ceci est juste A> B fait.

ARGUMENTS

J'utilise 7 arguments distincts combinés entre mes deux tarprocessus ici. Le plus important est répertorié ici en premier:

-stdout / stdin - cela indique tarqu'il transmettra en flux continu soit son entrée, soit sa sortie ou à partir de stdin/stdoutlaquelle il interprétera correctement selon qu'il s'agisse ou non de créer ou d'extraire une archive.

-ccreate - cela indique tarde construire l'archive. Le prochain argument tarattendu est ...

-ffile - nous spécifions que nous tarallons travailler avec un fileobjet plutôt qu'avec un lecteur de bande ou autre. Et le fichier avec lequel il travaillera, comme indiqué ci-dessus, est stdin/stdout- en d’autres termes, notre |pipe.

./*all $ PWD / files - pas trop à expliquer ici si ce n’est que l’argument de l’archive vient en premier, -alors ./*.

... et de l'autre côté de la |pipe...

-Cchange directory - cela indique tarqu'il faut changer le répertoire que je spécifie avant d'effectuer toute autre action, donc c'est juste cd ../test2avant l'extraction.

--transform='s/ed/EXPR/'- comme cela a déjà été mentionné, cela a été renommé. Mais la documentation indique que cela peut prendre n'importe quelle sedexpression ou //flag.

-xextraire - après avoir tarmodifié notre répertoire cible et reçu nos instructions de changement de nom, nous lui demandons de commencer à extraire tous les fichiers de son -f - |pipefichier d’archive dans son répertoire actuel . Pas de mystère.

Mikeserv
la source
1
Voici la paxsolution à cette question. Je voudrais l'ajouter à ma réponse, mais je pense que cela convient mieux ici -pax -rws '/^ABC/DEF/' ABC.* .
Graeme
@Graeme - woah - c'est bien mieux! Mec, ça bat l'enfer de renommer des perles, aussi! Vous devriez mettre ceci dans votre réponse - paxPOSIX est-il spécifié - d’où le pax.
mikeserv
paxPOSIX, mais il est malheureusement rarement installé par défaut.
Graeme
4

Si cela ne vous dérange pas d'utiliser un outil moins standard, je vous recommande d'utiliser mmv (déplacement de masse), qui a été écrit pour ce type de tâches. En utilisant mcp(copie de masse), qui fait partie de la suite, vous pouvez simplement faire

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

Ce n'est pas vraiment facile de trouver des paquets de téléchargement. Le paquet Debian actuel (qu'Arch utilise également dans AUR) à compiler à la maison est disponible ici .

Jonny
la source
installe sur ubuntu 15.10 hors de la boîte,apt-get install mmv
Thufir
2

Dans zsh, mettez les lignes suivantes dans votre .zshrc:

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

Ensuite, en ligne de commande, vous pouvez exécuter

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

ou simplement

zcp -W ABC.* DEF.*

Dans un autre shell, tant que vous avez installé zsh:

zsh -c 'autoload zmv; noglob zmv -CW ABC.* DEF.*'
Gilles, arrête de faire le mal
la source
1

Vous pourriez faire ceci:

for i in ABC.*; do cp {ABC,DEF}."${i#*.}"; done
Sara Fauzia
la source
Peut-être y a-t-il quelque chose que je ne vois pas, mais comment définir plusieurs destinations? J'utilise la boucle for pour obtenir chacun. J'ai préalablement testé le code et il a fonctionné comme prévu. J'apprécierais donc votre explication.
Sara Fauzia
C'est mon mauvais, j'ai mal interprété votre réponse. :-)
Chris Down le
Comment devrais-je lire le "${i#*.}"bit?
Dumbledad