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

发布于 2024-07-27 01:13:26 字数 173 浏览 6 评论 0原文

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

fghfilea
fghfileb
fghfilec

我想将它们全部重命名为以前缀 jkl 开头。 是否有一个命令可以做到这一点,而不是单独重命名每个文件?

There are multiple files in a directory that begin with prefix fgh, for example:

fghfilea
fghfileb
fghfilec

I want to rename all of them to begin with prefix jkl. Is there a single command to do that instead of renaming each file individually?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(27

飞烟轻若梦 2024-08-03 01:13:26

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

使用 renamePerl 的重命名):

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

使用另一个版本的重命名 (与 Judy2K 的回答):

rename fgh jkl fgh*

您应该检查您平台的手册页以了解上述哪一项适用。

There are several ways, but using rename will probably be the easiest.

Using one version of rename (Perl's rename):

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

Using another version of rename (same as Judy2K's answer):

rename fgh jkl fgh*

You should check your platform's man page to see which of the above applies.

丑丑阿 2024-08-03 01:13:26

这就是 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

This is how sed and mv can be used together to do rename:

for f in fgh*; do mv "$f" $(echo "$f" | sed 's/^fgh/jkl/g'); done

As per comment below, if the file names have spaces in them, quotes may need to surround the sub-function that returns the name to move the files to:

for f in fgh*; do mv "$f" "$(echo $f | sed 's/^fgh/jkl/g')"; done
我不是你的备胎 2024-08-03 01:13:26

重命名可能并不适用于每个系统。 所以如果你没有它,请使用 shell
这个例子在 bash shell 中

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

rename might not be in every system. so if you don't have it, use the shell
this example in bash shell

for f in fgh*; do mv "$f" "${f/fgh/xxx}";done
定格我的天空 2024-08-03 01:13:26

使用 mmv

mmv "fgh*" "jkl#1"

Using mmv:

mmv "fgh*" "jkl#1"
Spring初心 2024-08-03 01:13:26

有很多方法可以做到这一点(并非所有这些方法都适用于所有 unixy 系统):

  • ls | 切-c4- | xargs -I§ mv fgh§ jkl§

    § 可以替换为您认为方便的任何内容。 您也可以使用 find -exec 来完成此操作,但在许多系统上其行为略有不同,因此我通常会避免这样做

  • for f in fgh*; do mv "$f" "${f/fgh/jkl}";完成

    正如他们所说,粗鲁但有效

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

    非常漂亮,但是 rename 在 BSD 上不存在,据我所知,BSD 是最常见的 unix 系统。

  • 重命名 fgh jkl fgh*

  • ls | perl -ne 'chomp; 接下来除非-e; $o = $_; s/fgh/jkl/; 下一个如果-e; 重命名 $o, $_';

    如果你坚持使用Perl,但你的系统上没有重命名,你可以使用这个怪物。

其中一些有点复杂,列表还远未完成,但您会在这里找到几乎所有 UNIX 系统所需的内容。

There are many ways to do it (not all of these will work on all unixy systems):

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

    The § may be replaced by anything you find convenient. You could do this with find -exec too but that behaves subtly different on many systems, so I usually avoid that

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

    Crude but effective as they say

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

    Real pretty, but rename is not present on BSD, which is the most common unix system afaik.

  • rename fgh jkl fgh*

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

    If you insist on using Perl, but there is no rename on your system, you can use this monster.

Some of those are a bit convoluted and the list is far from complete, but you will find what you want here for pretty much all unix systems.

烂柯人 2024-08-03 01:13:26
rename fgh jkl fgh*
rename fgh jkl fgh*
夏有森光若流苏 2024-08-03 01:13:26

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

Using find, xargs and sed:

find . -name "fgh*" -type f -print0 | xargs -0 -I {} sh -c 'mv "{}" "$(dirname "{}")/`echo $(basename "{}") | sed 's/^fgh/jkl/g'`"'

It's more complex than @nik's solution but it allows to rename files recursively. For instance, the structure,

.
├── fghdir
│   ├── fdhfilea
│   └── fghfilea
├── fghfile\ e
├── fghfilea
├── fghfileb
├── fghfilec
└── other
    ├── fghfile\ e
    ├── fghfilea
    ├── fghfileb
    └── fghfilec

would be transformed to this,

.
├── fghdir
│   ├── fdhfilea
│   └── jklfilea
├── jklfile\ e
├── jklfilea
├── jklfileb
├── jklfilec
└── other
    ├── jklfile\ e
    ├── jklfilea
    ├── jklfileb
    └── jklfilec

The key to make it work with xargs is to invoke the shell from xargs.

鼻尖触碰 2024-08-03 01:13:26

通用命令为

find /path/to/files -name '*' -exec bash -c 'mv $0 ${0//}' {} \;

其中 应分别替换为您的源和目标。

作为针对您的问题量身定制的更具体的示例(应从文件所在的同一文件夹运行),上述命令如下所示:

find 。 -name 'gfh*' -exec bash -c 'mv $0 ${0/gfh/jkl}' {} \;

对于“试运行”,请在 之前添加 echo mv,这样您就可以看到生成了哪些命令:

find 。 -name 'gfh*' -exec bash -c 'echo mv $0 ${0/gfh/jkl}' {} \;

Generic command would be

find /path/to/files -name '<search>*' -exec bash -c 'mv $0 ${0/<search>/<replace>}' {} \;

where <search> and <replace> should be replaced with your source and target respectively.

As a more specific example tailored to your problem (should be run from the same folder where your files are), the above command would look like:

find . -name 'gfh*' -exec bash -c 'mv $0 ${0/gfh/jkl}' {} \;

For a "dry run" add echo before mv, so that you'd see what commands are generated:

find . -name 'gfh*' -exec bash -c 'echo mv $0 ${0/gfh/jkl}' {} \;

谜兔 2024-08-03 01:13:26

要安装 Perl rename 脚本:

sudo cpan install File::Rename

两个重命名,如 Stephan202 的回答中的评论中所述。
基于 Debian 的发行版具有 Perl 重命名。 Redhat/rpm 发行版具有 C 重命名
OS X 默认情况下没有安装(至少在 10.8 中),Windows/Cygwin 也没有。

To install the Perl rename script:

sudo cpan install File::Rename

There are two renames as mentioned in the comments in Stephan202's answer.
Debian based distros have the Perl rename. Redhat/rpm distros have the C rename.
OS X doesn't have one installed by default (at least in 10.8), neither does Windows/Cygwin.

2024-08-03 01:13:26

以下是使用命令行 Groovy 执行此操作的方法:

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

Here's a way to do it using command-line Groovy:

groovy -e 'new File(".").eachFileMatch(~/fgh.*/) {it.renameTo(it.name.replaceFirst("fgh", "jkl"))}'
淤浪 2024-08-03 01:13:26

在 Solaris 上您可以尝试:

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

On Solaris you can try:

for file in `find ./ -name "*TextForRename*"`; do 
    mv -f "$file" "${file/TextForRename/NewText}"
done
落在眉间の轻吻 2024-08-03 01:13:26
#!/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
#!/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
盗琴音 2024-08-03 01:13:26

该脚本适用于对可能包含空格的目录/文件名进行递归重命名:

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 表达式,在此示例中,它用空格 替换所有出现的 ; >。 当然这个要根据具体需要来更换。

This script worked for me for recursive renaming with directories/file names possibly containing white-spaces:

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

Notice the sed expression which in this example replaces all occurrences of ; with space . This should of course be replaced according to the specific needs.

安穩 2024-08-03 01:13:26

使用 StringSolver 工具(windows 和 Linux bash)通过示例进行处理:

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

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

Using StringSolver tools (windows & Linux bash) which process by examples:

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

It first computes a filter based on examples, where the input is the file names and the output (ok and notok, arbitrary strings). If filter had the option --auto or was invoked alone after this command, it would create a folder ok and a folder notok and push files respectively to them.

Then using the filter, the mv command is a semi-automatic move which becomes automatic with the modifier --auto. Using the previous filter thanks to --filter, it finds a mapping from fghfilea to jklfilea and then applies it on all filtered files.


Other one-line solutions

Other equivalent ways of doing the same (each line is equivalent), so you can choose your favorite way of doing it.

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"

Multi-step solution

To carefully find if the commands are performing well, you can type the following:

filter fghfilea ok
filter fghfileb ok
filter fghfileb notok

and when you are confident that the filter is good, perform the first move:

mv fghfilea jklfilea

If you want to test, and use the previous filter, type:

mv --test --filter

If the transformation is not what you wanted (e.g. even with mv --explain you see that something is wrong), you can type mv --clear to restart moving files, or add more examples mv input1 input2 where input1 and input2 are other examples

When you are confident, just type

mv --filter

and voilà! All the renaming is done using the filter.

DISCLAIMER: I am a co-author of this work made for academic purposes. There might also be a bash-producing feature soon.

梦纸 2024-08-03 01:13:26

在 Ruby 中(在我的 Mac 上)执行此操作要容易得多。 这里有 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

It was much easier (on my Mac) to do this in Ruby. Here are 2 examples:

# 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
听不够的曲调 2024-08-03 01:13:26

使用 renamer

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

删除 --dry-run 标志很高兴输出看起来正确。

Using renamer:

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

Remove the --dry-run flag once you're happy the output looks correct.

李不 2024-08-03 01:13:26

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

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

My version of renaming mass files:

for i in *; do
    echo "mv $i $i"
done |
sed -e "s#from_pattern#to_pattern#g” > result1.sh
sh result1.sh
情深如许 2024-08-03 01:13:26

另一种可能的参数扩展

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

Another possible parameter expansion:

for f in fgh*; do mv -- "$f" "jkl${f:3}"; done
|煩躁 2024-08-03 01:13:26

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

I would recommend using my own script, which solves this problem. It also has options to change the encoding of the file names, and to convert combining diacriticals to precomposed characters, a problem I always have when I copy files from my 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);
  }
}
半世晨晓 2024-08-03 01:13:26

