Unix'te desene göre birden çok dosyayı yeniden adlandırma


249

Bir dizinde önek ile başlayan birden fazla dosya vardır, fghörneğin:

fghfilea
fghfileb
fghfilec

Ön ek ile başlamak için hepsini yeniden adlandırmak istiyorum jkl. Her dosyayı tek tek yeniden adlandırmak yerine bunu yapmak için tek bir komut var mı?



Yanıtlar:


290

Birkaç yol vardır, ancak renamekullanımı muhtemelen en kolayı olacaktır.

Şunun bir sürümünü kullanma rename:

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

Başka bir sürümünü kullanma rename( Judy2K'nin cevabı ile aynı ):

rename fgh jkl fgh*

Yukarıdakilerden hangisinin geçerli olduğunu görmek için platformunuzun kılavuz sayfasını kontrol etmelisiniz.


7
+1 Yeniden adlandırmayı bile bilmiyordum ... Şimdi mv ve sed ile bir for döngüsü kullanmayı bırakabilirim ... Teşekkürler!
balpha

2
Farklı bir bağlantı veriyor renameo zaman için sözdizimi gösteriyoruz unixhelp.ed.ac.uk/CGI/man-cgi?rename diğeri
Hasturkun

7
Tüm * nix sistemlerinde mevcut değildir. Biri için Max OS X'te değil ve almak için fink'te paket yok. MacPorts'a bakmadım.
dmckee --- ex-moderatör kedi yavrusu

6
AFAICT, renameLinux'a özgü bir komut dosyası veya yardımcı program gibi görünüyor. Taşınabilirlikle ilgileniyorsanız, lütfen sedve döngülerini veya satır içi Perl betiğini kullanmaya devam edin .
D.Shawley

33
brew install renameOS X :)
sam

117

Bu nasıl sedve mvyeniden adlandırma yapmak için birlikte kullanılabilir:

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

Aşağıdaki yoruma göre, dosya adlarının içinde boşluklar varsa, tırnakların dosyaları taşımak için adı döndüren alt işlevi çevrelemesi gerekebilir :

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

1
Çok yakın. Yalnızca fgh: 's / ^ fgh / jkl / g' ifadesinin yalnızca ilk eşleşmesini eşleştirmek istediğinizi unutmayın (düzeltme işareti tüm farkı yaratır).
Stephan202

2
Sadece doğruluk uğruna ... "adın başlangıcında fgh" demek istersiniz, "fgh'nin ilk oluşumu" değil. / ^ fgh / "fghi" ile eşleşir, ancak "efgh" ile eşleşmez.
Dave Sherohman

@Stephan, Bu benim için bir yazım hatasıydı (düzeltti).
nik

5
Eğer "yeniden adlandır" a erişiminiz yoksa bu harika çalışıyor. Dosya adlarınız boşluk içeriyorsa kod için tırnak işareti gerekebilir. for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done
Dave Nelson

1
tırnak hata atmak olacaktır dosyaların bu listeyi yeniden adlandırma olmadan @nik: touch fghfilea fghfileb fghfilec fghfile\ d. Lütfen @DaveNelson sözlerini dikkate almanızı öneririm.
luissquall

75

rename her sistemde olmayabilir. eğer sahip değilseniz, bu örneği bash kabuğunda kullanın

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

1
Bu çözümde sedkomut gerekli değildir. Bu, @ nik'in cevabından daha basit.
Halil

xxx değiştirme anlamına mı geliyor? orijinal poster durumunda, bu "jkl" olurdu
Awinad

1
Hepsinin en temiz çözümü.
MT Head

3
Belki de bunun POSIX'te shveya genel olarak diğer mermilerde bulunmayan bir Bash uzantısı olduğuna dikkat çekiyorsunuz.
tripleee

Tatlı - Unix dünyasında çok fazla belirsiz köşe var - bunu daha önce hiç görmedim.
wcochran

40

Mmv kullanarak :

mmv "fgh*" "jkl#1"

1
Vay be, bu problemi çözmenin mükemmel ve basit bir yolu! Mmv ile tanıştığıma memnun oldum, teşekkürler!
Hendeca

1
Teşekkürler!!! Daha önce mmv'yi hiç duymamıştı. Sadece yüklü ve onunla oynuyor - basit, güçlü.
Nick Rice

