81 votes

Renommer récursivement des fichiers en utilisant find et sed

Je veux parcourir un tas de répertoires et renommer tous les fichiers qui se terminent par _test.rb pour les remplacer par _spec.rb. C'est quelque chose que je n'ai jamais réussi à faire avec bash, alors cette fois j'ai pensé que je ferais un effort pour y arriver. Jusqu'à présent, je n'ai pas réussi à le faire, mon meilleur effort est le suivant :

find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;

NB : il y a un echo supplémentaire après exec pour que la commande soit imprimée au lieu d'être exécutée pendant que je la teste.

Lorsque je l'exécute, la sortie pour chaque nom de fichier correspondant est la suivante :

mv original original

c'est-à-dire que la substitution par sed a été perdue. Quelle est l'astuce ?

119voto

ramtam Points 191

Pour le résoudre d'une manière plus proche du problème original, il faudrait probablement utiliser l'option xargs "args par ligne de commande" :

find . -name *_test.rb | sed -e "p;s/test/spec/" | xargs -n2 mv

Il trouve les fichiers dans le répertoire de travail actuel de manière récursive, renvoie le nom du fichier original ( p ) et ensuite un nom modifié ( s/test/spec/ ) et transmet le tout à mv par paires ( xargs -n2 ). Attention, dans ce cas, le chemin lui-même ne doit pas contenir de chaîne de caractères. test .

32voto

larsmans Points 167484

Cela se produit parce que sed reçoit la chaîne {} comme entrée, comme cela peut être vérifié avec :

find . -exec echo `echo "{}" | sed 's/./foo/g'` \;

qui imprime foofoo pour chaque fichier du répertoire, de manière récursive. La raison de ce comportement est que le pipeline est exécuté une fois, par le shell, lorsqu'il développe la commande entière.

Il n'y a aucun moyen de citer le sed de manière à ce que find l'exécutera pour chaque fichier, puisque find n'exécute pas de commandes via l'interpréteur de commandes et n'a aucune notion des pipelines ou des guillemets. Le manuel GNU findutils explique comment effectuer une tâche similaire en plaçant le pipeline dans un script séparé de l'interpréteur de commandes :

#!/bin/sh
echo "$1" | sed 's/_test.rb$/_spec.rb/'

(Il y a peut-être une manière perverse d'utiliser sh -c et une tonne de guillemets pour faire tout cela en une seule commande, mais je ne vais pas essayer).

23voto

ajreal Points 31456

Vous pourriez envisager d'autres moyens comme

for file in $(find . -name "*_test.rb")
do 
  echo mv $file `echo $file | sed s/_test.rb$/_spec.rb/`
done

17voto

csg Points 123

Je trouve celui-ci plus court

find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;

9voto

Wayne Conrad Points 31052

Vous pouvez le faire sans sed, si vous voulez :

for i in `find -name '*_test.rb'` ; do mv $i ${i%%_test.rb}_spec.rb ; done

${var%%suffix} bandes suffix de la valeur de var .

ou, de le faire en utilisant sed :

for i in `find -name '*_test.rb'` ; do mv $i `echo $i | sed 's/test/spec/'` ; done

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X