Regex 基于文件名使用bash和Perl批量重命名文件
我希望只批量重命名当前目录中的文件,并从文件名末尾删除某些字符串 样本:Regex 基于文件名使用bash和Perl批量重命名文件,regex,linux,perl,bash,shell,Regex,Linux,Perl,Bash,Shell,我希望只批量重命名当前目录中的文件,并从文件名末尾删除某些字符串 样本: foo-bar-(ab-4529111094).txt foo-bar-foo-bar-(ab-189534).txt foo-bar-foo-bar-bar-(ab-24937932201).txt 输出应如下所示: foo-bar.txt foo-bar-foo-bar.txt foo-bar-foo-bar-bar.txt 我想删除每个文件名末尾的字符串-(ab-2492201) 知道数字的长度可能不同 Perl
foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt
输出应如下所示:
foo-bar.txt
foo-bar-foo-bar.txt
foo-bar-foo-bar-bar.txt
我想删除每个文件名末尾的字符串-(ab-2492201)
知道数字的长度可能不同
Perl正则表达式优于模块,并且不使用任何实用程序,而对于bash oneliner命令则是非常首选的
如何在Linux上的Perl和Bash Shell中实现这一点?有兴趣了解这两种解决方案。试试:
$ rename 's/-\(ab-\d+\)(?=\.txt$)//' *.txt
有一个用Perl编写的rename
命令。它的第一个参数是描述如何转换文件名的Perl代码。您可以在自己的Perl程序或一行程序中使用相同的s//
命令
如果不起作用,请尝试
prename
而不是rename
;在某些系统上安装了一个不同的、非基于Perl的、rename
命令,在这种情况下,Perl命令可以在bash中被称为prename
,您可以编写如下代码:
for file in *-\(ab-[0-9]*\)*; do
newfile="${file/-(ab-[0-9]*)/}"
mv "$file" "$newfile"
done
更简单、更短(更好?:)rename
regex:
rename 's@-\(.*?\)@@' foo*.txt
当你说在当前目录下时,你是指在当前目录下,还是指在当前目录及其子目录中的任何地方
File::Find
是实现后者的简单方法,是一个核心模块,因此不需要安装。像这样:
use strict;
use warnings;
use autodie;
use File::Find;
find(\&rename, '.');
sub rename {
return unless -f;
my $newname = $_;
return unless $newname =~ s/-\(ab-[0-9]+\)(\.txt)$/$1/i;
print "rename $_, $newname\n";
}
更新
此程序将仅在当前目录中重命名具有给定文件名模式的所有文件
请注意,初始的open
循环仅用于创建用于重命名的示例文件
use strict;
use warnings;
use autodie;
open my $fh, '>', $_ for qw(
foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt
);
for (glob '*.txt') {
next unless -f;
my $newname = $_;
next unless $newname =~ s/-\(ab-[0-9]+\)(\.txt)$/$1/i;
print "rename $_, $newname\n";
rename $_, $newname;
}
输出
rename foo-bar-(ab-4529111094).txt, foo-bar.txt
rename foo-bar-foo-bar-(ab-189534).txt, foo-bar-foo-bar.txt
rename foo-bar-foo-bar-bar-(ab-24937932201).txt, foo-bar-foo-bar-bar.txt
选中此项:
ls -1 | nawk '/foo-bar-/{old=$0;gsub(/-\(.*\)/,"",$0);system("mv \""old"\" "$0)}'
> ls -1 foo*
foo-bar-(ab-4529111094).txt
foo-bar-foo-bar-(ab-189534).txt
foo-bar-foo-bar-bar-(ab-24937932201).txt
> ls -1 | nawk '/foo-bar-/{old=$0;gsub(/-\(.*\)/,"",$0);system("mv \""old"\" "$0)}'
> ls -1 foo*
foo-bar-foo-bar-bar.txt
foo-bar-foo-bar.txt
foo-bar.txt
>
有关详细说明,请检查使用Perl正则表达式重命名文件
通过使用find
、perl
和xargs
,您可以使用这一行程序
find . -type f | perl -pe 'print $_; s/input/output/' | xargs -n2 mv
不调用mv
的结果应该是
OldName NewName
OldName NewName
OldName NewName
它是如何工作的?
find-键入f
输出文件路径(或文件名…您可以在此处控制正则表达式处理的内容!)-p
打印要由正则表达式处理的文件路径,-e
执行内联脚本print$\ucode>首先打印原始文件名(独立于-p
)
-n2
每行打印两个元素mv
获取上一行的输入仅使用perl的另一种方法:
perl -E'for (<*.*>){ ($new = $_) =~ s/(^.+?)(-\(.+)(\..*$)/$1$3/; say $_." -> ".$new}'
perl-E'for(){($new=$\)=~s/(^.+?)(\(.+)(\..*$)/$1$3/;比如说$.->“$new}”
(say…
很适合测试,只需将其替换为rename$\u$new
或rename($\u$new)
)
读取当前目录中的每个文件($new=$\u)=~
将以下替换保存在$new
中,并保持$\ucode>不变
(^.+?)
将此匹配保存在$1中,从开始到(\(.+)
找到序列“-(…任何东西…”)。(此匹配项将保存在$2中)(\..*$)
将行结束($)之前最后一个“.”(句点)的所有内容保存到并包括行结束->到$3中$1$3
(您也可以使用
perl-E'for(){…
为特定目录执行此操作。为什么不想使用任何perl模块?为了在我的主脚本中插入代码后使其在多台机器上可移植,因此并非每台机器都必须安装模块依赖项。有很多perl“核心”任何标准Perl安装都应该提供的模块。其中一个模块是File::Find
,但您的问题似乎不需要它。当前目录只有一级,没有子目录。我将尝试一下,谢谢。此代码将下降到子目录中,而不是您想要的。我添加了一个解决方案,仅重命名文件当前目录中的y。如果目录中的两个文件名为foo-bar-(ab-4529111094).txt
和foo-bar-(ab-189534).txt
,会发生什么情况?对不起,请注意字符串“foo”文件名的开头可以是任何类似于用破折号分隔的多个单词的内容,@sputnick编写的内容将适用于文件名开头的任何内容,只要它有一个连字符、一个开头部分和(稍后的某一点)结尾部分。您是否尝试过创建您在问题中提到的确切文件名,并查看其是否适用于这些文件名?工作完全符合我的要求,而其他解决方案则没有,节省了我的时间,非常感谢!奇怪。我只是创建了与您的问题相同的文件名,然后粘贴在上面的rename
命令中,效果很好。尝试时发生了什么?是否有任何错误消息?如果将-v
选项置于重命名
命令中,是否会产生任何输出?显然有多个名为“重命名”的命令;一些Linux发行版附带了一个非perl版本,其工作方式有所不同。请参阅:Arnon Weinberg:很好,谢谢。答案已更新以反映这一点。