web-dev-qa-db-fra.com

Renommer plusieurs fichiers sous Unix

Il existe plusieurs fichiers dans un répertoire qui commencent par le préfixe fgh, par exemple:

fghfilea
fghfileb
fghfilec

Je veux tous les renommer pour commencer par le préfixe jkl. Existe-t-il une seule commande pour faire cela au lieu de renommer chaque fichier individuellement?

202
john

Il existe plusieurs manières, mais utiliser rename sera probablement le plus simple.

Utilisation d'une version de rename :

rename 's/^fgh/jkl/' fgh*

Utilisation d'une autre version de rename (identique à réponse de Judy2K ):

rename fgh jkl fgh*

Vous devriez consulter la page de manuel de votre plate-forme pour savoir lequel des cas ci-dessus s'applique.

248
Stephan202

Voici comment sed et mv peuvent être utilisés ensemble pour renommer:

for f in fgh*; do mv "$f" $(echo "$f" | sed 's/^fgh/jkl/g'); done

Comme indiqué ci-dessous, si les noms de fichiers contiennent des espaces, les guillemets peuvent avoir besoin de entourer la sous-fonction qui renvoie le nom pour déplacer les fichiers:

for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done
102
nik

renommer peut ne pas être dans tous les systèmes. Donc, si vous ne l'avez pas, utilisez Shell Cet exemple dans bash Shell

for f in fgh*; do mv "$f" "${f/fgh/xxx}";done
68
ghostdog74

Utiliser mmv :

mmv "fgh*" "jkl#1"
35
Lee Netherton

Il y a plusieurs façons de le faire (toutes ne fonctionneront pas sur tous les systèmes Unixy): 

  • ls | cut -c4- | xargs -I§ mv fgh§ jkl§

    Le § peut être remplacé par tout ce qui vous convient. Vous pouvez le faire aussi avec find -exec mais cela se comporte légèrement différemment sur de nombreux systèmes, aussi j’évite généralement cela

  • for f in fgh*; do mv "$f" "${f/fgh/jkl}";done 

    Brut mais efficace comme on dit 

  • rename 's/^fgh/jkl/' fgh*

    Vraiment joli, mais renommer n’est pas présent sur BSD, qui est le système unix le plus répandu à ce jour.

  • rename fgh jkl fgh*

  • ls | Perl -ne 'chomp; next unless -e; $o = $_; s/fgh/jkl/; next if -e; rename $o, $_';

    Si vous insistez pour utiliser Perl, mais qu'il n'y a pas de renommage sur votre système, vous pouvez utiliser ce monstre.

Certaines sont un peu compliquées et la liste est loin d’être exhaustive, mais vous trouverez ici ce que vous voulez pour à peu près tous les systèmes Unix.

18
iwein
rename fgh jkl fgh*
12
Judy2K

Utiliser find, xargs et sed:

find . -name "fgh*" -type f -print0 | xargs -0 -I {} sh -c 'mv "{}" "$(dirname "{}")/`echo $(basename "{}") | sed 's/^fgh/jkl/g'`"'

C'est plus complexe que la solution de @ nik mais elle permet de renommer les fichiers de manière récursive. Par exemple, la structure,

.
├── fghdir
│   ├── fdhfilea
│   └── fghfilea
├── fghfile\ e
├── fghfilea
├── fghfileb
├── fghfilec
└── other
    ├── fghfile\ e
    ├── fghfilea
    ├── fghfileb
    └── fghfilec

serait transformé à cela,

.
├── fghdir
│   ├── fdhfilea
│   └── jklfilea
├── jklfile\ e
├── jklfilea
├── jklfileb
├── jklfilec
└── other
    ├── jklfile\ e
    ├── jklfilea
    ├── jklfileb
    └── jklfilec

La clé pour que cela fonctionne avec xargs est de invoquer le shell à partir de xargs .

8
luissquall

Pour installer le script Perl rename

Sudo cpan install File::Rename

Il y a deux renommés comme mentionné dans les commentaires de la réponse de Stephan202 . Les distributions basées sur Debian ont le renommer Perl . Les distributions RedHat/RPM ont le nom C renommé .
OS X n’en a pas installé par défaut (au moins en 10.8), pas plus que Windows/Cygwin. 

