根据Unix中的模式重命名多个文件


248

目录中存在多个以prefix开头的文件fgh,例如:

fghfilea
fghfileb
fghfilec

我想重命名所有它们以prefix开头jkl。是否有一个命令可以执行此操作,而不是分别重命名每个文件?



Answers:


289

有几种方法,但是使用rename可能是最简单的方法。

使用以下版本rename

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

使用另一个版本rename(与Judy2K的答案相同):

rename fgh jkl fgh*

您应该检查平台的手册页,以查看以上哪一项适用。


7
+1甚至都不知道重命名...现在,我可以停止在mv和sed中使用for循环...谢谢!
balpha

2
您链接到其他链接,rename然后显示unixhelp.ed.ac.uk/CGI/man-cgi?的语法?另一个是重命名
Hasturkun

7
并非在所有* nix系统上都存在。不在Max OS X上使用,并且没有粉红色的包装可以得到它。没看过MacPorts。
dmckee ---前主持人小猫,

6
AFAICT,rename似乎是Linux特定的脚本或实用程序。如果您完全关心可移植性,请继续使用sed和循环或内联Perl脚本。
D.Shawley

32
brew install rename在OS X上:)
sam

117

这是方法sedmv可以一起使用来进行重命名:

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'(脱字符号使所有内容有所不同)。
09年

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 2014年

75

重命名可能不在每个系统中。因此,如果没有它,请在bash shell中使用此示例的shell

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

1
在此解决方案中,sed不需要命令。这比@nik的答案更简单。
Halil

xxx代表更换?如果是原始海报,那应该是“ jkl”
Awinad

1
所有这些中最干净的解决方案。
MT头

3
也许更强调指出,这是Bash扩展,在POSIX sh或其他Shell中通常不存在。
人间

亲爱的-Unix世界有很多晦涩的角落-以前从未见过。
wcochran

40

使用mmv

mmv "fgh*" "jkl#1"

1
哇,这是解决问题的极好简单的方法!很高兴介绍给mmv,谢谢!
Hendeca 2014年

1
谢谢!!!以前从未听说过mmv。刚安装并使用它-简单,功能强大。
尼克·赖斯

3
如果要批量重新命名;,请与结合使用#1。例如:mmv ";fgh*" "#1jkl#2"
Alp 2015年

非常优雅的解决方案!
费马的小学生

1
正是我需要的。用' '捕获组,并用#1,#2等回叫它们:例如: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上不存在,这是最常见的Unix系统afaik。

  • rename fgh jkl fgh*

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

    如果您坚持使用Perl,但系统上没有重命名,则可以使用此怪物。

其中一些有点令人费解,并且列表还很不完整,但是您将在这里找到几乎所有unix系统所需的内容。


2
我喜欢这种怪物!
lefakir

是的,我对perl仍然有一个弱点:)
iwein


如果文件名中有空格,则Perl怪兽可以正常工作。
mlerley

13
rename fgh jkl fgh*

6
在我的机器上,这会产生错误“在(eval 1)第1行中使用“ strict subs”时,不允许使用字词“ fgh”。”
09年

@ Stephan202,您的电脑是什么?
NIK

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

@ Stephan202我有同样的问题,您解决了吗?(7年后)
whossname

1
@whossname:对不起,真的不记得了。(由于节假日,回复缓慢。)
Stephan202 '16

8

使用findxargssed

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调用shell


1
我本来要发布这样的内容,但是
Juan Mendes 2014年


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在循环./filenamewith,和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在此示例;中用space 替换所有出现的表达式。当然,应根据特定需要将其替换。


非常感谢 !
出色的

1

使用通过示例处理的StringSolver工具(Windows和Linux bash):

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

它首先根据examples来计算一个过滤器,其中输入是文件名,输出是(ok和notok,任意字符串)。如果filter具有--auto选项或在此命令后单独调用,它将创建一个文件夹ok和一个文件夹notok并将文件分别推送到它们。

然后使用过滤器,该mv命令是半自动移动,使用修饰符--auto变为自动。使用前面的过滤器由于--filter,发现来自映射fghfileajklfilea,然后它适用于所有过滤的文件。


其他一线解决方案

其他等效的方式(每行等效),因此您可以选择自己喜欢的方式。

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中执行此操作要容易得多。这里有两个例子:

# 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 2015年

这在包含空格,引号或其他shell元字符的文件名上会造成严重损坏。
人间

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

正如@kleopatra所预测的,链接已获得stale over time
y2k-shubham


0

这对我使用正则表达式工作:

我想要这样重命名文件:

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

使用[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解决方案renamesolution结合使用):

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.