Comment renommer plusieurs fichiers en utilisant find


37

Je souhaite renommer plusieurs fichiers (file1 ... filen en file1_renamed ... filen_renamed) à l'aide de la findcommande suivante:

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'

Mais obtenir cette erreur:

mv: cannot stat filename=./file1’: No such file or directory

Cela ne fonctionne pas car le nom de fichier n'est pas interprété comme une variable shell.

Réponses:


46

Ce qui suit est une solution directe de votre approche:

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;

Cependant, cela coûte très cher si vous avez beaucoup de fichiers correspondants, car vous démarrez un nouveau shell (qui exécute a mv) pour chaque correspondance. Et si vous avez des personnages amusants dans n'importe quel nom de fichier, cela va exploser. Une approche plus efficace et sécurisée est la suivante:

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed

Il a également l'avantage de travailler avec des fichiers étrangement nommés. Si le findsupporte, cela peut être réduit à

find . -type f -name 'file*' -exec mv {} {}_renamed \;

La xargsversion est utile lorsque vous n'utilisez pas {}, comme dans

find .... -print0 | xargs --null rm

Ici, rmon appelle une fois (ou plusieurs fois avec plusieurs fichiers), mais pas pour tous les fichiers.

J'ai enlevé la basenamequestion en vous, parce que c'est probablement faux: vous iriez foo/bar/file8à file8_renamed, pas foo/bar/file8_renamed.

Modifications (comme suggéré dans les commentaires):

  • Ajouté raccourci findsansxargs
  • Ajout de la vignette de sécurité

Dans le cas où la xest inutile: les find . -type f -name 'file*' -exec mv {} "{}_renamed" \; xargsversions ont la même efficacité que le premier exemple /
Costas

Le xn'est là que pour fixer directement l'approche du demandeur.
Thomas Erker

2
(1) Il est très dangereux d’utiliser {}directement dans une sh -c "…"commande shell ( ) - vous devriez toujours le transmettre en argument. (2) Toutes les versions de findsupportent la {}_renamedconstruction. (3) Je ne comprends pas votre affirmation xargsutile pour supprimer des fichiers (par opposition à les renommer).
G-Man dit 'Réintégrez Monica' le

@ G-Man: La différence avec xargsn'est pas mvvs rm, mais utilisation de {}vs sans. La première est semblable à mv file1 file1_renamed; mv file2 file2_renamedla dernière rm file1 file2.
Thomas Erker

1
et si vous vouliez ensuite changer les fichiers _renamed en leur nom d'origine, comment feriez-vous cela?
mcmillab

17

Après avoir essayé la première réponse et un peu joué avec elle, j'ai trouvé que cela pouvait être fait légèrement plus court et moins complexe en utilisant -execdir:

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'

On dirait qu'il devrait aussi faire exactement ce dont vous avez besoin.


2
Avec des findimplémentations qui prennent en charge -execdiret {}non dans leur ensemble, c'est aussi le plus sûr. Vous voudrez peut-être ajouter un -ià mvsi (et -Tsi votre mvsoutien le soutient)
Stéphane Chazelas

1
@ StéphaneChazelas encore mieux, au lieu de compter sur mvune invite, ou en plus de celle-ci, vous pouvez (sans aucun doute, selon que votre implémentation la findprenne en charge) également utiliser -okdirce qui va générer la commande à exécuter avant de l'exécuter.
Matijs

Il vaut la peine de mentionner que -depthc’est aussi une bonne idée si vous allez aussi toucher les noms de répertoire.
Ciro Santilli 改造 中心 法轮功 六四 事件

Argh, qui vient de découvrir qu’il ya -execdirun inconvénient très gênant, findrefuse de faire quoi que ce soit si elle PATHcontient des chemins relatifs ... askubuntu.com/questions/621132/… find: The relative path XXX is included in the PATH environment
Ciro Santilli 新疆

6

Une autre approche consiste à utiliser une while readboucle sur la findsortie. Cela permet d'accéder à chaque nom de fichier sous forme de variable pouvant être manipulée sans avoir à s'inquiéter des coûts supplémentaires / des problèmes de sécurité potentiels liés à la génération d'un sh -cprocessus distinct à l' aide findde l' -execoption de.

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

Et si le shell utilisé prend en charge l’ -doption de spécifier un readdélimiteur, vous pouvez prendre en charge les fichiers portant un nom étrange (par exemple, avec une nouvelle ligne) en utilisant les éléments suivants:

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

3

Je veux développer la première réponse et noter que cela ne fonctionnera pas pour ajouter au nom de fichier car le ./préfixe de chemin est présent dans l'argument filename.

En modifiant la réponse de Thomas Erker, je trouve celle-ci plus générique.

find . -name PATTERN -printf "%f\0" | xargs --null -I{} mv {} "prefix {} suffix"

Options de xargs:

--nullIndique que chaque argument transmis stdinse termine par un caractère nul (\0 ). De cette façon, le nom de fichier peut contenir des espaces, sinon chaque mot sera traité comme un paramètre différent pour la mvcommande.

-I replace-strChaque occurrence de replace-strsera remplacée par l'argument lu dans stdin. Donc, vous pouvez le changer pour une autre chaîne si vous en avez besoin.


Pourquoi le pringf "%f\0"lieu d'un print0?
slm

1
@sim, car -print0produira des ./préfixes si PATTERNcontient des métacaractères shell qui gênent lors du changement de nom en quelque chose qui préfixe les noms originaux. (par exemple , changement de nom 0 - foo.txtà 00 - foo.txt, 1 - bar.txtà , 01 - bar.txtetc.)
das g

2

Je suis en mesure de faire quelque chose de similaire avec le for, findet mv.

for i in $(find . -name 'config.yml'); do mv $i $i.bak; done

Ceci trouve tous les config.ymlfichiers et les renommeconfig.yml.bak


cela a l'avantage de pouvoir utiliser une expansion variable, par exemple. $ {i: r}. Bonne réponse.
Salami

Oui, vous pouvez insérer n'importe quelle expression dans le foret effectuer une opération en bloc.
Varun Risbud
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.