Переименование нескольких файлов на основе шаблона в Unix


249

В каталоге несколько файлов, начинающихся с префикса fgh, например:

fghfilea
fghfileb
fghfilec

Я хочу переименовать их все, чтобы начать с префикса jkl. Есть ли одна команда, чтобы сделать это вместо переименования каждого файла в отдельности?


1
проверьте здесь: theunixshell.blogspot.com/2013/01/…
Виджай

Ответы:


290

Есть несколько способов, но с помощью rename будет, вероятно, самым простым.

Используя одну версию rename:

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

Используя другую версию rename(так же, как ответ Judy2K ):

rename fgh jkl fgh*

Вы должны проверить страницу руководства вашей платформы, чтобы увидеть, что из вышеперечисленного применимо.


7
+1 Даже не знал о переименовании ... Теперь я могу прекратить использовать цикл for с mv и sed ... Спасибо!
Балфа

2
Вы ссылаетесь на другой, renameтогда вы показываете синтаксис для unixhelp.ed.ac.uk/CGI/man-cgi?rename, другой
Hasturkun

7
Не присутствует во всех * nix системах. Не на Max OS X для одного, и нет пакета в fink, чтобы получить его. Не смотрел на 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".
Дейв Шерохман

@ Стефан, Это была опечатка с моей стороны (исправил это).
Ник

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.
Halil

ххх обозначает замену? в случае оригинального плаката это был бы "jkl"
Awinad

1
Самое чистое решение из всех.
MT Head

3
Возможно, еще раз подчеркнем, что это расширение Bash, которого нет ни в POSIX, shни в других оболочках.
tripleee

Сладкий - в мире Unix так много непонятных уголков - такого еще никогда не видел.
wcochran

40

Используя mmv :

mmv "fgh*" "jkl#1"

1
Вау, это отличный и простой способ решения проблемы! Рад быть представленным mmv, спасибо!
Хендека

1
Спасибо!!! Никогда раньше не слышал о MMV. Просто установил и играл с ним - просто, мощно.
Ник Райс

3
если вы хотите пакетное переименование рекурсивно использовать ;в сочетании с #1. пример:mmv ";fgh*" "#1jkl#2"
Alp

Очень элегантное решение!
Маленькая ученица Ферма

1
Именно то, что мне было нужно. Захватывайте группы с помощью ' ' и перезванивайте их с помощью # 1, # 2, ... EX: mmv "my show ep 1080p. *" "My.show. # 1. # 2" = my.show.001.avi
Ланди

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, которая является самой распространенной системой 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, какая у тебя машина?
Ник

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

Есть два переименования, как указано в комментариях в ответе Stephan202. Дистрибутивы на основе 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

На Солярисе вы можете попробовать:

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и никакое количество квотирования внутри цикла поможет (или даже необходимо).
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

Этот скрипт работал для меня для рекурсивного переименования с каталогами / именами файлов, которые могут содержать пробелы:

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

и вуаля! Все переименование осуществляется с помощью фильтра.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я являюсь соавтором этой работы, созданной для академических целей. Возможно, скоро появится функция создания bash.


1

Это было намного проще (на моем Mac) сделать это в Ruby. Вот 2 примера:

# 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 :

$ 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

Это великолепно нарушает имена файлов, содержащие пробелы, кавычки или другие метасимволы оболочки.
tripleee

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
Обратите внимание, что ответы , содержащие только ссылки, не приветствуются, поэтому ответы SO должны быть конечной точкой поиска решения (в отличие от еще одной остановки ссылок, которая, как правило, устаревает со временем). Пожалуйста, рассмотрите возможность добавления отдельного краткого обзора здесь, сохраняя ссылку в качестве ссылки.
Клеопатра

По прогнозу @kleopatra , ссылка полученаstale over time
y2k-shubham

1
@ y2k-shubham, больше нет.
Андре фон Кугланд

0

Это сработало для меня с помощью регулярных выражений:

Я хотел, чтобы файлы были переименованы так:

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

usig регулярное выражение [az | _] + 0 * ([0-9] +. ), где ([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.