这对我使用正则表达式有用:

我希望像这样重命名文件:

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

Produces:

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

Produces:

  mv file001abc.txt abc1.txt
  mv ofile0002abcd.txt abcd2.txt 

警告,小心。

This worked for me using regexp:

I wanted files to be renamed like this:

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

usig the [a-z|_]+0*([0-9]+.) regexp where ([0-9]+.) is a group substring to use on the rename command

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

Produces:

mv file0001.txt 1.txt
mv ofile0002.txt 2.txt
mv f_i_l_e0003.txt 3.txt

Another example:

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

Produces:

  mv file001abc.txt abc1.txt
  mv ofile0002abcd.txt abcd2.txt 

Warning, be careful.

红衣飘飘貌似仙 2024-08-03 01:13:26

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

I wrote this script to search for all .mkv files recursively renaming found files to .avi. You can customize it to your neeeds. I've added some other things such as getting file directory, extension, file name from a file path just incase you need to refer to something in the future.

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;
心如狂蝶 2024-08-03 01:13:26

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

#!/bin/sh

e=$1
shift

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

通过向脚本传递 < code>sed 表达式,然后是任何文件列表,就像 rename< 的版本一样/a>:

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

A generic script to run a sed expression on a list of files (combines the sed solution with the rename solution):

#!/bin/sh

