Benennen Sie mehrere Dateien basierend auf dem Muster in Unix um


248

In einem Verzeichnis befinden sich mehrere Dateien, die mit dem Präfix beginnen fgh, zum Beispiel:

fghfilea
fghfileb
fghfilec

Ich möchte alle umbenennen, um mit dem Präfix zu beginnen jkl. Gibt es einen einzigen Befehl, um dies zu tun, anstatt jede Datei einzeln umzubenennen?


1
Überprüfen Sie hier: theunixshell.blogspot.com/2013/01/…
Vijay

Antworten:


289

Es gibt verschiedene Möglichkeiten, aber die Verwendung renamewird wahrscheinlich die einfachste sein.

Verwenden einer Version von rename:

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

Verwenden einer anderen Version von rename(wie die Antwort von Judy2K ):

rename fgh jkl fgh*

Sie sollten die Manpage Ihrer Plattform überprüfen, um festzustellen, welche der oben genannten Bedingungen zutrifft.


7
+1 Ich wusste nicht einmal, wie man umbenennt ... Jetzt kann ich aufhören, eine for-Schleife mit mv und sed zu verwenden ... Danke!
Balpha

2
Sie verlinken auf eine andere renameals die Syntax für unixhelp.ed.ac.uk/CGI/man-cgi?rename ist die andere
Hasturkun

7
Nicht auf allen * nix-Systemen vorhanden. Zum einen nicht unter Max OS X und kein Paket in Fink, um es zu bekommen. Ich habe MacPorts nicht angeschaut.
dmckee --- Ex-Moderator Kätzchen

6
AFAICT renamescheint ein Linux-spezifisches Skript oder Dienstprogramm zu sein. Wenn Sie sich überhaupt für Portabilität interessieren, verwenden Sie weiterhin sedand-Schleifen oder ein Inline-Perl-Skript.
D. Shawley

33
brew install renameunter OS X :)
Sam

117

Dies ist, wie sedund mvkann zusammen verwendet werden, um umzubenennen:

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

Laut Kommentar unten müssen Anführungszeichen, wenn die Dateinamen Leerzeichen enthalten, die Unterfunktion umgeben, die den Namen zurückgibt, um die Dateien zu verschieben:

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

1
Sehr nah. Beachten Sie, dass Sie nur das erste Vorkommen von fgh abgleichen möchten: 's / ^ fgh / jkl / g' (Das Caret macht den Unterschied).
Stephan202

2
Nur aus Gründen der Präzision ... Sie meinen "fgh am Anfang des Namens", nicht "das erste Auftreten von fgh". / ^ fgh / entspricht "fghi", aber nicht "efgh".
Dave Sherohman

@Stephan, das war ein Tippfehler von meiner Seite (behoben).
Nik

5
Wenn Sie keinen Zugriff auf "Umbenennen" haben, funktioniert dies hervorragend. Der Code erfordert möglicherweise Anführungszeichen, wenn Ihre Dateinamen Leerzeichen enthalten. for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done
Dave Nelson

1
@nik Ohne Anführungszeichen würde das Umbenennen dieser Dateiliste einen Fehler auslösen : touch fghfilea fghfileb fghfilec fghfile\ d. Ich schlage freundlicherweise vor, die @ DaveNelson-Bemerkungen zu berücksichtigen.
Luissquall

75

Das Umbenennen ist möglicherweise nicht in jedem System möglich. Wenn Sie es nicht haben, verwenden Sie die Shell in diesem Beispiel in der Bash-Shell

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

1
In dieser Lösung sedist kein Befehl erforderlich. Dieser ist einfacher als die Antwort von @ nik.
Halil

xxx steht für den Ersatz? im Falle des Originalplakats wäre das "jkl" gewesen
Awinad

1
Die sauberste Lösung von allen.
MT Head

3
Weisen Sie vielleicht nachdrücklicher darauf hin, dass dies eine Bash-Erweiterung ist, die in POSIX shoder allgemein in anderen Shells nicht vorhanden ist .
Tripleee

Süß - es gibt so viele dunkle Ecken in der Unix-Welt - noch nie zuvor gesehen.
Wcochran

40

Mit mmv :

mmv "fgh*" "jkl#1"

1
Wow, dies ist eine hervorragende und einfache Möglichkeit, das Problem zu lösen! Ich bin froh, mmv kennenzulernen, danke!
Hendeca

1
Vielen Dank!!! Hatte noch nie von mmv gehört. Einfach installiert und damit gespielt - einfach, leistungsstark.
Nick Rice

