Rinomina più file in base al modello in Unix


248

Esistono più file in una directory che iniziano con il prefisso fgh, ad esempio:

fghfilea
fghfileb
fghfilec

Voglio rinominarli tutti per iniziare con il prefisso jkl. Esiste un singolo comando per farlo invece di rinominare ogni file singolarmente?



Risposte:


289

Esistono diversi modi, ma l'utilizzo renamesarà probabilmente il più semplice.

Utilizzando una versione di rename:

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

Utilizzando un'altra versione di rename(uguale alla risposta di Judy2K ):

rename fgh jkl fgh*

Dovresti controllare la pagina man della tua piattaforma per vedere quale di questi si applica.


7
+1 Non sapevo nemmeno di rinominare ... Ora posso smettere di usare un ciclo for con mv e sed ... Grazie!
balpha,

2
Stai collegando a un altro, renamequindi stai mostrando la sintassi per unixhelp.ed.ac.uk/CGI/man-cgi?rename è l'altro
Hasturkun,

7
Non presente su tutti i sistemi * nix. Non su Max OS X per uno, e nessun pacchetto in fink per ottenerlo. Non ho visto MacPorts.
dmckee --- ex-moderatore gattino

6
AFAICT, renamesembra essere uno script o un'utilità specifica per Linux. Se ti interessa la portabilità, continua a utilizzare sede cicli o uno script Perl in linea.
D.Shawley,

33
brew install renamesu OS X :)
sam,

117

Ecco come sede mvpuò essere usato insieme per rinominare:

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

Come da commento di seguito, se i nomi dei file contengono spazi, potrebbe essere necessario che le virgolette racchiudano la funzione secondaria che restituisce il nome per spostare i file in:

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

1
Molto vicino. Nota che vuoi solo abbinare la prima occorrenza di fgh: 's / ^ fgh / jkl / g' (Il cursore fa la differenza).
Stephan202,

2
Solo per motivi di precisione ... Intendi "fgh all'inizio del nome", non "la prima occorrenza di fgh". / ^ fgh / corrisponderà a "fghi", ma non a "efgh".
Dave Sherohman,

@Stephan, è stato un errore di battitura da parte mia (risolto).
nik,

5
Se non hai accesso a "rinomina", funziona alla grande. Il codice potrebbe richiedere delle virgolette se i nomi dei file includono spazi. for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done
Dave Nelson,

1
@ Nik senza virgolette rinominare questo elenco di file getterebbe un errore: touch fghfilea fghfileb fghfilec fghfile\ d. Consiglio di prendere in considerazione le osservazioni di @DaveNelson.
Luissquall,

75

la ridenominazione potrebbe non essere presente in tutti i sistemi. quindi se non ce l'hai, usa la shell questo esempio nella shell bash

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

1
In questa soluzione, il sedcomando non è richiesto. Questo è più semplice della risposta di @ nik.
Halil,

xxx sta per la sostituzione? nel caso del poster originale, sarebbe stato "jkl"
Awinad,

1
La soluzione più pulita di tutte.
Testa MT

3
Forse sottolineare più enfaticamente che questa è un'estensione di Bash che non esiste in POSIX sho generalmente in altre shell.
Tripleee,

Dolce - ci sono così tanti angoli oscuri nel mondo Unix - mai visto prima.
wcochran,

40

Utilizzando mmv :

mmv "fgh*" "jkl#1"

1
Caspita, questo è un modo eccellente e semplice per risolvere il problema! Sono contento di essere introdotto a mmv, grazie!
Hendeca,

1
Grazie!!! Non avevo mai sentito parlare di mmv prima. Ho appena installato e ci ho giocato - semplice, potente.
Nick Rice,

3
se si desidera rinominare in batch ricorsivamente utilizzare ;insieme a #1. esempio:mmv ";fgh*" "#1jkl#2"
Alp

Soluzione molto elegante!
Little Student di Fermat,

