如何使用前缀/后缀重命名?

137

如何在不重新输入原始文件名的情况下执行mv original.filename new.original.filename操作?

我想象中可以使用类似于mv -p=new. original.filename或者mv original.filename new.~之类的命令,但是在查看了man mv / info mv页面后,我并没有找到这样的选项。

当然,我可以编写一个shell脚本来实现这一点,但是否有现成的命令/标志可用呢?

11个回答

199

您可以使用rename(1)命令:

rename 's/(.*)$/new.$1/' original.filename

编辑:如果rename不可用且您必须重命名多个文件,则对于此操作,shell脚本可以非常简短和简单。例如,在当前目录中将所有*.jpg重命名为prefix_*.jpg

for filename in *.jpg; do mv "$filename" "prefix_${filename}"; done;

同时,还可以借鉴Dave Webb的答案,并使用花括号展开:

for filename in *.jpg; do mv {,prefix_}"$filename"; done;

上周末安装了Graphite,他们有一个步骤基本上是(“将所有*.example文件复制到conf文件夹中,删除示例后缀”)。这在他们那里真的很懒,但是rename 's/(.*).example/$1/' *.example让我免受了乏味的工作。 - Conrad.Dean
2
到目前为止,这是最好的答案。 - lifeisfoo
2
我认为在这种情况下您不需要第一个 $。所以只需使用 's/(.*)/new.$1/' - wisbucky
1
“rename” 命令在我的 RHEL 6 机器上无法工作。是否有任何不同版本的此命令? “for ... done” 对我有用。 - robsch
对于那些寻找后缀版本的人:for filename in *.jpg; do mv "$filename" "${filename}_suffix"; done; - basZero
显示剩余3条评论

144
在Bash和zsh中,您可以使用花括号扩展(Brace Expansion)来实现此操作。这只是在括号中扩展项目列表。例如:

In Bash 和 zsh ,你可以使用花括号扩展来实现这个功能。这个方法只是将花括号中的项目进行扩展。例如:

# echo {vanilla,chocolate,strawberry}-ice-cream
vanilla-ice-cream chocolate-ice-cream strawberry-ice-cream

那么您可以按照以下方式进行重命名:

mv {,new.}original.filename

因为这会扩展为:

mv original.filename new.original.filename

谢谢,这看起来是最好的解决方案 - 也适用于ksh。 - Peter Boughton
32
这个命令无法使用通配符。也就是说,在包含文件名a、b、c的目录中,使用“echo {,new.}*”会生成列表:“a b c new.*”,因为在扩展通配符字符之前,通过大括号扩展生成文件名(至少可以这样推断)。请注意,这并不是通配符的效果。 - Jonathan Leffler
1
几个月来,我一直在寻找只使用基本 shell 功能的解决方案... 我永远不会想到这么简单的方法,非常感谢! - flonk
谢谢您提供大括号扩展的参考资料,这正是我在寻找的! - BitfulByte
1
我喜欢这个例子。现在我感觉想在午休时间去Baskin Robbins。 - Sridhar Sarnobat
显示剩余3条评论

26

通过创建for循环,您可以实现对多个Unix兼容文件的重命名(使用通配符):

for file in *; do
  mv $file new.${file%%}
done

7
这个问题和 Simon Lehmann 已经回答的问题 有什么区别? - Peter Boughton
4
这很好用。${file%%}是什么意思?看起来和${file}差不多。 - Sidmitra
我也对 ${file%%} 感到好奇。 - Lucas Bustamante
2
这里的 %% 看起来对我来说像是一个错误。 - Asteroids With Wings

10

我注意到有人提到了一个rename命令,但这在Unix系统上并不常见(相较于Linux系统或Cygwin)。在前者中,rename是一个脚本而不是可执行文件。那个版本的rename功能相当有限:

rename from to file ...

它将文件名中的from部分替换为to,man页面中给出的示例是:

rename foo foo0 foo? foo??

这将把foo1重命名为foo01,将foo10重命名为foo010等。

我使用一个名为rename的Perl脚本,最初是从1992年左右的第一版Camel书中挖掘出来的,然后进行了扩展以重命名文件。

#!/bin/perl -w
#
# @(#)$Id: rename.pl,v 1.7 2008/02/16 07:53:08 jleffler Exp $
#
# Rename files using a Perl substitute or transliterate command

use strict;
use Getopt::Std;

my(%opts);
my($usage) = "Usage: $0 [-fnxV] perlexpr [filenames]\n";
my($force) = 0;
my($noexc) = 0;
my($trace) = 0;

die $usage unless getopts('fnxV', \%opts);

if ($opts{V})
{
    printf "%s\n", q'RENAME Version $Revision: 1.7 $ ($Date: 2008/02/16 07:53:08 $)';
    exit 0;
}
$force = 1 if ($opts{f});
$noexc = 1 if ($opts{n});
$trace = 1 if ($opts{x});

my($op) = shift;
die $usage unless defined $op;

if (!@ARGV) {
    @ARGV = <STDIN>;
    chop(@ARGV);
}

for (@ARGV)
{
    if (-e $_ || -l $_)
    {
        my($was) = $_;
        eval $op;
        die $@ if $@;
        next if ($was eq $_);
        if ($force == 0 && -f $_)
        {
            print STDERR "rename failed: $was - $_ exists\n";
        }
        else
        {
            print "+ $was --> $_\n" if $trace;
            print STDERR "rename failed: $was - $!\n"
                unless ($noexc || rename($was, $_));
        }
    }
    else
    {
        print STDERR "$_ - $!\n";
    }
}

这样可以让您编写任何Perl替换或转换命令来映射文件名。在所请求的具体示例中,您将使用:

rename 's/^/new./' original.filename

