Перейменуйте кілька файлів на основі шаблону в Unix


249

У каталозі є кілька файлів, які починаються з префікса fgh, наприклад:

fghfilea
fghfileb
fghfilec

Я хочу перейменувати їх усіх, щоб почати з префікса jkl. Чи є одна команда зробити це замість перейменування кожного файлу окремо?


1
перевірити тут: theunixshell.blogspot.com/2013/01/…
Vijay

Відповіді:


290

Є кілька способів, але використовувати rename, мабуть, буде найпростіше.

Використання однієї версії rename:

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

Використовуючи іншу версію rename(те саме , що відповідь Judy2K ):

rename fgh jkl fgh*

Перевірте головну сторінку вашої платформи, щоб побачити, що з вищезазначеного застосовується.


7
+1 Навіть не знав про перейменування ... Тепер я можу припинити використовувати цикл for mv та sed ... Дякую!
balpha

2
Ви посилаєтесь на інше, renameтоді ви показуєте синтаксис unixhelp.ed.ac.uk/CGI/man-cgi?rename є іншим
Hasturkun

7
Немає у всіх * nix системах. Не на Max OS X для одного, і жодного пакета в накладних, щоб отримати його. Не дивився на MacPorts.
dmckee --- кошеня колишнього модератора

6
AFAICT, renameздається, є специфічним для Linux сценарієм або утилітою. Якщо ви взагалі переймаєтесь портативністю, продовжуйте використовувати sedі циклічні вбудовані сценарії Perl.
Д.Шоулі

33
brew install renameна OS X :)
сам

117

Ось як це можна sedі mvразом використовувати для перейменування:

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

Відповідно до коментаря нижче, якщо в іменах файлів є пробіли, лапки, можливо, знадобиться оточити підфункцію, яка повертає ім'я для переміщення файлів у:

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

1
Дуже близько. Зауважте, що ви хочете відповідати лише першому виникненню fgh: 's / ^ fgh / jkl / g' (карета має все значення).
Stephan202

2
Просто задля точності ... Ви маєте на увазі "fgh на початку імені", а не "перше виникнення fgh". / ^ fgh / відповідатиме "fghi", але не "efgh".
Дейв Шерохман

@Stephan, це був друкарський помилок з мого боку (виправлено).
nik

5
Якщо у вас немає доступу до "перейменувати", це чудово працює. Код може зажадати лапок, якщо імена файлів містять пробіли. for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done
Дейв Нельсон

1
@nik Без лапок перейменування цей список файлів буде згенеровано повідомлення про помилку: touch fghfilea fghfileb fghfilec fghfile\ d. Я пропоную врахувати зауваження @DaveNelson.
luissquall

75

перейменування може бути не в кожній системі. тож якщо у вас його немає, використовуйте цей приклад у bash shell

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

1
У цьому рішенні sedкоманда не потрібна. Цей простіший, ніж відповідь @ nik.
Халіль

xxx означає заміну? у випадку оригінального плаката це було б "jkl"
Awinad

1
Найчистіше рішення їх усіх.
Голова МТ

3
Можливо, більш чітко вкажіть, що це розширення Bash, яке не існує в POSIX shабо взагалі в інших оболонках.
трійка

Солодке - у світі Unix так багато незрозумілих куточків - цього раніше ніколи не бачив.
wcochran

40

Використання mmv :

mmv "fgh*" "jkl#1"

1
Ого, це відмінний і простий спосіб вирішення проблеми! Радий бути представленим у mmv, дякую!
Хендека

1
Дякую!!! Ніколи раніше не чув про ммв. Щойно встановлена ​​та граюча з нею - проста, потужна.
Нік Райс

3
якщо ви хочете отримати пакетне перейменування, рекурсивно використовуйте ;спільно з #1. Приклад:mmv ";fgh*" "#1jkl#2"
Alp

Дуже елегантне рішення!
Маленький студент Ферма

1
Саме те, що мені було потрібно. Захоплення груп за допомогою " " та передзвоніть їм за допомогою номерів №1, # 2, ... EX: mmv "my show ep 1080p. *" "My.show. # 1. # 2" = my.show.001.avi
Lundy

20

Існує багато способів зробити це (не всі вони будуть працювати на всіх системах unixy):

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

    Пункт може бути замінений будь-чим, що вам зручно. Ви можете зробити це find -execзанадто, але це поводиться дещо по-різному в багатьох системах, тому я зазвичай цього уникаю

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

    Сирий, але ефективний, як кажуть

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

    Справжнє досить, але перейменування немає на BSD, яка є найпоширенішою системою afaik Unix.

  • rename fgh jkl fgh*

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

    Якщо ви наполягаєте на використанні Perl, але у вашій системі немає перейменування, ви можете використовувати цього монстра.

Деякі з них дещо заплутані, і перелік далеко не повний, але ви знайдете те, що хочете тут, майже для всіх Unix систем.


2
я люблю такого роду чудовисько!
лефакір

так, у мене все ще слабке місце для Perl :)
iwein


Тут чудовисько Perl працює бездоганно, якщо у ваших іменах є пробіли.
mlerley

13
rename fgh jkl fgh*

6
На моїй машині це створює помилку "Bareword" fgh "не дозволено, коли" строгі підводні записи "використовуються в (eval 1) рядку 1."
Stephan202

@ Stephan202, яка у вас машина?
nik

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

@ Stephan202 У мене те саме питання, ви вирішили? (7 років пізніше)
Whossname

1
@whossname: вибачте, насправді не пам'ятаю. (Повільна відповідь через свято.)
Stephan202

8

Використання find, xargsі sed:

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