1
Esattamente quello di cui avevo bisogno. Cattura i gruppi con ' ' e richiamali con # 1, # 2, ... EX: mmv "my show ep 1080p. *" "My.show. # 1. # 2" = my.show.001.avi
Lundy,

20

Esistono molti modi per farlo (non tutti funzioneranno su tutti i sistemi unixy):

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

    Il § può essere sostituito da tutto ciò che ritieni opportuno. Potresti farlo find -execanche con questo, ma si comporta in modo leggermente diverso su molti sistemi, quindi di solito lo evito

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

    Crudo ma efficace come si suol dire

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

    Davvero carino, ma la ridenominazione non è presente su BSD, che è il sistema unix più comune in assoluto.

  • rename fgh jkl fgh*

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

    Se insisti nell'usare Perl, ma non vi è alcuna ridenominazione sul tuo sistema, puoi usare questo mostro.

Alcuni di questi sono un po 'contorti e l'elenco è lungi dall'essere completo, ma troverai quello che vuoi qui praticamente per tutti i sistemi unix.


2
adoro questo tipo di mostro!
Lefakir,

sì, ho ancora un punto debole anche per il perl :)
iwein


Il mostro Perl qui funziona perfettamente se hai spazi nei nomi dei file.
mlerley il

13
rename fgh jkl fgh*

6
Sulla mia macchina questo produce l'errore "Bareword" fgh "non consentito mentre" subs rigidi "è in uso alla (prima riga) 1".
Stephan202,

@ Stephan202, qual è la tua macchina?
nik,

Ubuntu 8.10 (perl v5.10.0 / 2009-06-26)
Stephan202,

@ Stephan202 Ho lo stesso problema, hai risolto? (7 anni dopo)
whossname

1
@whossname: scusa, davvero non ricordo. (Risposta lenta per ferie).
Stephan202,

8

Utilizzando find, xargse sed:

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

È più complesso della soluzione di @ nik ma consente di rinominare i file in modo ricorsivo. Ad esempio, la struttura,

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

si trasformerebbe in questo,

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

La chiave per farlo funzionare xargsè invocare la shell da xargs .


1
Avrei pubblicato qualcosa del genere, ma mi ci sarebbe voluta un'ora per ottenere il comando giusto
Juan Mendes,

3

Per installare lo script di rinomina Perl :

sudo cpan install File::Rename

Ci sono due rinominazioni come menzionato nei commenti nella risposta di Stephan202. Le distribuzioni basate su Debian hanno il nome Perl . RedHat / rpm distribuzioni hanno la ridenominazione C .
OS X non ne ha uno installato per impostazione predefinita (almeno in 10.8), né Windows / Cygwin.


3

Ecco un modo per farlo usando la riga di comando Groovy:

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

2

Su Solaris puoi provare:

for file in `find ./ -name "*TextForRename*"`; do 
    mv -f "$file" "${file/TextForRename/NewText}"
done

Ottieni mezzo punto per citare i nomi dei file all'interno del ciclo, ma for file in $(find)è fondamentalmente imperfetto e non può essere corretto con la citazione. Se findi rendimenti ./file name with spacessi otterrà un forciclo su ./file, name, with, ed spacese nessuna quantità di citare all'interno del ciclo aiuterà (o addirittura essere necessario).
triplo il

2
#!/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

Questo script ha funzionato per me per la ridenominazione ricorsiva con directory / nomi di file che potrebbero contenere spazi bianchi:

find . -type f -name "*\;*" | while read fname; do
    dirname=`dirname "$fname"`
    filename=`basename "$fname"`
    newname=`echo "$filename" | sed -e "s/;/ /g"`
    mv "${dirname}/$filename" "${dirname}/$newname"
done

Notare l' sedespressione che in questo esempio sostituisce tutte le occorrenze di ;con spazio . Questo dovrebbe ovviamente essere sostituito in base alle esigenze specifiche.


Molte grazie ! Ottima sceneggiatura
Walter Schrabmair,

1

Usando gli strumenti StringSolver (windows e Linux bash) che elaborano con esempi:

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

