Shell : find + rename

Bonjour

Dans un répertoire donné, j’ai une liste conséquente de sous-répertoire à un seul niveau.
Ces sous-répertoires sont nommés, tel que ‹ Chiffre-Nom ›, soit ‹ 1-repertoire-x ›, ‹ 2-rep-y ›, ‹ 100-directory-z ›, etc…

Dans plusieurs d’entres eux, j’ai plusieurs fichiers. Certains reprennent la nommenclature ‹ Chiffre-Nom.ext ›, soit ‹ 1-Nom.jpg › ou ‹ 1-Nom-min.jpg › (pour l’exemple).

1/ Parfois, j’ai commis l’erreur de nommer que selon le type suivant ‹ Nom.jpg › (ou 'Nom-min.jpg) ;
j’aurais besoin de les renommer selon le schéma ‹ Chiffre-Nom.ext › et bien sûr, réciproquement ‹ Chiffre-Nom-min.ext ›.

2/ Autre erreur de nom est le suivant : ‹ map-nom-en-minuscule.ext ›, et réciproquement ‹ map-nom-en-minuscule-min.ext › ; qu’il me faudrait transformer en ‹ Chiffre-Nom-map.ext › et ‹ Chiffre-Nom-map-min.ext › réciproquement.

Je sais que la commande suivante est efficace :

find "${dir}" -type f -name "*xyz*" -exec rename 's/PATTERN/REPLACEMENT/' {} +

Mais dans ce contexte, comment je peux la manipuler ?
Ou avez vous une autre idée ?


Voici le bout de code qui m’est utile :

    while read -r line; do
		#find "${dir_src}/${line}" -type f -name "*P1*" -exec rename 's/P1/map/' {} +
		#find "${dir_src}/${line}" -type f -name "*P2*" -exec rename 's/\-P2//' {} +
	done < "${file_src}"

J’ai été obligé de faire la centaine de modifications à la main, pour la partie 1, parce que « pressé ».

Pour la deuxième part, je réfléchis encore, d’autant que ce n’est pas urgent.
Mais je n’arrive pas à trouver comment renommer.

J’ai bien essayé avec sed mais je n’arrive à aucun résultat, tel que :
echo "$nom" | sed 's/map(.*)(-min)?(.jpg)/\1-map\2\3/' - pour l’exemple du propos, sachant qu’en lieu et place de remplacement \1, c’est une autre variable qui sera injectée…

Bref, chou blanc.

Hello,

tu aurais quelques exemples de fichiers à renommer ? « Chiffre », c’est récupéré à partir du nom du répertoire ?

Sinon, je sais pas si tu connais, mais la commande rename du paquet rename est très pratique pour ce genre de cas de figures, surtout si on se motive à écrire une expression régulière (Perl):

RENAME(1p)                                                                          User Contributed Perl Documentation                                                                          RENAME(1p)

NAME
       rename - renames multiple files

SYNOPSIS
       rename [ -h|-m|-V ] [ -v ] [ -0 ] [ -n ] [ -f ] [ -d ] [ -e|-E perlexpr]*|perlexpr [ files ]

DESCRIPTION
       "rename" renames the filenames supplied according to the rule specified as the first argument.  The perlexpr argument is a Perl expression which is expected to modify the $_ string in Perl for at
       least some of the filenames specified.  If a given filename is not modified by the expression, it will not be renamed.  If no filenames are given on the command line, filenames will be read via
       standard input.

   Examples (Larry Wall,  1992)
       For example, to rename all files matching "*.bak" to strip the extension, you might say

               rename 's/\.bak$//' *.bak

       To translate uppercase names to lower, you'd use

               rename 'y/A-Z/a-z/' *

   More examples (2020)
       You can also use rename to move files between directories, possibly at the same time as making other changes (but see --filename)

               rename 'y/A-Z/a-z/;s/^/my_new_dir\//' *.*

       You can also write the statements separately (see -e/-E)

               rename -E 'y/A-Z/a-z/' -E 's/^/my_new_dir\//' *.*

/repertoire-parent/1-Rep-X/maps-rep-x.jpg
/repertoire-parent/1-Rep-X/maps-rep-x-min.jpg
/repertoire-parent/2-Rep-Y/maps-rep-y.jpg
/repertoire-parent/2-Rep-Y/maps-rep-y-min.jpg
/repertoire-parent/3-Rep-Z/maps-rep-z.jpg
/repertoire-parent/3-Rep-A/maps-rep-z-min.jpg
(…)
/repertoire-parent/100-Rep-Abc/map-rep-abc.jpg
/repertoire-parent/100-Rep-Abc/map-rep-abc-min.jpg

Mais ce n’est pas le fait de récupérer les noms des répertoires qui me posent problème.
Je lance un find à la racine de repertoire-parent et injecte cela dans un fichier, tel que :
find "${dir_src}" -maxdepth 1 -type d -printf "%f\n" | sort -n > "${file_src}"

Relis mon premier post, stp :wink:

Le bout de code que je montre m’a été utile pour renommer tous les fichiers comprenant la chaîne de caractère ‹ P1 ›, et ceux comprenant ‹ -P2 ›…
Je les ais mis en commentaire dans le code que j’ai donné, car c’est traité !

Arf oui, désolé.

Comme ça (avec le chiffre du répertoire = 1) ?

sputnik@debian:~$ rename -nono 's/(maps)-(nom)-(.*).jpg/1-\u$2-\u$1-$3.jpg/' *
rename(maps-nom-y.jpg, 1-Nom-Maps-y.jpg)
rename(maps-nom-y-min.jpg, 1-Nom-Maps-y-min.jpg)

Alors non, ça serait plutôt :

while read -r line; do
    find "${dir_src}/${line}" -type f -name "map*.jpg" -exec  rename -nono 's/(map)-(.*)(-min).jpg/\u\Q"{line}"-\u$2$3.jpg/' {} +
done < "${file_src}"

Pour restituer :

rename(maps-nom-y.jpg, 1-Nom-Y-map.jpg)
rename(maps-nom-y-min.jpg, 1-Nom-Y-map-min.jpg)

Test dans le premier répertoire :

$ rename 's/^map(.*)(-min).jpg$/\Q"{line}"-map$2.jpg/' *
'\"\{line\}\"\-map\-min\.jpg'

ooops, c’est pas le résultat attendu :stuck_out_tongue:

la commande a géré la deuxième forme de nom avec le suffixe ‹ -min ›, mais pas la première sans le suffixe.

et d’ailleurs, pourquoi sont rajoutés les ‹ \ › ?


OK, j’avance :

$ rename "s/^map(.*)(-min).jpg$/${line}-map$2.jpg/" *

gére bien la deuxième forme de fichier ‹ maps-nom-y-min.jpg › et me le transforme bien en ‹ 1-Nom-Y-map.jpg › mais sans le suffixe ‹ -min › qui est pourtant nécessaire - et fait l’impasse sur la première forme ‹ maps-nom-y.jpg › -

quant à :

rename "s/^map(.*)(-min)?.jpg$/${line}-map$2.jpg/" *

gére la première forme mais pas la deuxième en provoquant l’erreur suivante : file not renamed: xyz already exists


Bon, j’aimerais comprendre.

⇒ si j’écris : rename "s/^map(.*)(-min.jpg)$/${line}-map\2/" * cela fonctionne avec un message d’avertissement : \2 better written as $2 at (eval 25) line 2.

⇒ par contre, si j’écris rename "s/^map(.*)(.jpg)$/${line}-map$2/" * - cela fonctionne en partie, puisqu’il ne « recopie » pas l’extension-, tout en considérant son résultat comme bon.

???

En fait si tu entoures l’expression par des double-quotes (afin que bash puisse interpréter ${line}), ton shell va tenter d’interpréter $2. Soit tu utilises la syntaxe \2 pour appeler le deuxième groupe de capture (.jpeg), et là rename affiche l’avertissement, soit tu échappes avec \$2 dans ton exepression, et là il est content :smiley:

OK, je comprends mieux ce qu’il me dit.

Bon, ce n’est pas encore ça, quoiqu’il en soit :

$ rename "s/^map(.*)(-min)?.jpg$/${line}-map\$2.jpg/" *
Use of uninitialized value $2 in concatenation (.) or string at (eval 25) line 2.
Use of uninitialized value $2 in concatenation (.) or string at (eval 25) line 2.
maps-rep-y-min.jpg not renamed: 2-Rep-Y-map.jpg already exists

Donc, il n’arrive pas à pas à transformer l’écriture type ‹ maps-nom-y-min.jpg › car il cherche à la transformer en ‹ Chiffre-Nom-Y-map.jpg › comme il le fait avec l’écriture type ‹ maps-nom-y.jpg ›, alors que le but est de le transformer en ‹ Chiffre-Nom-Y-map-min.jpg ›

Je tourne autour sans réussir pleinement.

Qu’est-ce que je loupe ? ou ne comprend pas !?


Pour l’instant, pour transformer :

  • ‹ maps-nom-y-min.jpg › en ‹ Chiffre-Nom-Y-map-min.jpg ›, je dois utiliser :
    rename "s/^map(.*)(-min).jpg$/${line}-map\$2.jpg/" *
  • ‹ maps-nom-y.jpg › en ‹ Chiffre-Nom-Y-map.jpg ›, il me faut utiliser :
    rename "s/^map(.*)(-min)?.jpg$/${line}-map\$2.jpg/" *

au moins, en deux passes, j’arrive à un résultat ! :wink:

Le problème est que .* est trop large, du coup les éventuels caractères -min se retrouvent dans le premier groupe de capture.

J’ai testé ça, en capturant des caractères alphabétiques minuscules, plutôt que . (on peut utiliser [a-zA-Z] si jamais les noms de fichiers originaux comportent aussi des majuscules, voire carrément [a-zA-Z0-9] pour des caractères alphanumériques majuscules/minuscules)

sputnik@debian:~/tests/1-rep-x$ line=1
sputnik@debian:~/tests/1-rep-x$ ll
total 0
-rw-r--r-- 1 sputnik sputnik 0 May 31 13:02 maps-nom-y.jpg
-rw-r--r-- 1 sputnik sputnik 0 May 31 13:02 maps-nom-y-min.jpg
sputnik@debian:~/tests/1-rep-x$ rename -nono "s/^(map)s?-([a-z]*)-([a-z]*)(.*)$/${line}-\u\$2-\u\$3-\$1\$4/" *
rename(maps-nom-y.jpg, 1-Nom-Y-map.jpg)
rename(maps-nom-y-min.jpg, 1-Nom-Y-map-min.jpg)
  • On met map dans un premier groupe de capture
  • j’ai inclus la possibilité que le nom de fichier original commence par ‹ maps › au lieu de ‹ map ›
  • ensuite, chaque suite de caractères alphabétiques minuscules séparée par un tiret se voit affecté dans un groupe de capture (le deuxième et le troisième)
  • puis tout le reste (donc soit .jpg soit -min.jpg selon le fichier) se voit affecté dans le quatrième groupe de capture
1 J'aime

Eh, beh, tu me permets de bien avancer.

Il semble en effet que :
$ rename -v "s/^(map)s?-([a-zA-Z]*)-([a-zA-Z]*)(.*)$/${line}-\$1\$4/" *
soit la réponse !

:smiley:

Merci de ton sacré coup de main.