3
Sam Inverso

Voici un moyen de le faire en utilisant Groovy en ligne de commande:

groovy -e 'new File(".").eachFileMatch(~/fgh.*/) {it.renameTo(it.name.replaceFirst("fgh", "jkl"))}'
2
jesseplymale

Sous Solaris, vous pouvez essayer:

for file in `find ./ -name "*TextForRename*"`; do 
    mv -f "$file" "${file/TextForRename/NewText}"
done
2
Andrei Aleksandrov
#!/bin/sh

#replace all files ended witn .f77 to .f90 in a directory

for filename in *.f77
do 
    #echo $filename
    #b= echo $filename | cut -d. -f1
    #echo $b    
    mv "${filename}" "${filename%.f77}.f90"    
done
2
Suragini

Je recommanderais d'utiliser mon propre script, qui résout ce problème. Il propose également des options pour modifier l’encodage des noms de fichiers et convertir les combinaisons de signes diacritiques en caractères précomposés, un problème que j’ai toujours lorsque je copie des fichiers de mon Mac.

#!/usr/bin/Perl

# Copyright (c) 2014 André von Kugland

# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

$help_msg =
"rename.pl, a script to rename files in batches, using Perl
           expressions to transform their names.
Usage:
    rename.pl [options] FILE1 [FILE2 ...]