Calcola innanzitutto un filtro basato su esempi , in cui l'input è il nome del file e l'output (ok e notok, stringhe arbitrarie). Se il filtro avesse l'opzione --auto o fosse invocato da solo dopo questo comando, creerebbe una cartella oke una cartella notoke invierebbe i file rispettivamente a loro.

Quindi usando il filtro, il mvcomando è uno spostamento semi-automatico che diventa automatico con il modificatore --auto. Usando il filtro precedente grazie a --filter, trova un mapping da fghfileaa jklfileae quindi lo applica su tutti i file filtrati.


Altre soluzioni a una linea

Altri modi equivalenti di fare lo stesso (ogni riga è equivalente), quindi puoi scegliere il tuo modo preferito di farlo.

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"

Soluzione multi-step

Per scoprire attentamente se i comandi funzionano bene, puoi digitare quanto segue:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

e quando sei sicuro che il filtro sia buono, esegui la prima mossa:

mv fghfilea jklfilea

Se si desidera verificare e utilizzare il filtro precedente, digitare:

mv --test --filter

Se la trasformazione non è quella che volevi (ad esempio anche mv --explainse vedi che qualcosa non va), puoi digitare mv --clearper riavviare i file in movimento o aggiungere altri esempi in mv input1 input2cui input1 e input2 sono altri esempi

Quando sei sicuro, basta digitare

mv --filter

e voilà! Tutta la ridenominazione viene eseguita utilizzando il filtro.

NOTA BENE: sono un coautore di questo lavoro realizzato a fini accademici. Potrebbe anche esserci presto una funzione per la produzione di bash.


1

È stato molto più facile (sul mio Mac) farlo in Ruby. Ecco 2 esempi:

# 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

Utilizzando renamer :

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

Rimuovi la --dry-runbandiera quando sei soddisfatto che l'output appaia corretto.


1

La mia versione di rinominare i file di massa:

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

Mi piace la possibilità di verificare prima di eseguire lo script
ardochhigh

Ciò si interrompe magnificamente sui nomi dei file contenenti spazi, virgolette o altri metacaratteri della shell.
triplo il

1

Consiglierei di usare il mio script, che risolve questo problema. Ha anche opzioni per cambiare la codifica dei nomi dei file e per convertire la combinazione di segni diacritici in caratteri precomposti, un problema che ho sempre quando copio i file dal mio 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);
  }
}

2
Si noti che le risposte solo al collegamento sono sconsigliate, le risposte SO dovrebbero essere il punto finale di una ricerca di una soluzione (rispetto a un altro scalo di riferimento, che tende a diventare stantio nel tempo). Si prega di considerare l'aggiunta di una sinossi stand-alone qui, mantenendo il collegamento come riferimento.
Kleopatra,

Come previsto da @kleopatra , il collegamento è stato ottenutostale over time
y2k-shubham

1
@ y2k-shubham, non più.
André von Kugland,

0

Questo ha funzionato per me usando regexp:

Volevo che i file fossero rinominati in questo modo:

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

usa regexp [az | _] + 0 * ([0-9] +. ) dove ([0-9] +. ) è una sottostringa di gruppo da usare sul comando rename

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

produce:

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

Un altro esempio:

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

produce:

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

Attenzione, stai attento.


0

Ho scritto questo script per cercare tutti i file .mkv rinominando in modo ricorsivo i file trovati in .avi. Puoi personalizzarlo in base alle tue esigenze. Ho aggiunto altre cose come ottenere la directory dei file, l'estensione, il nome del file da un percorso del file nel caso in cui sia necessario fare riferimento a qualcosa in futuro.

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

Uno script generico per eseguire sedun'espressione in un elenco di file (combina la sedsoluzione con la renamesoluzione ):

#!/bin/sh

e=$1
shift

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

Richiamare trasmettendo sedun'espressione allo script e quindi qualsiasi elenco di file, proprio come una versione di rename:

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

0

Puoi anche usare lo script seguente. è molto facile da eseguire sul terminale ...

// Rinomina più file alla volta

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

Esempio:-

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.