3
toplu olarak yeniden adlandırmak istiyorsanız ile ;birlikte kullanın #1. örnek:mmv ";fgh*" "#1jkl#2"
Alp

Çok zarif bir çözüm!
Fermat'ın Küçük Öğrencisi

1
Tam olarak ihtiyacım olan şey. ' ' İle grupları yakalayın ve # 1, # 2, ... EX: mmv "şov ep 1080p. *" "My.show. # 1. # 2" = my.show.001.avi
Lundy

20

Bunu yapmanın birçok yolu vardır (bunların hepsi tüm unixy sistemlerinde çalışmayacaktır):

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

    § yerine uygun bulduğunuz herhangi bir şey konabilir. Bunu da yapabilirsiniz find -execama bu birçok sistemde farklı davranır, bu yüzden genellikle bundan kaçınırım

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

    Kaba ama dedikleri gibi etkilidir

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

    Gerçekten güzel, ama yeniden adlandırma, en yaygın unix sistemi afaik olan BSD'de mevcut değil.

  • rename fgh jkl fgh*

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

    Perl kullanmakta ısrar ediyorsanız, ancak sisteminizde yeniden adlandırma yoksa, bu canavarı kullanabilirsiniz.

Bunlardan bazıları biraz kıvrık ve liste tam olmaktan uzak, ancak hemen hemen tüm unix sistemleri için burada ne istediğinizi bulacaksınız.


2
bu tür bir canavarı seviyorum!
lefakir

evet, hala perl için de zayıf bir nokta var :)
iwein


Dosya adlarınızda boşluk varsa Perl canavarı kusursuz çalışır.
mlerley

13
rename fgh jkl fgh*

6
Makinemde bu, (eval 1) satır 1'de "katı subs" kullanılırken 'Bareword "fgh" hatasına izin verilmiyor hatası veriyor.
Stephan202

@ Stephan202, makineniz nedir?
nik

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

@ Stephan202 Aynı sorunum var, çözdün mü? (7 yıl sonra)
whossname

1
@whossname: üzgünüm, gerçekten hatırlayamıyorum. (Tatil nedeniyle yavaş cevap.)
Stephan202

8

Kullanılması find, xargsve sed:

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

@ Nik'in çözümünden daha karmaşıktır, ancak dosyaları özyinelemeli olarak yeniden adlandırmaya izin verir. Örneğin, yapı,

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

buna dönüşecekti,

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

Anahtarı ile çalışması için xargsolmaktır Xargs gelen kabuğu çağırmak .


1
Böyle bir şey yayınlayacaktım, ama doğru komutu almam bir saatimi alacaktı
Juan Mendes


3

İşte Groovy komut satırı kullanarak bunu yapmanın bir yolu:

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

2

Solaris'te deneyebilirsiniz:

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

Döngü içindeki dosya adlarını alıntılamak için yarım puan alırsınız , ancak for file in $(find)temelde kusurludur ve alıntı ile düzeltilemez. Eğer findgetiri ./file name with spacesBir alacak forüzerindeki döngü ./file, name, withve spacesve yardımcı (hatta gerekli) olacak döngünün içine alıntı hiçbir miktarı.
Üçlü

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

Bu komut dosyası benim için muhtemelen boşluk içeren dizinler / dosya adları ile yinelenen yeniden adlandırma için çalıştı:

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

sedBu örnekte tüm oluşumlarını ;boşlukla değiştiren ifadeye dikkat edin . Bu elbette özel ihtiyaçlara göre değiştirilmelidir.


Çok teşekkürler ! Great script
Walter Schrabmair

1

Örneklerle işlenen StringSolver araçlarını (windows ve Linux bash) kullanma :

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

İlk olarak , girdilerin dosya adları ve çıktı (ok ve notok, rastgele dizeler) olduğu örneklere dayalı bir filtre hesaplar . Filtrenin --auto seçeneği varsa veya bu komuttan sonra tek başına çağrıldıysa, sırasıyla okbir klasör notokve klasör oluşturur ve dosyaları bunlara aktarır.

