目录中有多个文件以前缀fgh开头,例如:

fghfilea
fghfileb
fghfilec


我想重命名所有文件以前缀jkl开头。是否有一个命令可以执行此操作,而不是单独重命名每个文件?

评论

在这里检查:theunixshell.blogspot.com/2013/01/…

相关问题:基于多种模式重命名文件的更好方法。

#1 楼

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

使用rename的一个版本:

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


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

rename fgh jkl fgh*


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

评论


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

– balpha
09年7月6日在11:27

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

– Hasturkun
09年7月6日在11:39

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

– dmckee ---前主持人小猫
09年7月6日在16:07

AFAICT,重命名似乎是Linux特定的脚本或实用程序。如果您完全关心可移植性,请继续使用sed和loops或嵌入式Perl脚本。

–肖利
09年12月25日在16:55

在OS X上brew install重命名:)

–山姆
13年4月29日在21:33

#2 楼

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


评论


很接近。请注意,您只想匹配首次出现的fgh:'s / ^ fgh / jkl / g'(脱字符号使所有内容有所不同)。

– Stephan202
09年7月6日在11:53

只是为了精确起见……您的意思是“名称开头的fgh”,而不是“ fgh的首次出现”。 / ^ fgh /将匹配“ fghi”,但不匹配“ efgh”。

–戴夫·谢罗曼(Dave Sherohman)
09年7月6日在12:12

@Stephan,那是我的错字(已修正)。

– nik
09年7月6日在12:29

如果您无权访问“重命名”,则效果很好。如果您的文件名包含空格,则该代码可能需要使用引号。为f in fgh *;做mv“ $ f”“ $(echo $ f | sed's / ^ fgh / jkl / g')”;完成

–戴夫·尼尔森(Dave Nelson)
2011年7月13日14:24



@nik如果不使用引号重命名此文件列表,则会引发错误:触摸fghfilea fghfileb fghfilec fghfile \ d。我建议考虑@DaveNelson的言论。

–luissquall
2014年6月2日21:09

#3 楼

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

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


评论


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

–哈利尔
16 Sep 24 '16:47

xxx代表更换?如果是原始海报,则应为“ jkl”

– Awinad
17年6月21日在20:09

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

– M T Head
17年8月2日在17:16

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

–tripleee
18年8月22日在4:46

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

– wcochran
19年8月8日在17:29

#4 楼

使用mmv:

mmv "fgh*" "jkl#1"


评论


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

–亨德卡
2014年5月21日,0:52

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

–尼克·赖斯
2014-09-10 14:09

如果要批量递归重命名,请使用;结合#1。例如:mmv“; fgh *”“#1jkl#2”

–阿尔卑斯山
2015年4月12日在16:14

非常优雅的解决方案!

–费马的小学生
15年4月29日在15:03

正是我所需要的。用''捕获组,然后用#1,#2,...回叫它们:EX:mmv“ my show ep 1080p。*”“ my.show。#1.#2” = my.show.001.avi

–丰足
15年8月16日在0:57

#5 楼

有很多方法可以做到(并非所有这些方法都可以在所有的unixy系统上使用):



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

§可以替换通过任何您认为方便的方式。您也可以使用find -exec来执行此操作,但是在许多系统上的行为都略有不同,因此我通常会避免使用



粗略但有效,正如他们所说
/>

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

真正漂亮,但在BSD上不存在重命名,这是最常见的unix系统afaik。

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

rename fgh jkl fgh*

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


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

评论


我喜欢这种怪物!

– lefakir
13年7月30日在19:54

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

– iwein
13年7月31日在11:09

请注意,不应解析和/或通过管道输出ls输出

–phuclv
18年1月25日在9:54



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

– mlerley
18年1月30日,0:34

#6 楼

rename fgh jkl fgh*


评论


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

– Stephan202
09年7月6日在11:32

@ Stephan202,您的电脑是什么?

– nik
09年7月6日在11:33

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

– Stephan202
09年7月6日在11:38

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

–人名
16年8月14日在4:19

@whossname:对不起,真的不记得了。 (由于假期而回复缓慢。)

– Stephan202
16年8月22日在19:37

#7 楼

使用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。

评论


我本来要发布这样的内容,但是要花上一个小时才能得到正确的命令

–胡安·门德斯(Juan Mendes)
2014年6月2日22:06

这个答案所允许的递归使得它确实很强大-我只是在一个深层嵌套的层次结构中快速重命名了数千个文件。

–kevinmicke
10月28日17:05

#8 楼

要安装Perl重命名脚本:

sudo cpan install File::Rename


Stephan202的答案中的注释中提到了两个重命名。基于Debian的发行版具有Perl重命名。 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 楼

在Solaris上,您可以尝试:

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


评论


在循环中引用文件名可以得到半分,但是对于$(find)中的文件从根本上来说是有缺陷的,无法通过引用进行更正。如果find返回带有空格的./file name,您将在./file、name、with、space上得到一个for循环,而循环内的大量引用都无济于事(甚至是必要的)。

–tripleee
18年8月22日在4:48

#11 楼

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


#12 楼

该脚本为我进行了递归重命名,其中包含可能包含空格的目录/文件名:

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表达式在此示例中将所有出现的;替换为空格。当然,应根据特定需要将其替换。

#13 楼

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

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


它首先基于示例计算过滤器,其中输入是文件名,输出是(可以,也可以,任意字符串)。如果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生成功能。

#14 楼

(在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


#15 楼

使用重命名器:

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


一旦满意输出就删除--dry-run标志。

#16 楼

我重命名海量文件的版本:

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


评论


我喜欢在运行脚本之前进行验证的功能

–ardochhigh
15年7月4日在15:52

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

–tripleee
18年8月22日在4:53

#17 楼

另一个可能的参数扩展:

 for f in fgh*; do mv -- "$f" "jkl${f:3}"; done
 


#18 楼

我建议使用自己的脚本来解决此问题。它还具有用于更改文件名的编码以及将组合的变音符号转换为预组合字符的选项,这是我从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);
  }
}


评论


请注意,不鼓励仅链接的答案,因此,SO答案应该是搜索解决方案的终点(与引用的另一种中途停留相比,随着时间的流逝,它们往往会过时)。请考虑在此处添加独立的摘要,并保留该链接作为参考。

– kleopatra
13年11月1日在11:45

正如@kleopatra所预测的那样,随着时间的推移,该链接已过时

–y2k-shubham
19年1月20日在10:44

@ y2k-shubham,现在不再。

–安德烈·冯·库格兰(Andrévon Kugland)
19年1月21日在13:56



#19 楼

这对我来说使用regexp起作用:

我希望将文件重命名为:

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


使用[az | _] + 0 *([0-9] +。)regexp其中([0-9] +。)是要在重命名命令上使用的组子字符串。

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


产生:

file001abc.txt -> abc1.txt
ofile0002abcd.txt -> abcd2.txt 

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


另一个例子: />
警告,请小心。

#20 楼

我写了这个脚本来搜索所有.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;


#21 楼

在文件列表上运行sed表达式的通用脚本(将sed解决方案与rename解决方案结合在一起):

#!/bin/sh

e=
shift

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


通过向脚本传递sed表达式进行调用,然后是任何文件列表,就像rename的版本一样:

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


#22 楼

您也可以使用以下脚本。在终端上运行非常容易...

//一次重命名多个文件

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