Lorsque j'utilise xargs
, il n'est parfois pas nécessaire d'utiliser explicitement la chaîne de remplacement:
find . -name "*.txt" | xargs rm -rf
Dans d'autres cas, je souhaite spécifier la chaîne de remplacement afin d'effectuer des opérations telles que:
find . -name "*.txt" | xargs -I '{}' mv '{}' /foo/'{}'.bar
La commande précédente déplacerait tous les fichiers texte du répertoire en cours dans /foo
et ajouterait l'extension bar
à tous les fichiers.
Si, au lieu d'ajouter du texte à la chaîne de remplacement, je voulais modifier cette chaîne de manière à pouvoir insérer du texte entre le nom et l'extension des fichiers, comment pourrais-je le faire? Par exemple, supposons que je souhaite faire la même chose que dans l'exemple précédent, mais les fichiers doivent être renommés/déplacés de <name>.txt
à /foo/<name>.bar.txt
(au lieu de /foo/<name>.txt.bar
).
UPDATE: Je parviens à trouver une solution:
find . -name "*.txt" | xargs -I{} \
sh -c 'base=$(basename $1) ; name=${base%.*} ; ext=${base##*.} ; \
mv "$1" "foo/${name}.bar.${ext}"' -- {}
Mais je me demande s’il existe une solution plus courte/meilleure.
Dans ce cas, une boucle while
serait plus lisible:
find . -name "*.txt" | while IFS= read -r pathname; do
base=$(basename "$pathname"); name=${base%.*}; ext=${base##*.}
mv "$pathname" "foo/${name}.bar.${ext}"
done
Notez que vous pouvez trouver des fichiers portant le même nom dans différents sous-répertoires. Acceptez-vous que les doublons soient écrasés par mv
?
La commande suivante construit la commande move avec xargs, remplace la deuxième occurrence de '.' avec '.bar.', puis exécute les commandes avec bash, travaillant sur mac OSX.
ls *.txt | xargs -I {} echo mv {} foo/{} | sed 's/\./.bar./2' | bash
Il est possible de faire cela en un seul passage (testé sous GNU) en évitant l’utilisation des assignations de variables temporaires.
find . -name "*.txt" | xargs -I{} sh -c 'mv "$1" "foo/$(basename ${1%.*}).new.${1##*.}"' -- {}
Si vous avez GNU Parallel http://www.gnu.org/software/parallel/ installé, vous pouvez le faire:
find . -name "*.txt" | parallel 'ext="{/}" ; mv -- {} foo/{/.}.bar.${ext##*.}'
Regardez les vidéos d'introduction de GNU Parallel pour en savoir plus: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1
Si vous êtes autorisé à utiliser autre chose que bash/sh, ET ceci est juste pour un "mv" fantaisie ... vous pouvez essayer le vénérable script "rename.pl" Je l'utilise sous Linux et cygwin sous Windows tout le temps.
http://people.sc.fsu.edu/~jburkardt/pl_src/rename/rename.html
rename.pl 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' list_of_files_or_glob
Vous pouvez également utiliser un paramètre "-p" pour renommer.pl afin qu’il vous dise ce qu’il aurait FAIT, sans le faire réellement.
Je viens d'essayer ce qui suit dans mon c:/bin (environnement cygwin/windows). J'ai utilisé le "-p" pour qu'il crache ce qu'il aurait fait. Cet exemple divise simplement la base et l'extension et ajoute une chaîne entre eux.
Perl c:/bin/rename.pl -p 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' *.bat
rename "here.bat" => "here-new_stuff_here.bat"
rename "htmldecode.bat" => "htmldecode-new_stuff_here.bat"
rename "htmlencode.bat" => "htmlencode-new_stuff_here.bat"
rename "sdiff.bat" => "sdiff-new_stuff_here.bat"
rename "widvars.bat" => "widvars-new_stuff_here.bat"
les fichiers doivent être renommés/déplacés de
<name>.txt
à/foo/<name>.bar.txt
Vous pouvez utiliser l'utilitaire rename
, par exemple:
rename s/\.txt$/\.txt\.bar/g *.txt
Astuce: La syntaxe de substitution est similaire à sed
ou vim
.
Déplacez ensuite les fichiers dans un répertoire cible en utilisant mv
:
mkdir /some/path
mv *.bar /some/path
Pour renommer les fichiers en sous-répertoires en fonction d'une partie de leur nom, vérifiez les points suivants:
-p
/--mkpath
/--make-dirs
Créez des répertoires non existants dans le chemin cible.
Essai:
$ touch {1..5}.txt
$ rename --dry-run "s/.txt$/.txt.bar/g" *.txt
'1.txt' would be renamed to '1.txt.bar'
'2.txt' would be renamed to '2.txt.bar'
'3.txt' would be renamed to '3.txt.bar'
'4.txt' would be renamed to '4.txt.bar'
'5.txt' would be renamed to '5.txt.bar'
Ajouter à cela l'article de wikipedia est étonnamment informatif
Tour de coquille
Un autre moyen d'obtenir un effet similaire consiste à utiliser un shell en tant que commande lancée et à gérer sa complexité, par exemple:
$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 bash -c 'for filename; do cp -a "$filename" ~/backups; done' bash