Daha sonra filtreyi kullanarak, mvkomut --auto değiştiricisi ile otomatik hale gelen yarı otomatik bir harekettir . --Filter önceki filtre sayesinde kullanarak, bir eşleme bulur fghfileaetmek jklfileave sonra tüm filtrelenmiş dosyalar üzerinde uygular.


Diğer tek hatlı çözümler

Aynı yapmanın diğer eşdeğer yolları (her satır eşdeğerdir), böylece en sevdiğiniz yolu seçebilirsiniz.

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"

Çok adımlı çözüm

Komutların iyi performans gösterip göstermediğini dikkatlice bulmak için aşağıdakileri yazabilirsiniz:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

filtrenin iyi olduğundan eminseniz, ilk hamleyi yapın:

mv fghfilea jklfilea

Test etmek ve önceki filtreyi kullanmak istiyorsanız, şunu yazın:

mv --test --filter

Dönüştürme istediğiniz gibi değilse (örneğin mv --explain, bir şeylerin yanlış olduğunu görürseniz bile ), mv --clearhareketli dosyaları yeniden başlatmak için yazabilir veya mv input1 input2input1 ve input2'nin diğer örnekler olduğu başka örnekler ekleyebilirsiniz.

Emin olduğunuzda, sadece şunu yazın

mv --filter

ve voilà! Tüm yeniden adlandırma filtre kullanılarak yapılır.

YASAL UYARI: Ben akademik amaçlarla yapılan bu çalışmanın ortak yazarıyım. Yakında bash üreten bir özellik de olabilir.


1

Bunu (Mac bilgisayarımda) Ruby'de yapmak çok daha kolaydı. İşte 2 örnek:

# 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

Renamer'ı kullanma :

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

--dry-runÇıkışın doğru göründüğünden mutlu olduktan sonra bayrağı kaldırın .


1

Toplu dosyaları yeniden adlandırma sürümüm:

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

Komut dosyasını çalıştırmadan önce doğrulama yeteneğini seviyorum
ardochhigh

Bu, boşluklar, tırnak işaretleri veya diğer kabuk metakarakterlerini içeren dosya adlarını büyük ölçüde kırar.
tripleee

1

Bu sorunu çözen kendi scriptimi kullanmanızı tavsiye ederim. Ayrıca dosya adlarının kodlamasını değiştirmek ve aksanları birleştirerek önceden oluşturulmuş karakterlere dönüştürmek için seçenekler var, Mac'imden dosyaları kopyalarken her zaman sahip olduğum bir sorun.

#!/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
Not o bağlantı sadece cevaplar tavsiye edilmez, SO cevaplar çözüm (vs. henüz zamanla bayat almak eğilimindedir referanslar, başka mola) için bir aramanın son nokta olmalıdır. Lütfen bağlantıyı referans olarak tutarak bağımsız bir özet eklemeyi düşünün.
Kleopatra

Tarafından tahmin olarak @kleopatra , bağlantı aldıstale over time
y2k-Shubham

1
@ y2k-shubham, artık değil.
André von Kugland

0

Bu benim için regexp kullanarak çalıştı:

Dosyaların bu şekilde yeniden adlandırılmasını istedim:

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

[az | _] + 0 * ([0-9] +. ) regexp'yi kullanın; burada ([0-9] +. ) rename komutunda kullanılacak bir grup alt dizesidir

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

üretir:

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

Başka bir örnek:

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

üretir:

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

Uyarı, dikkatli ol.


0

Bulunan dosyaları .avi olarak yinelenen tüm .mkv dosyalarını aramak için bu komut dosyasını yazdım. Bunu kendi gereksinimlerinize göre özelleştirebilirsiniz. Gelecekte bir şeye başvurmanız gerektiğinde, dosya dizininden dosya dizini, uzantı, dosya adı almak gibi başka şeyler ekledim.

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

Bir çalıştırmak için genel bir komut seddosyaları bir listede ifade (birleştiren sedçözümü ile renameçözelti ):

#!/bin/sh

e=$1
shift

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

Komut dosyasına bir sedifade ve daha sonra dosyaların herhangi bir listesi gibi bir sürüm geçirerek çağırın rename:

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

0

Aşağıdaki betiği de kullanabilirsiniz. terminalde çalıştırmak çok kolay ...

// Tek seferde birden fazla dosyayı yeniden adlandırma

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

Misal:-

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.