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

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

fghfilea
fghfileb
fghfilec

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

Ответ 1

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

Использование одной версии rename:

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

Использование другой версии rename (аналогично ответу Judy2K):

rename fgh jkl fgh*

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

Ответ 2

Вот как 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

Ответ 3

переименование может не быть в каждой системе. поэтому, если у вас его нет, используйте оболочку этот пример в bash shell

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

Ответ 4

Используя mmv:

mmv "fgh*" "jkl#1"

Ответ 5

Существует много способов сделать это (не все из них будут работать на всех системах 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 afaik.

  • rename fgh jkl fgh*

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

    Если вы настаиваете на использовании Perl, но в вашей системе нет переименования, вы можете использовать этого монстра.

Некоторые из них немного запутаны, и список далеко не завершен, но вы найдете то, что вы хотите здесь, для почти всех систем unix.

Ответ 6

rename fgh jkl fgh*

Ответ 7

Используя 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.

Ответ 8

Чтобы установить Perl rename script:

sudo cpan install File::Rename

Есть два переименования, как указано в комментариях в ответе Stephan202. На дистрибутивах на Debian есть Perl rename. В дистрибутивах Redhat/rpm есть переименовать C.
OS X не имеет установленного по умолчанию (по крайней мере, в 10.8), и Windows/Cygwin.

Ответ 9

Здесь можно сделать это с помощью командной строки Groovy:

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

Ответ 10

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

Ответ 11

В Solaris вы можете попробовать:

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

Ответ 12

Используя renamer:

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

Удалите флаг --dry-run только вы будете довольны, результат будет правильным.

Ответ 13

Я бы порекомендовал использовать свой собственный скрипт, который решает эту проблему. У него также есть опции для изменения кодировки имен файлов и для преобразования комбинированных диакритических знаков в заранее составленные символы, проблема, с которой я всегда сталкиваюсь, когда копирую файлы с моего 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);
  }
}

Ответ 14

Используя 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 -producing.

Ответ 15

Было намного проще (на моем 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

Ответ 16

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

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

Ответ 17

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

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, которое в этом примере заменяет все вхождения ; пробелом . Это, конечно, должно быть заменено в соответствии с конкретными потребностями.

Ответ 18

Это работало для меня с использованием regexp:

Мне хотелось, чтобы файлы были переименованы следующим образом:

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

usig the [a-z | _] + 0 * ([0-9] +.) regexp где ([0-9] +.) является групповой подстрокой для использования при команде rename

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 

Предупреждение. Будьте осторожны.

Ответ 19

Я написал этот script для поиска всех файлов .mkv, рекурсивного переименования найденных файлов в .avi. Вы можете настроить его на свои needs. Я добавил некоторые другие вещи, такие как получение каталога файлов, расширение, имя файла из пути к файлу, только если вы хотите что-то сказать в будущем.

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;

Ответ 20

Общий script для запуска выражения sed в списке файлов (объединяет sed решение с rename:

#!/bin/sh

e=$1
shift

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

Вызвать, передав выражение script a sed, а затем любой список файлов, как версия rename:

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

Ответ 21

Вы также можете использовать ниже script. его очень легко запустить на терминале...

//Переименование нескольких файлов за раз

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