Це складніше, ніж рішення @ nik, але дозволяє реєструвати файли рекурсивно. Наприклад, структура,

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

буде перетворено на це,

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

Ключовим фактором для роботи xargsє виклик оболонки від xargs .


1
Я збирався опублікувати щось подібне, але для отримання правильної команди знадобилася б мені година
Хуан Мендес

3

Щоб встановити сценарій перейменування Perl :

sudo cpan install File::Rename

Є два перейменування, як згадується у коментарях у відповіді Стефана202. Дистрибутиви на основі Debian мають перейменування Perl . Дистрибутори Redhat / rpm мають C перейменувати .
У OS X не встановлено жодного за замовчуванням (принаймні в 10.8), а також Windows / Cygwin.


3

Ось спосіб це зробити за допомогою командного рядка Groovy:

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

2

На Solaris ви можете спробувати:

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

Ви отримуєте половину балу за цитування імен файлів всередині циклу, але for file in $(find)це принципово хибно і не може бути виправлено цитуванням. Якщо findповертається ./file name with spacesви отримаєте forпетлю на ./file, name, withі spacesта ніяке кількість квотування всередині циклу допоможе (або навіть необхідно).
тріплей

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

Цей сценарій працював для мене для рекурсивного перейменування з іменами каталогів / файлів, можливо, містять пробіли:

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

Зауважте sedвираз, який у цьому прикладі замінює всі випадки ;пробілу . Звичайно, це слід замінити відповідно до конкретних потреб.


Дуже дякую ! Чудовий сценарій
Вальтер Шрабмайр

1

Використання інструментів StringSolver (Windows & Linux bash), які обробляють приклади:

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

Спочатку він обчислює фільтр на основі прикладів , де вхідними є імена файлів та вихід (ok і notok, довільні рядки). Якщо фільтр мав опцію --auto або був викликаний один після цієї команди, він створив би папку okта папкуnotok та надсилав файли відповідно до них.

Потім, використовуючи фільтр, mvкоманда - це напівавтоматичний хід, який стає автоматичним з модифікатором --auto. Використовуючи попередній фільтр завдяки --filter, він знаходить відображення від fghfileaдо, jklfileaа потім застосовує його до всіх відфільтрованих файлів.


Інші однолінійні рішення

Інші еквівалентні способи зробити те саме (кожен рядок еквівалентний), тому ви можете вибрати свій улюблений спосіб робити це.

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"

Багатоступеневе рішення

Щоб уважно встановити, чи добре працюють команди, можна ввести наступне:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

і коли ви впевнені, що фільтр хороший, виконайте перший хід:

mv fghfilea jklfilea

Якщо ви хочете протестувати і використовувати попередній фільтр, введіть:

mv --test --filter

Якщо трансформація не є тим, що ви хотіли (наприклад, навіть якщо mv --explainви бачите, що щось не так), ви можете ввести, mv --clearщоб перезапустити переміщувані файли або додати більше прикладів, mv input1 input2де input1 та input2 - інші приклади

Коли ви впевнені, просто введіть

mv --filter

і voilà! Все перейменування проводиться за допомогою фільтра.

ВІДМОВА: Я є співавтором цього твору, зробленого в академічних цілях. Невдовзі також може з’явитися функція, що виробляє башти.


1

Це було набагато простіше (на моєму Mac) зробити це в Ruby. Ось два приклади:

# 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 --find /^fgh/ --replace jkl * --dry-run

Видаліть --dry-runпрапор, як тільки ви щасливі, результат виглядає правильним.


1

Моя версія перейменування масових файлів:

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

Мені подобається можливість перевірити перед запуском сценарію
ardochhigh

Це суттєво розбивається на імена файлів, що містять пробіли, лапки або інші метахарактеристики оболонки.
трійка

1

Я б рекомендував використовувати власний сценарій, який вирішує цю проблему. У ньому також є можливості змінити кодування імен файлів і перетворити комбінуючі діакритики на заздалегідь складені символи - проблема, яку я завжди маю, коли копіюю файли з мого 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
Зауважте, що відповіді, що стосуються лише посилань, не відштовхують, відповіді на відповідність повинні бути кінцевою точкою пошуку рішення (проти ще однієї зупинки посилань, яка, як правило, затягується з часом). Будь ласка, розглянути можливість додавання сюди окремого конспекту, зберігаючи посилання як орієнтир.
Клеопатра

Як прогнозував @kleopatra , посилання stale over time
дісталося

1
@ y2k-shubham, вже не.
Андре фон Кугланд

0

Це працювало для мене за допомогою regexp:

Я хотів, щоб файли були перейменовані так:

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

usig [az | _] + 0 * ([0-9] +. ) regexp де ([0-9] +. ) є підрядком групи, який слід використовувати в команді перейменування.

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

Виробляє:

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

Ще один приклад:

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

Виробляє:

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

Попередження, будьте обережні.


0

Я написав цей скрипт, щоб шукати всі .mkv файли рекурсивно перейменовуючи знайдені файли у .avi. Ви можете налаштувати його під свої потреби. Я додав ще деякі речі, такі як отримання файлової каталогів, розширення, ім'я файлу з шляху до файлу просто у випадку, якщо вам потрібно буде посилатися на щось у майбутньому.

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

Універсальний скрипт для запуску sedвиразу у списку файлів (поєднує sedрішення з renameрішенням ):

#!/bin/sh

e=$1
shift

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

Закликайте, передаючи скрипту sedвираз, а потім будь-який список файлів, як і версію rename:

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

0

Ви також можете використовувати сценарій нижче. запустити на терміналі дуже просто ...

// Перейменуйте кілька файлів одночасно

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

Приклад: -

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.