web-dev-qa-db-fra.com

Modification de la chaîne de remplacement dans xargs

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.

40
betabandido

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?

15
glenn jackman

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
31
fairidox

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##*.}"' -- {}
16
Santrix

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

11
Ole Tange

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"
2
Michael Campbell

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'
1
kenorb

Ajouter à cela l'article de wikipedia est étonnamment informatif

par exemple:

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
0
Mike Graf