3
Wenn Sie die Batch-Umbenennung rekursiv umbenennen möchten, verwenden Sie sie ;in Verbindung mit #1. Beispiel:mmv ";fgh*" "#1jkl#2"
Alp

Sehr elegante Lösung!
Fermats kleiner Student

1
Genau das, was ich brauchte. Erfassen Sie Gruppen mit ' ' und rufen Sie sie mit # 1, # 2, ... zurück. EX: mmv "my show ep 1080p. *" "My.show. # 1. # 2" = my.show.001.avi
Lundy

20

Es gibt viele Möglichkeiten, dies zu tun (nicht alle funktionieren auf allen Unixy-Systemen):

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

    Der § kann durch alles ersetzt werden, was Sie für zweckmäßig halten. Sie könnten dies auch tun, find -execaber das verhält sich auf vielen Systemen subtil anders, deshalb vermeide ich das normalerweise

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

    Roh aber effektiv wie sie sagen

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

    Wirklich hübsch, aber das Umbenennen ist auf BSD, dem am häufigsten verwendeten Unix-System afaik, nicht vorhanden.

  • rename fgh jkl fgh*

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

    Wenn Sie darauf bestehen, Perl zu verwenden, Ihr System jedoch nicht umbenannt wurde, können Sie dieses Monster verwenden.

Einige davon sind etwas kompliziert und die Liste ist bei weitem nicht vollständig, aber Sie finden hier, was Sie für so ziemlich alle Unix-Systeme wollen.


2
Ich liebe diese Art von Monster!
Lefakir

Ja, ich habe immer noch eine Schwachstelle für Perl :)
iwein


Das Perl-Monster hier funktioniert einwandfrei, wenn Ihre Dateinamen Leerzeichen enthalten.
Mlerley

13
rename fgh jkl fgh*

6
Auf meinem Computer wird der Fehler "Bareword" fgh "nicht zulässig, während" strict subs "in (eval 1) Zeile 1 verwendet wird."
Stephan202

@ Stephan202, was ist deine Maschine?
Nik

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

@ Stephan202 Ich habe das gleiche Problem, hast du gelöst? (7 Jahre später)
Whossname

1
@whossname: Entschuldigung, ich kann mich wirklich nicht erinnern. (Langsame Antwort wegen Urlaub.)
Stephan202

8

Unter Verwendung find, xargsund sed:

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

Es ist komplexer als die Lösung von @ nik , ermöglicht jedoch das rekursive Umbenennen von Dateien. Zum Beispiel die Struktur,

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

würde dazu transformiert werden,

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

Der Schlüssel, mit dem es funktioniert, xargsist das Aufrufen der Shell von xargs .


1
Ich wollte so etwas posten, aber ich hätte eine Stunde gebraucht, um den richtigen Befehl zu erhalten
Juan Mendes


3

Hier ist eine Möglichkeit, dies mit der Befehlszeile Groovy zu tun:

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

2

Unter Solaris können Sie Folgendes versuchen:

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

Sie erhalten einen halben Punkt für das Zitieren der Dateinamen innerhalb der Schleife, sind jedoch for file in $(find)grundlegend fehlerhaft und können nicht durch Zitieren korrigiert werden. Wenn findRückkehr ./file name with spaceswerden Sie erhalten eine forSchleife über ./file, name, with, und spacesund kein Betrag der innerhalb der Schleife unter Angabe helfen (oder sogar notwendig sein).
Tripleee

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

Dieses Skript funktionierte für mich beim rekursiven Umbenennen mit Verzeichnissen / Dateinamen, die möglicherweise Leerzeichen enthalten:

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

Beachten Sie den sedAusdruck, der in diesem Beispiel alle Vorkommen von ;durch Leerzeichen ersetzt . Dies sollte natürlich entsprechend den spezifischen Anforderungen ersetzt werden.


Vielen Dank ! Tolles Drehbuch
Walter Schrabmair

1

Verwenden von StringSolver- Tools (Windows & Linux Bash), die anhand von Beispielen verarbeitet werden:

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

Zunächst wird anhand von Beispielen ein Filter berechnet , bei dem die Eingabe die Dateinamen und die Ausgabe sind (ok und notok, beliebige Zeichenfolgen). Wenn der Filter die Option --auto hätte oder nach diesem Befehl alleine aufgerufen würde, würden ein Ordner okund ein Ordner erstelltnotok und Dateien an diese .