e=$1
shift

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

Invoke by passing the script a sed expression, and then any list of files, just like a version of rename:

script.sh 's/^fgh/jkl/' fgh*
习ぎ惯性依靠 2024-08-03 01:13:26

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

//一次重命名多个文件

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

You can also use below script. it is very easy to run on terminal...

//Rename multiple files at a time

for file in  FILE_NAME*
do
    mv -i "${file}" "${file/FILE_NAME/RENAMED_FILE_NAME}"
done

Example:-

for file in  hello*
do
    mv -i "${file}" "${file/hello/JAISHREE}"
done
漫漫岁月 2024-08-03 01:13:26

这是 find + sed + xargs 解决方案的扩展版本。

原始解决方案:这个这个

要求:搜索、修剪、正则表达式、重命名

  1. 我想重命名许多文件夹中的多个文件。
  2. 一些文件夹应该被修剪/排除。
  3. 我在 cygwin 上,无法让 perl rename 工作,这是 所必需的最流行的解决方案(我认为它很慢,因为它似乎没有修剪选项?)

解决方案

  1. 使用 find 有效地获取文件(通过修剪),并使用许多定制选项。
  2. 使用 sed 进行正则表达式替换。
  3. 使用 xargs 将结果汇集到最终命令中。

示例 1:重命名 *.js 文件但忽略 node_modules