谢谢,如果我可以接受第二个答案的话,这就是它了。我想要一个标准命令,可以在任何我可能使用的东西上正常工作,但我可以将此脚本放在我经常使用的机器上,它仍然会很有帮助。 - Peter Boughton
James Wong指出,对于Arch Linux,您可以通过使用以下命令在pacman中下载和安装此脚本:pacman -Sy perl-rename。然后在您的bashrc中添加一个别名: alias rename='perl-rename' - Jonathan Leffler
如果“from”或“to”是空字符串,即您想将某些内容添加到每个文件名或完全删除某些内容,该怎么办? - Troyseph
1
@Troyseph — 要在文件名后添加'.html',请使用's/$/.html/' — 要从文件名中的任何位置删除'tmp',请使用's/tmp//g'(查找'tmp'并在所有地方替换为空)。 - Jonathan Leffler

9

我知道这里有很好的答案,但我没有找到处理文件名扩展名并添加后缀的参考。

我需要在文件扩展名之前为文件夹中的所有wav文件添加“_en”后缀。

关键是这里:%.*

for filename in *.wav; do mv $filename ${filename%.*}_en.wav; done;

如果您需要处理不同的文件扩展名,请查看答案。这个有点不太直观。


8

在目录中批量重命名文件的最简单方法是:

ls | xargs -I fileName mv fileName fileName.suffix

2
如果可以修改文件名,你可以使用后缀代替前缀。然后你就可以使用制表符补全来获取原始文件名并添加后缀。
否则,mv命令不支持此功能。但是一个简单的shell脚本可以解决这个问题。

通常我都需要两者,但主要是前缀 - 就像你所说的,制表符自动完成可以处理后缀。 - Peter Boughton

2
我不喜欢其他答案。
我的看法是你想要一个纯粹的bash解决方案。所以这是你需要的!
在使用数千个文件之前,请先尝试使用echo查看会发生什么!
如果您想要放置前缀,请删除'./'
find . -type f -exec bash -c 'echo prefix_${0#./}' {} \;
在提问中,prefix_new.因此命令变为
find . -type f -exec bash -c 'echo mv $0 new.${0#./}' {} \;
如果您想在扩展名后添加内容,请删除扩展名
find . -type f -exec bash -c 'echo ${0%.*}_suffix.${0##*.}' {} \;
现在,只需将echo替换为mv $0即可重命名。
示例:
find . -type f -exec bash -c 'mv $0 1.${0#./}' {} \; 受以下启发: https://gist.github.com/larshaendler/723d2ec447c3c694b71dc6e3bc3aadc9

这个答案对我很有帮助。注意:对于前缀示例,您运行的命令是针对所有文件的,并且不检查只影响预期文件。我的意思是,您可能需要添加一个额外的检查-name 'original*',像这样:find . -type f -name 'original*' -exec bash -c 'echo mv $0 new.${0#./}' {} \; - Henke

1
批量重命名文件的Bash脚本。
#!/bin/bash
# USAGE: cd FILESDIRECTORY; RENAMERFILEPATH/MultipleFileRenamer.sh FILENAMEPREFIX INITNUMBER
# USAGE EXAMPLE: cd PHOTOS; /home/Desktop/MultipleFileRenamer.sh 2016_
# VERSION: 2016.03.05.
# COPYRIGHT: Harkály Gergő | mangoRDI (https://wwww.mangordi.com/) 

# check isset INITNUMBER argument, if not, set 1 | INITNUMBER is the first number after renaming
if [ -z "$2" ]
    then i=1;
else
    i=$2;
fi

# counts the files to set leading zeros before number | max 1000 files
count=$(ls -l * | wc -l)
if [ $count -lt 10 ]
    then zeros=1;
else
    if [ $count -lt 100 ]
        then zeros=2;
    else
        zeros=3
    fi
fi

# rename script
for file in *
do
    mv $file $1_$(printf %0"$zeros"d.%s ${i%.*} ${file##*.})
    let i="$i+1"
done

1
在我的情况下,我有一组文件需要在我能够使用它们之前进行重命名。每个文件在组中都有自己的角色和自己的模式。
因此,我有一个像这样的重命名命令列表:
f=`ls *canctn[0-9]*`   ;  mv $f  CNLC.$f
f=`ls *acustb[0-9]*`   ;  mv $f  CATB.$f
f=`ls *accusgtb[0-9]*` ;  mv $f  CATB.$f
f=`ls *acus[0-9]*`     ;  mv $f  CAUS.$f

尝试这个:

也可以试试这个:

f=MyFileName; mv $f {pref1,pref2}$f{suf1,suf2}

这将生成所有带有前缀和后缀的组合:
pref1.MyFileName.suf1
...
pref2.MyFileName.suf2

解决同样问题的另一种方法是创建映射数组,并为每个文件类型添加相应的前缀,如下所示:

#!/bin/bash
unset masks
typeset -A masks
masks[ip[0-9]]=ip
masks[iaf_usg[0-9]]=ip_usg
masks[ipusg[0-9]]=ip_usg
...
for fileMask in ${!masks[*]}; 
do  
registryEntry="${masks[$fileMask]}";
fileName=*${fileMask}*
[ -e ${fileName} ] &&  mv ${fileName} ${registryEntry}.${fileName}  
done

据我所知,f=\ls *canctn[0-9]*` ; mv $f CNLC.$f` 只在匹配单个文件时起作用。 - icc97
这是正确的。为了避免这种情况,您可以使用find命令:<code> find * -maxdepth 1 -type f -name "canctn[0-9]" -exec echo mv {} CNLC.$(basename {}) ; </code>但是,对于一个紧凑的重复任务来说,这个命令太大了。 - aprodan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接