Where options can be:
    -v                      Verbose.
    -vv                     Very verbose.
    --apply                 Really apply modifications.
    -e PERLCODE             Execute PERLCODE. (e.g. 's/a/b/g')
    --from-charset=CS       Source charset. (e.g. \"iso-8859-1\")
    --to-charset=CS         Destination charset. (e.g. \"utf-8\")
    --unicode-normalize=NF  Unicode normalization form. (e.g. \"KD\")
    --basename              Modifies only the last element of the path.
";

use Encode;
use Getopt::Long;
use Unicode::Normalize 'normalize';
use File::Basename;
use I18N::Langinfo qw(langinfo CODESET);

Getopt::Long::Configure ("bundling");

# ----------------------------------------------------------------------------------------------- #
#                                           Our variables.                                        #
# ----------------------------------------------------------------------------------------------- #

my $apply = 0;
my $verbose = 0;
my $help = 0;
my $debug = 0;
my $basename = 0;
my $unicode_normalize = "";
my @scripts;
my $from_charset = "";
my $to_charset = "";
my $codeset = "";

# ----------------------------------------------------------------------------------------------- #
#                                        Get cmdline options.                                     #
# ----------------------------------------------------------------------------------------------- #

$result = GetOptions ("apply" => \$apply,
                      "verbose|v+" => \$verbose,
                      "execute|e=s" => \@scripts,
                      "from-charset=s" => \$from_charset,
                      "to-charset=s" => \$to_charset,
                      "unicode-normalize=s" => \$unicode_normalize,
                      "basename" => \$basename,
                      "help|h|?" => \$help,
                      "debug" => \$debug);

# If not going to apply, then be verbose.
if (!$apply && $verbose == 0) {
  $verbose = 1;
}

if ((($#scripts == -1)
  && (($from_charset eq "") || ($to_charset eq ""))
  && $unicode_normalize eq "")
  || ($#ARGV == -1) || ($help)) {
  print $help_msg;
  exit(0);
}

if (($to_charset ne "" && $from_charset eq "")
  ||($from_charset eq "" && $to_charset ne "")
  ||($to_charset eq "" && $from_charset eq "" && $unicode_normalize ne "")) {
  $codeset = langinfo(CODESET);
  $to_charset = $codeset if $from_charset ne "" && $to_charset eq "";
  $from_charset = $codeset if $from_charset eq "" && $to_charset ne "";
}

# ----------------------------------------------------------------------------------------------- #
#         Composes the filter function using the @scripts array and possibly other options.       #
# ----------------------------------------------------------------------------------------------- #

$f = "sub filterfunc() {\n    my \$s = shift;\n";
$f .= "    my \$d = dirname(\$s);\n    my \$s = basename(\$s);\n" if ($basename != 0);
$f .= "    for (\$s) {\n";
$f .= "        $_;\n" foreach (@scripts);   # Get scripts from '-e' opt. #
# Handle charset translation and normalization.
if (($from_charset ne "") && ($to_charset ne "")) {
  if ($unicode_normalize eq "") {
    $f .= "        \$_ = encode(\"$to_charset\", decode(\"$from_charset\", \$_));\n";
  } else {
    $f .= "        \$_ = encode(\"$to_charset\", normalize(\"$unicode_normalize\", decode(\"$from_charset\", \$_)));\n"
  }
} elsif (($from_charset ne "") || ($to_charset ne "")) {
    die "You can't use `from-charset' nor `to-charset' alone";
} elsif ($unicode_normalize ne "") {
  $f .= "        \$_ = encode(\"$codeset\", normalize(\"$unicode_normalize\", decode(\"$codeset\", \$_)));\n"
}
$f .= "    }\n";
$f .= "    \$s = \$d . '/' . \$s;\n" if ($basename != 0);
$f .= "    return \$s;\n}\n";
print "Generated function:\n\n$f" if ($debug);

# ----------------------------------------------------------------------------------------------- #
#                 Evaluates the filter function body, so to define it in our scope.               #
# ----------------------------------------------------------------------------------------------- #

eval $f;

# ----------------------------------------------------------------------------------------------- #
#                  Main loop, which passes names through filters and renames files.               #
# ----------------------------------------------------------------------------------------------- #

foreach (@ARGV) {
  $old_name = $_;
  $new_name = filterfunc($_);

  if ($old_name ne $new_name) {
    if (!$apply or (rename $old_name, $new_name)) {
      print "`$old_name' => `$new_name'\n" if ($verbose);
    } else {
      print "Cannot rename `$old_name' to `$new_name'.\n";
    }
  } else {
    print "`$old_name' unchanged.\n" if ($verbose > 1);
  }
}
1

C'était beaucoup plus facile (sur mon Mac) de faire cela en Ruby. Voici 2 exemples:

# for your fgh example. renames all files from "fgh..." to "jkl..."
files = Dir['fgh*']

files.each do |f|
  f2 = f.gsub('fgh', 'jkl')
  system("mv #{f} #{f2}")
end

# renames all files in directory from "021roman.rb" to "021_roman.rb"
files = Dir['*rb'].select {|f| f =~ /^[0-9]{3}[a-zA-Z]+/}

files.each do |f|
  f1 = f.clone
  f2 = f.insert(3, '_')
  system("mv #{f1} #{f2}")
end
1
Raymond Gan

Ma version de renommer des fichiers de masse:

for i in *; do
    echo "mv $i $i"
done |
sed -e "s#from_pattern#to_pattern#g” > result1.sh
sh result1.sh
1
mdev

Utiliser renamer :

$ renamer --find /^fgh/ --replace jkl * --dry-run

Supprimez l'indicateur --dry-run une fois que vous êtes satisfait du résultat.

1
Lloyd

Utilisation de StringSolver tools (windows & Linux bash) qui traitent par exemples:

filter fghfilea ok fghreport ok notfghfile notok; mv --all --filter fghfilea jklfilea

Il commence par calcule un filtre basé sur des exemples, où l'entrée correspond aux noms de fichier et à la sortie (ok et notok, chaînes arbitraires). Si filter avait l'option --auto ou était appelé seul après cette commande, il créerait respectivement un dossier ok et un dossier notok et des fichiers Push.

Ensuite, en utilisant le filtre, la commande mv est un déplacement semi-automatique qui devient automatique avec le modificateur --auto. En utilisant le filtre précédent grâce à --filter, il trouve un mappage de fghfilea à jklfilea et l’applique ensuite à tous les fichiers filtrés.


_ {Autres solutions monolignes} _

Il existe d’autres moyens équivalents de faire de même (chaque ligne est l’équivalent), ce qui vous permet de choisir votre méthode préférée.

filter fghfilea ok fghreport ok notfghfile notok; mv --filter fghfilea jklfilea; mv
filter fghfilea ok fghreport ok notfghfile notok; auto --all --filter fghfilea "mv fghfilea jklfilea"
# Even better, automatically infers the file name
filter fghfilea ok fghreport ok notfghfile notok; auto --all --filter "mv fghfilea jklfilea"

Solution en plusieurs étapes

Pour rechercher avec soin si les commandes fonctionnent bien, vous pouvez taper les éléments suivants:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

et lorsque vous êtes sûr que le filtre est bon, effectuez le premier mouvement:

mv fghfilea jklfilea

Si vous voulez tester et utiliser le filtre précédent, tapez:

mv --test --filter

Si la transformation ne correspond pas à ce que vous souhaitiez (par exemple, même si mv --explain vous voyez que quelque chose ne va pas), vous pouvez taper mv --clear pour redémarrer le déplacement des fichiers ou ajouter d'autres exemples mv input1 input2 où input1 et input2 sont d'autres exemples.

Lorsque vous êtes confiant, tapez simplement

mv --filter

et voilà! Tout le renommage est fait en utilisant le filtre.

DISCLAIMER: Je suis co-auteur de ce travail à des fins académiques. Il pourrait également y avoir bientôt une fonctionnalité produisant bash.

1
Mikaël Mayer

J'ai écrit ce script pour rechercher tous les fichiers .mkv en renommant récursivement les fichiers trouvés en .avi. Vous pouvez le personnaliser selon vos besoins. J'ai ajouté d'autres éléments tels que l'obtention du répertoire de fichier, de l'extension, du nom de fichier à partir d'un chemin de fichier, au cas où vous deviez faire référence à quelque chose dans le futur. 

find . -type f -name "*.mkv" | while read fp; do 
fd=$(dirname "${fp}");
fn=$(basename "${fp}");
ext="${fn##*.}";
f="${fn%.*}";
new_fp="${fd}/${f}.avi"
mv -v "$fp" "$new_fp" 
done;
0
David Okwii

Cela a fonctionné pour moi en utilisant regexp:

Je voulais que les fichiers soient renommés comme ceci:

file0001.txt -> 1.txt
ofile0002.txt -> 2.txt 
f_i_l_e0003.txt -> 3.txt

en utilisant l'expression rationnelle [a-z | _] + 0 * ([0-9] +.) où ([0-9] +. ) est une sous-chaîne de groupe à utiliser dans la commande de renommage

ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)/, arr) { print   arr[0]  " "  arr[1] }'|xargs  -l mv

Produit:

mv file0001.txt 1.txt
mv ofile0002.txt 2.txt
mv f_i_l_e0003.txt 3.txt

Un autre exemple:

file001abc.txt -> abc1.txt
ofile0002abcd.txt -> abcd2.txt 

ls -1 | awk 'match($0, /[a-z|\_]+0*([0-9]+.*)([a-z]+)/, arr) { print   arr[0]  " "  arr[2] arr[1] }'|xargs  -l mv

Produit:

  mv file001abc.txt abc1.txt
  mv ofile0002abcd.txt abcd2.txt 

Attention, soyez prudent.

0
Steven Lizarazo

Une autre extension possible de paramètre :

for f in fgh*; do mv -- "$f" "jkl${f:3}"; done
0
PesaThe

Un script générique pour exécuter une expression sed sur une liste de fichiers (combine la solution sed avec la solution rename ):

#!/bin/sh

e=$1
shift

for f in $*; do
    fNew=$(echo "$f" | sed "$e")
    mv "$f" "$fNew";
done

Appelez en transmettant au script une expression sed, puis toute liste de fichiers, comme une version de rename :

script.sh 's/^fgh/jkl/' fgh*
0
palswim

Vous pouvez également utiliser le script ci-dessous. il est très facile de courir sur un terminal ...

// Renomme plusieurs fichiers à la fois

for file in  FILE_NAME*
do
    mv -i "${file}" "${file/FILE_NAME/RENAMED_FILE_NAME}"
done

Exemple:-

for file in  hello*
do
    mv -i "${file}" "${file/hello/JAISHREE}"
done
0
Sanjay Singh