此示例查找文件并回显找到的文件和重命名的文件。 出于安全考虑,目前它不会移动任何东西。 为此,您必须将 echo 替换为 mv

set -x # stop on error
set -e # verbose mode (echo all commands)

find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js" | 
  sed -nE "s/(.*)\/my(.*)/& \1\/YOUR\2/p" |
  xargs -n 2 echo  # echo first (replace with `mv` later)

上面的脚本将这样:

./x/y/my-abc.js

变成这样:

./x/y/YOUR-abc.js

解决方案分解

  1. find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js"
    • 搜索文件(-type f)。
    • -not 部分排除(而且重要的是,它不会遍历!)(众所周知的巨大)node_modules 文件夹。
    • 文件名必须与“*.js”匹配。
    • 您可以添加更多包含和排除子句。
    • 参考文献:
  2. sed -nE "s/(.*)\/my \-(.*\.js)/&\1\/你的-\2/p"
    • 注意:sed 总是需要一些时间来适应。
    • -E 启用“扩展”(即更现代的)正则表达式语法。
    • -n 与尾随 /p 标志结合使用:-n 隐藏所有结果,而 /p< /code> 将仅打印匹配的结果。 这样,我们只查看/移动需要更改的文件,而忽略所有其他文件。
    • 使用 sed(和其他正则表达式工具)替换正则表达式始终采用以下格式:s/regex/replacement/FLAGS
    • 在替换中,& 表示匹配的输入字符串。 这将是 mv 的第一个参数。
    • 参考文献:


  3. xargs -n 2 echo
    • 使用替换字符串(的前两个字符串)运行命令 echo
    • 参考文献:man xargs

祝你好运!

This is an extended version of the find + sed + xargs solution.

Original solutions: this and this.

Requirements: search, prune, regex, rename

  1. I want to rename multiple files in many folders.
  2. Some folders should be pruned/excluded.
  3. I am on cygwin and cannot get perl rename to work, which is required for the most popular solution (and I assume it to be slow, since it does not seem to have a pruning option?)

Solution

  1. Use find to get files effectively (with pruning), and with many customization options.
  2. Use sed for regex replacement.
  3. Use xargs to funnel the result into the final command.

Example 1: rename *.js files but ignore node_modules

This example finds files and echos the found file and the renamed file. For safety reasons, it does not move anything for now. You have to replace echo with mv for that.

set -x # stop on error
set -e # verbose mode (echo all commands)

find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js" | 
  sed -nE "s/(.*)\/my(.*)/& \1\/YOUR\2/p" |
  xargs -n 2 echo  # echo first (replace with `mv` later)

The above script turns this:

./x/y/my-abc.js

Into this:

./x/y/YOUR-abc.js

Breakdown of Solution

  1. find "." -type f -not \( -path "**/node_modules/**" -prune \) -name "*.js"
    • Searches for files (-type f).
    • The -not part excludes (and, importantly does not traverse!) the (notoriously ginormous) node_modules folder.
    • File name must match "*.js".
    • You can add more include and exclude clauses.
    • Refs:
  2. sed -nE "s/(.*)\/my\-(.*\.js)/& \1\/YOUR-\2/p"
    • NOTE: sed always takes some getting used to.
    • -E enables "extended" (i.e. more modern) regex syntax.
    • -n is used in combination with the trailing /p flag: -n hides all results, while /p will print only matching results. This way, we only see/move files that need changing, and ignore all others.
    • Replacement regex with sed (and other regex tools) is always of the format: s/regex/replacement/FLAGS
    • In replacement, the & represents the matched input string. This will be the first argument to mv.
    • Refs:
  3. xargs -n 2 echo
    • Run the command echo with (the first two strings of) the replaced string.
    • Refs: man xargs