Bei Verwendung des Filters ist der mvBefehl eine halbautomatische Bewegung, die mit dem Modifikator --auto automatisch wird. Mit dem vorherigen Filter findet er dank --filter eine Zuordnung von fghfileabis jklfileaund wendet sie dann auf alle gefilterten Dateien an.


Andere einzeilige Lösungen

Andere äquivalente Methoden, um dasselbe zu tun (jede Zeile ist äquivalent), sodass Sie Ihre bevorzugte Methode auswählen können.

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"

Mehrstufige Lösung

Um sorgfältig festzustellen, ob die Befehle eine gute Leistung erbringen, können Sie Folgendes eingeben:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

und wenn Sie sicher sind, dass der Filter gut ist, führen Sie den ersten Schritt aus:

mv fghfilea jklfilea

Wenn Sie den vorherigen Filter testen und verwenden möchten, geben Sie Folgendes ein:

mv --test --filter

Wenn die Umwandlung nicht Ihren Wünschen entspricht (z. B. auch mv --explainwenn Sie feststellen, dass etwas nicht stimmt), können Sie eingeben, um verschobene mv --clearDateien neu zu starten, oder weitere Beispiele hinzufügen, mv input1 input2wobei input1 und input2 andere Beispiele sind

Wenn Sie sicher sind, geben Sie einfach ein

mv --filter

und voilà! Das Umbenennen erfolgt über den Filter.

HAFTUNGSAUSSCHLUSS: Ich bin Mitautor dieser Arbeit für akademische Zwecke. Möglicherweise wird es bald auch eine Bash-produzierende Funktion geben.


1

In Ruby war dies (auf meinem Mac) viel einfacher. Hier sind 2 Beispiele:

# 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

Mit Renamer :

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

Entfernen Sie das --dry-runFlag, wenn Sie zufrieden sind, dass die Ausgabe korrekt aussieht.


1

Meine Version zum Umbenennen von Massendateien:

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

Ich mag die Fähigkeit zu überprüfen, bevor das Skript ausgeführt wird
ardochhigh

Dies funktioniert hervorragend bei Dateinamen, die Leerzeichen, Anführungszeichen oder andere Shell-Metazeichen enthalten.
Tripleee

1

Ich würde empfehlen, mein eigenes Skript zu verwenden, um dieses Problem zu lösen. Es gibt auch Optionen zum Ändern der Codierung der Dateinamen und zum Konvertieren von kombinierten diakritischen Zeichen in vorkomponierte Zeichen, ein Problem, das ich immer habe, wenn ich Dateien von meinem Mac kopiere.

#!/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
Beachten Sie, dass von Antworten nur mit Links abgeraten wird. SO-Antworten sollten der Endpunkt einer Suche nach einer Lösung sein (im Gegensatz zu einem weiteren Zwischenstopp von Referenzen, die im Laufe der Zeit veralten). Bitte fügen Sie hier eine eigenständige Zusammenfassung hinzu, wobei Sie den Link als Referenz behalten.
Kleopatra

Wie von @kleopatra vorhergesagt , hat der Link bekommenstale over time
y2k-shubham

1
@ y2k-shubham, nicht mehr.
André von Kugland

0

Dies funktionierte bei mir mit regexp:

Ich wollte, dass Dateien wie folgt umbenannt werden:

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

Verwenden Sie den regulären Ausdruck [az | _] + 0 * ([0-9] +. ), wobei ([0-9] +. ) eine Gruppensubstring ist, die für den Umbenennungsbefehl verwendet werden soll

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

Produziert:

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

Ein anderes Beispiel:

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

Produziert:

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

Achtung, sei vorsichtig.


0

Ich habe dieses Skript geschrieben, um nach allen .mkv-Dateien zu suchen, die gefundene Dateien rekursiv in .avi umbenennen. Sie können es an Ihre Bedürfnisse anpassen. Ich habe einige andere Dinge hinzugefügt, wie das Abrufen des Dateiverzeichnisses, der Erweiterung und des Dateinamens von einem Dateipfad, nur für den Fall, dass Sie in Zukunft auf etwas verweisen müssen.

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

Ein generisches Skript zum Ausführen eines sedAusdrucks in einer Liste von Dateien (kombiniert die sedLösung mit der renameLösung ):

#!/bin/sh

e=$1
shift

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

Rufen Sie auf, indem Sie dem Skript einen sedAusdruck und dann eine beliebige Liste von Dateien übergeben, genau wie bei einer Version von rename:

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

0

Sie können auch das folgende Skript verwenden. es ist sehr einfach auf Terminal zu laufen ...

// Mehrere Dateien gleichzeitig umbenennen

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

Beispiel:-

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.