Good luck!

岁月静好 2024-08-03 01:13:26

这个答案只是其他答案中解释的内容的具体应用,我希望它有用:

我遇到过一个情况,我必须重命名一些格式为 CodeAPE_fr.properties 的属性文件到 CodeAPE.properties,在整个目录中,我做了:

for i in `dir`; do mv $i `echo $i | sed 's/_fr.properties/.properties/g'`; done

This answer is only a concrete application of what is explained in other answers, I hope it will be useful:

I went in a case where I had to rename some properties files that were in the form CodeAPE_fr.properties to CodeAPE.properties, in a whole directory and I did:

for i in `dir`; do mv $i `echo $i | sed 's/_fr.properties/.properties/g'`; done
×纯※雪 2024-08-03 01:13:26

如果文件名包含空格并且您想要根据文件名中包含空格模式的部分进行过滤,则需要进行一些轻微的改进/调整:

for f in "Filename With Spaces"*; do mv "$f" "$(echo $f | sed 's/^Filename With Spaces/FilenameWithNoMoreSpaces/g')"; done

我的答案中的主要区别是您需要在引号中添加引号“带空格的文件名”(如果这是您要用作过滤器来选择要操作的文件的文件名部分)。 前面的答案提到了带空格的文件名,但随后基于 jkl* 选择了不带空格的文件。

注意:您不必替换为 FilenameWithNoMoreSpaces。 我只是想展示一个典型的用例作为例子。

如果带有空格的模式位于文件名的中间,则调整后的命令将遵循以下模式:

for f in *"Spaces In Middle of Filename"*; do mv "$f" "$(echo $f | sed 's/Spaces In Middle of Filename/FilenameWithNoMoreSpaces/g')"; done

注意,我在文件名模式的开头添加了另一个星号,其中双引号中包含空格,并且删除了克拉 ^来自 sed 命令的“查找”部分。 如果我解释过度,我很抱歉。 我考虑到有些阅读本文的人可能对编程完全陌生。

Here is a slight improvement/adjustment that you need if the filenames have whitespaces AND you want to filter based on the portion of the filename that has the pattern with the whitespaces:

for f in "Filename With Spaces"*; do mv "$f" "$(echo $f | sed 's/^Filename With Spaces/FilenameWithNoMoreSpaces/g')"; done

The key difference in my answer is that you need to put quotation marks around the "Filename With Spaces" if that is the portion of the filename that you want to use as the filter to choose which files to operate on. The previous answer(s) mentioned filename with spaces but then selected files based on jkl* which does not have spaces.

Note: You do not have to replace with FilenameWithNoMoreSpaces. I just wanted to show a typical use case as an example.

If the pattern with the whitespace is in the MIDDLE of the filename, then the adjusted command would follow this pattern:

for f in *"Spaces In Middle of Filename"*; do mv "$f" "$(echo $f | sed 's/Spaces In Middle of Filename/FilenameWithNoMoreSpaces/g')"; done

Notice, I added another asterisk to the beginning of the filename pattern with spaces that is in the double quotes and I removed the carat ^ from the "find" part of the sed command. My apologies if I'm over-explaining. I'm taking into account that some people who read this might be completely new to programming.

夏末染殇 2024-08-03 01:13:26

使用 nautilus:

  • 选择要按模式重命名的文件
  • 按 Ctrl-F2
  • 选择“查找并替换文本”
  • 输入模式和替换文本
  • 按“重命名”

在此处输入图像描述

With nautilus:

  • select file want to rename by pattern
  • Press Ctrl-F2
  • Choose "Find and replace text"
  • Enter pattern and the replacementt text
  • Press "Rename"

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文