Perl命令行应用程序详解
perl-PracticalExtractionandReportLanguage,Perl有很多命令行参数,通过它可以让你的程序更简练,并且可以写出很多只有一行命令的perl。在这篇文章里我们来了解一些常用的命令行参数。
命令行调用
perl [-sTtuUWX] [-hv][-V[:configvar]] [-cw][-d[t][:debugger]][-D[number/list]] [-pna][-Fpattern][-l[octal]][-0[octal/hexadecimal]] [-Idir][-m[-]module][-M[-]'module...'][-f] [-C[number/list]] [-P] [-S] [-x[dir]] [-i[extension]] [[-e|-E]'command'][--][programfile][argument]...
[开关项描述]
-0
规定记录分隔符。
-0<数字>
(用8进制表示)指定记录分隔符($/变量),默认为换行
-00
段落模式,即以连续换行为分隔符
-0777
禁用分隔符,即将整个文件作为一个记录
-a
与-n或者-p一起使用,负责打开自动拆分模式,用于对空白字符进行隐式拆分,用空格分隔$_并保存到@F中,相当于@F=split''。分隔符可以使用-F参数指定例如:
date|perl-ane'print"$F[0]\n"';
-c
只检查Perl脚本语法,而不执行脚本。
-d
对脚本打开Perl调试器。
-D
设置Perl的调试标记(请先检查Perl的安装情况,确保已经安装了调试器),若要观察Perl是如何执行脚本的,可使用-D14。
-ecommand
用于再命令行而不是在脚本中执行Perl命令。
-Fpattern
规定拆分输入行时使用的模式。模式是位于斜杠、单引号或双引号之间的正则表达式。例如,-F/:+/表示在一个或多个冒号上拆分输入行,如果-a仍然起作用的话,则打开它。
-h
打印Perl的命令选项列表。
-iextension
在使用<>循环遍历文件时启用原位编辑模式。如果没有规定扩展名的话,则原位修改各行内容,否则使用扩展名来修改输入文件名(以便充当备份文件),并使用原位编辑的原文件名创建输出文件。这也是所有print语句选择的文件句柄。
-Idirectory
与-P选项一起使用,负责告诉C预处理程序查找包含的文件,其默认目录包括/usr/included、/usr/lib/perl以及当前目录。
-1digits
启用自动行结束处理。如果使用了-n或者-p选项,则去掉终止符。把位数(八进制)赋值给$\,并将行终止符添加到print语句中。如果没有规定位数的话,则将$\设置为$/的当前值。
-l
对输入内容自动chomp,对输出内容自动添加换行。使用-l有两个效果,第一自动chomp输入分隔号,第二把$/值付给$\(这样print的时候就会自动在末尾加\n)
-m[-]module -M[-]module -M[-]module' -[mM]module= arg[,arg].. -mmodule
在执行Perl脚本之前执行use方法。
-Mmodule
再执行Perl脚本之前执行use方法。如果添加附加的文本,则可使用引号。方括号中的短横线表示把use指令替换为no。
-n
使Perl隐式地循环遍历指定的文件,并只打印规定的行。自动循环,相当于while(<>){脚本;}
-p
使Perl隐式地循环遍历指定的文件,同时打印所有的行。自动循环+自动输出,相当于while(<>){脚本;print;}
-P
使要运行的脚本在Perl编译之前通过C预处理程序。
-s
再脚本名之后、文件名参数之前启用开关项解析,并从@ARGV数组中删除所有找到的开关项。将开关项设置为同名的标量型变量,并将标量赋值为1。例如,-abc在脚本中将转换为$abc。
-S
如果不支持#!/usr/bin/perl行,则让Perl在PATH环境变量中搜索脚本。
-T
强制打开“污染“检查,用于测试脚本,一般只用在setuid或setgid程序上。推荐自行检查CGI脚本。
-u
编译后产生脚本的核心转储(基于UNIX系统)。
-U
允许Perl执行不安全的操作,如果是超级用户的话,则断开目录链接。
-v
打印Perl的版本信息。
-V
打印最重要的Perl配置项和数组@INC中当前值的汇总。
-V:NAME
打印NAME的值,其中NAME是配置变量。
-w
打印警告信息,包括错误使用保留字、文件句柄、子例程等情况。
-W
启用所有警告,而不论是否已经用nowarnings关闭了本地警告。
-xdirectory
忽略#!/usr/bin/perl行之前的任何文本。如果将目录名当作-x开关的参数,则Perl会在开始执行脚本之前自动切换到该目录。
-X
关闭所有警告。
每一行将缺省保存在$_,-p和-n一样,但是还会打印$_的内容。
请注意:-p开关和-n开关的使用。当您想显式打印数据时,使用-n开关。-p开关隐式地将print$_语句插入到-n开关所产生的循环中。因此,-p开关更适用于对文件进行的完全处理,而-n开关更适用于选择性文件处理,这样的处理只需打印特定数据。
安全网参数
有三个参数我认为可以起到“安全网”的作用,因为它们可以让你避免犯错,特别是当你在使用Perl尝试一些特别聪明(或这愚蠢)的想法时,错误难免会发生。有经验的Perl程序员常常使用这三个参数来提前找到错误所在。
-c是第一个。这个参数编译Perl程序但并不真正运行它,由此检查所有语法错误,每次修改perl程序之后我都会立刻使用它来找到任何语法错误。
$perl-cprogram.pl
这保证了程序依然可以编译。很显然,当你输入一小段代码之后立即进行检查,比起一下子输入几百行代码然后开始debug要容易很多。
-W是第二个参数。它会提示你任何潜在的bug。Perl5.6.0之后的版本已经用usewarnings;替换了-w。你应该使用usewarnings因为它要比-w更灵活。
-T是第三个参数。它让perl出于了taint模式中。在这个模式里,Perl会质疑任何程序外传来的数据。例如从命令行读取,外部文件里读取或是CGI程序里传来的数据。这些数据在-T模式里都会被Tainted(污染),Tainted数据不可以被用来和外部交互。例如使用在system调用和用作open的文件名,关于什么数据会被Tainted,请参阅perlsec文档,那里有一个完整的列表。
要想使用Tainted的数据就必须untaint这个数据。untaint是通过正则表达式来实现的,关于taint本身的内容足够写一篇单独的文章,所以这里我不会太多的讲述taint模式。如果你要编写的程序(例如CGI程序)需要从从用户那里接受不可知的输入,我推荐使有taint模式。
还有一个值得一提的参数是-d,它将让Perl处于Debugger模式。这个话题内容非常多,我推荐阅读文档‘perldocperldebug'或RichardFoley的PerlDebuggerPocketReference一书.
使用模块
下面的几个Perl参数可以让短小的Perl程序很容易的在命令行上运行,-e可以让Perl代码在命令行上被编译器直接执行。例如,我们可以直接在命令行上运行“HelloWorld”程序而不用把它写称Perl程序。
$perl-e'print"HelloWorld\n"'
多个-e也可以同时使用,运行顺序根据它出现的位置。
$perl-e'print"Hello";'-e'print"World\n"'
象所有的Perl程序一样,只有程序的最后一行不需要以;结尾,你也可以用-e来引用模块,但-M让它变得更容易。
$perl-MLWP::Simple-e'printhead"http://www.example.com"'
-M模块名和use模块名一样。有些模块有默认的模块导入,如果你不想导入它们,你可以使用-m。-m模块名和usemodule()一样,关闭了默认的导入。例如下面这个例子,因为head函数是默认导入,而使用-m时就不会执行,结果是没有输出。
$perl-mLWP::Simple-e'printhead"http://www.example.com"'
-m和-M有很多方便的语法来帮助你使用它们,你可以在=后面列出对use的各种参数。
$perl-MCGI=:standard-e'printheader'
在这里,CGI.pm的:standard被引入,header函数因此可以使用。要引入多个参数可以通过使用引号和逗号。
$perl-MCGI='header,start_html'-e'printheader,start_html'
这里我们引入了header和start_html函数。
ImplicitLoops
-n和-p增加了循环的功能,使你可以一行一行来处理文件。
$perl-n-e'somecode'file1
这与下面的程序一样.
LINE:
while(){ #yourcodegoeshere }
注意:打开命令行里的文件,一行行的读取,每一行将缺省保存在$_。
$perl-n-e'print"$.-$_"'file
上面的这一行可以写成LINE:while(){print”$.–$_”}输出当前行数$.和当前行$_,-p可以让上面的程序变得更容易,-p会输出$_的内容,就像这样:
while(){ #yourcodegoeshere }continue{ printordie"-pdestination:$!\n"; }
continue在这里保证print在每次循环都会被调用。使用-p,我们的打印行数程序可以改为
$perl-p-e'$_="$.-$_"'filename
这种情况下我们就不需要要明确地调用print函数了,因为-p选项已经调用了它。注意,LINE:标签可以让我们直接跳到下一个输入记录,而不管你进入了多少层循环,使用nextLINE。
$perl-n-e'nextLINEunless/pattern/;print$_'
当然,也可以这样写:
$perl-n-e'printunless/pattern/'
在更复杂的情况里,nextLINE可以让你的代码更容易理解。如果想在循环的前后做些处理,可以使用BEGIN或ENDblock,下面的这一行代码可以计算text文件里的字数:
$perl-ne'END{print$t}@w=/(\w+)/g;$t+=@w'file.txt
每一行所有匹配的字放入数组@w,然后把@w的元素数目递加到$t,ENDblock里的print最后输出文件总字数。
还有两个参数可以让这个程序变得更简单,-a打开自动分离(split)模式,空格是缺省的分离号,输入根据分离号被分离然后放入缺省数组@F。由此,我们可以把上面的程序改写为:
$perl-ane'END{print$x}$x+=@F'file.txt
你也可以通过-F把缺省的分离号改为你想要的.例如把分离号定为非字符:
$perl-F'\W'-ane'END{print$x}$x+=@F'file.txt
下面通过Unixpassword文件来介绍一个复杂的例子。Unixpassword是文本文件,每一行是一个用户记录,由冒号':'分离。第6列是用户的登录shell路径,我们可以得出每一个不同shell路径被多少个用户使用:
$perl-F':'-ane'chomp($F[6]);$s{$F[6]}++;END{print"$_:$s{$_}n"forkeys%s}'/etc/passwd $perl-F':'-alne'$s{$F[6]}++;'-e'END{foreach(keys%s){chomp($_);print"$_\:$s{$_}"}}'/etc/passwd
有如下输出:
/usr/sbin/nologin:3 /bin/sh:18 /bin/sync:1 /bin/bash:2 /bin/false:9
数据分隔符
我以前的文章里提到过$/和$\—输入,输出分隔号。$/用来分隔从文件句柄里读出的数据,缺省$/分隔号是\n,这样每次从文件句柄里就会一行行的读取。$\缺省是空字符,用来自动加到要print的数据尾端。这就是为什么很多时候print都要在末尾加上\n,$/和$\可与-n-p一起使用。在命令行上相对应为-0(零)和-l(这是L)。-0后面可以跟一个16进制或8进制数值,这个值用来赋给$/。-00打开段落模式,-0777打开slurp模式(即可以一次把整个文件读入),这与把$/设为空字符和undef一样效果。
单独使用-l有两个效果,第一自动chomp输入分隔号,第二把$/值付给$\(这样print的时候就会自动在末尾加\n)我个人常常使用-l参数,用来给每一个输出加\n,例如:
$perl-le'print"HelloWorld"'
原位编辑
使用已有的参数我们可以写出很有效的命令行程序.常见的UnixI/O重定向:
$perl-pe'somecode'output.txt
这个程序从input.txt读取数据,然后做一些处理再输出到output.txt.你当然也可以把输出重定向到同一个文件里。上面的程序可以通过-i参数做的更简单些。-i把源文件更名然后从这个更名的源文件里读取,最后把处理后的数据写入源文件。如果-i后跟有其他字符串,这个字符串与源文件名合成后来生成一个新的文件名,此文件会被用来储存原始文件以免被-i参数覆盖。
这个例子把所有php字符替换为perl:
$perl-i-pe's/\bPHP\b/Perl/g'file.txt
程序读取文件的每一行,然后替换字符,处理后的数据重新写入(即覆盖)源文件.如果不想覆盖源文件,可以使用$perl-i.bak-
pe's/\bPHP\b/Perl/g'file.txt
这里处理过的数据写入file.txt,file.txt.bak是源文件的备份。
打开Perl的taint模式
1、该程序的输入数据来自外部数据源。
2、该程序所调用sub-shell或者函数影响到该程序之外其它东西。
你可以用“-T”开关来打开Perl的taint模式。当打开taint模式时,Perl就会进行执行检查以确保你的数据未被taint,如果不安全的使用了被taint的数据,就会发出严重错误。为了使你的数据不被taint,执行正则表达式来匹配数据并提取匹配部分。这样你就必须描述出你所期望的数据的内容以及格式,并只接受符合这一要求的数据。
例如,假设你期望收到一个单词字符(wordcharacters,即字母数字以及下划线),那么下面的代码通过一个正则表达式(它只通过全部由单词字符组成字符串)会“蒸馏出”你的数据来:
if($data=~/^(\w+)$/){ $data=$1; }else{ die”Error:tainteddatafound:$data\n”; }
Perl命令行调试
缺省的Perl调试器就是perl解释器本身,另外还有图形界面的调试器。图形界面的调试器推荐ptkdb,这里不再进行介绍,下载安装以后用两次便会。所以这里主要介绍一下缺省的命令行调试器的用法,一般的调试,用下面这些命令已经足够了,这些命令说明也来自网上整理而成。
用-d命令行选项启动Perl解释器,例如:
perl-dtest.pl即进入Perl调试器的交互界面。
调试命令列表如下:(所有命令都在调试提示符下顶格输入,命令大小写有关)
h:显示调试器的帮助信息。
|h:以分页形式显示调试器的帮助信息。
hh:显示一个压缩的帮助信息。
h调试命令:显示某个调试命令的帮助。
p表达式:显示变量或表达式的值,不显示复杂变量嵌入的结构和数据。
x表达式:显示变量或表达式的值,对较为复杂的变量,以一种可读形式显示嵌入的结构和数据。
V包名变量名列表:显示指定包内的所有(或部分)变量的值。(缺省的包名为main)
X变量名列表:显示当前包内所有(或部分)变量的值。
注:V、X命令中的变量名列表以空格分隔且变量名前应去掉$、@或%。
T:程序的调用栈回退一级。
s表达式:单步执行,进入子函数。(stepinto)如果提供一个表达式并且表达式中包括函数调用,则单步进入该函数内。
n表达式:单步执行,越过子函数。(stepover)
c行号/函数名:执行到某一行或某一个函数。
l:显示未执行的一窗(一屏)文件内容。
lmin-max:显示第min到第max行的文件内容。
l行号:显示指定行的内容。
l函数名:显示指定函数的一窗(一屏)文件内容。
w行号:显示某行周围一窗(一屏)文件内容。
f文件名:切换到另一个文件。
/模式:在当前文件中向前(文件尾)查找匹配的字符串。
?模式:在当前文件中向后(文件头)查找匹配的字符串。
L:显示所有断点和附加操作。
S模式:显示匹配(或不匹配,在模式前加!)的函数名。
t:切换跟踪模式。
t表达式:跟踪执行表达式过程。
b行号条件:在某一行设置一个断点,当程序执行到该行并且条件满足时,产生中断。
b函数名条件:在某函数上设置一个断点,当程序执行到该函数并且条件满足时,产生中断。
bload文件名:在某个文件的第一个可执行语句上设置一个断点。
d行号:删除某一行的断点。
D:删除所有断点。
a行号命令:给程序的某一行加一个附加操作。在执行该行语句前先执行附加的操作。
A:删除所有已安装的附加操作。
W表达式:增加一个监视项。
W:删除所有监视项。
O选项名?:查询调试器可选项的值。
O选项名=选项值:设置调试器可选项的值。
ltPerl语句:设置一个操作显示调试提示符前执行的操作。
ltltPerl语句:增加一个显示调试提示符前执行的操作。
gtPerl语句:设置一个离开调试提示符(转入运行态)时执行的操作。
gtgtPerl语句:增加一个离开调试提示符(转入运行态)时执行的操作。
{调试命令:设置一个操作显示调试提示符前执行的操作。
{{调试命令:设置一个操作显示调试提示符前执行的操作。
注:上述Perl语句和调试命令均可输入多行,行尾以\转义。
!number:重新执行以前第number次执行的调试命令。
!-number:重新执行现在以前number次执行的调试命令。
!模式:重新执行以前执行过的与模式匹配的调试命令。
!!命令:不退出调试器执行一个shell命令。
H-number:显示以前执行的number条调试命令。如果省略number,则显示所有执行过的调试命令。
R:重新启动正在调试的程序。
q或^D:退出调试器。
|调试命令:将调试命令的输出分页显示。
||调试命令:类似|调试命令,适于有大量输出的调试命令,例如:|Vmain。
=别名值:给某个调试命令一个别名,例如:=quitq。
所有未识别的命令:当作插入的一条Perl语句执行。(使用eval)
Perl调试器的功能还有很多,可以设置很多选项来定制调试器的环境,它本身也是用Perl开发的,并且Perl发布中还有接口让你能开发其它的Perl调试器。如果要用Perl开发大的项目,有必要详细了解这些细节。使用调试器和设置调试器选项的例子请参考Perl发布中关于perldebug的文档。
更多信息
Perl有大量的命令行参数,这篇文章只是列举了最有用的一小部分,更详细的信息请参考“perlrun”文档。
命令行的运用
用perl做命令行的一些简单的介绍,来替代sed、awk之类的软件。
Perl作为命令行实用程序,可以度参考ibm的这篇文章。作者提到的很重要的一点是:有经验的程序员不应回避快速而又难看的解决方案。
Perl单行命令示例
我们先看看perl如何接收用户的参数。如下,Dumper模块不用理会,只是它会给数据结构打印出来,shell给接收到的参数都放到了@ARGV这个数组。
perl-MData::Dumper-e'printDumper\@ARGV'ab-w $VAR1=[ 'a', 'b', '-w' ];
如上,perl可以直接接收到shell命令行的参数。“-e”后的任何内容并将它当作脚本来运行。“-M”参数表示获取其后的任何内容并将该内容作为模块导入,类似于正规脚本中的“useModuleName”。我们可以看到象所有的Perl程序一样,只有程序的最后一行不需要以;结尾。
注:-e在单行命令中非常的重要,做单行命令时一定要加入在使用perl的单行命令时要注意使用”,不要使用"",其实sed和awk也是一样,当然这只是一个建议,看下面的例子就能明白:
perl-e'print"$$\n"' 5719
'$$'在perl中表示当前的pid,如果使用的是单引号,其中还可以放双引号,还有变量,重要的是,这些变量不用担心被shell转义。
perl-pi-e's/aaa/bbb/'filename
修改当前file文件中的文件,不生成中间文件,速度很快.记住-i开关,因为它让你原地编辑文件。
perl-ne'printif/^int/'filename
象grep一样过滤文件中需要的内容。这个地方,使用了-n,所以一次是做一行的操作,直到整个文件读完。另外,在管道时,-n也会一样,来遍历管道送过来的内容。
perl-n-e'print"$.-$_"'filename
这个例子中的,没用-ne,只是命令写成了-n-e,其实一样,这个例子中,是给当前文件中的内容都加一个行号打印出来.注:$.表示当前行号。
perl-pe'$_="$.$_"'filename
这个其实和上面一样,分别只是使用了-p替换了-n,这个有个什么好处啦,别的地方都一样,但-p按行来遍历完文件后,会给$_打印出来。相当于awk分割域(awk‘{i=NF–1;print$1+$i}')。
perl-lane'print$F[0]+$F[-2]'
这个神奇的地方在于-a,使用-a后.因为-n分行读进来,然后-a给数据分割成@F的数组。
perl-ne'printif/^START$/../^END$/'
打印正则中从$start到$end的地方
perl-ne'printif$.>=15;exitif$.>=17;'
有效地打印数字范围中的行
perl-p-i.bak-e's/\bfoo\b/bar/g'*.c
原地修改-i开关的神奇之处在于它对@ARGV中的每个文件都用该脚本对该文件输出所产生的文件版本进行替代。
perl-ne'printscalarreverse$_'test
给文件中的内容反向排序,比如文件中有fukai,就会变成iakuf.
替换
将所有C程序中的foo替换成bar,旧文件备份成.bak
perl-p-i.bak-e's/\bfoo\b/bar/g'*.c
很强大的功能,特别是在大程序中做重构。记得只有在UltraEdit用过,如果你不想备份,就直接写成perl-p-i-e或者更简单perl-pie
将每个文件中出现的数值都加一
perl-i.bak-pe's/(\d+)/1+$1/ge'file1file2....
将换行符\r\n替换成\n
perl-p-i-e's/\r\n/\n/g'file
同dos2unix命令。
将换行符\n替换成\r\n
perl-pie's/\n/\r\n/g'file
同unix2dos命令。
取出文件的一部分
显示字段0-4和字段6,字段的分隔符是空格
perl-lane'print"@F[0..4]$F[6]"'file
同awk'print$1,$2,$3,$4,$5,$7'。参数名称lane也很好记。
如果字段分隔符不是空格而是冒号,则用
perl-F:-lane'print"@F[0..4]\n"'/etc/passwd
显示START和END之间的部分
perl-ne'printif/^START$/../^END$/'file
相反,不显示START和END之间的部分
perl-ne'printunless/^START$/../^END$/'file
显示开头50行:
perl-pe'exitif$.>50'file
同命令head-n50
不显示开头10行:
perl-ne'printunless1..10'file
显示15行到17行:
perl-ne'printif15..17'file
每行取前80个字符:
perl-lne'printsubstr($_,0,80)=""'file
每行丢弃前10个字符:
perl-lne'printsubstr($_,10)=""'file
搜索
查找comment字符串:
perl-ne'printif/comment/'duptext
这个就是普通的grep命令了。
查找不含comment字符串的行:
perl-ne'printunless/comment/'duptext
反向的grep,即grep-v。
查找包含comment或apple的行:
perl-ne'printif/comment/||/apple/'duptext
相同的功能就要用到egrep了,语法比较复杂……
计算
计算字段4和倒数第二字段之和:
perl-lane'print$F[4]+$F[-2]'
要是用awk,就得写成awk'{i=NF-1;print$5+$i}'
排序和反转
文件按行排序:
perl-e'printsort<>'file
相当于简单的sort命令。
文件按段落排序:
perl-00-e'printsort<>'file
多个文件按文件内容排序,并返回合并后的文件:
perl-0777-e'printsort<>'file1file2
文件按行反转:
perl-e'printreverse<>'file1
相应的命令有吗?有,tac(cat的反转)
数值计算
10进制转16进制:
perl-ne'printf"%x\n",$_'
10进制转8进制:
perl-ne'printf"%o\n",$_'
16进制转10进制:
perl-ne'printhex($_)."\n"'
8进制转10进制:
perl-ne'printoct($_)."\n"'
简易计算器
perl-ne'printeval($_)."\n"'
批量重命名文件
以下是在verycd网站下载的资料,现对其改名的例子:
$ls 帝王之死001.mp3帝王之死006.mp3 $perl-MFile::Find-e'findsub{rename$_,substr($1,1,2).".mp3"if/(\d+)\.mp3$/;},"."' $ls 01.mp306.mp3
$ls 李斯傳奇-第001回.mp3 $perl-MFile::Find-e'findsub{rename$_,$1.".mp3"if/(\d+)(.*)\.mp3$/;},"."' $ls 001.mp3
$ls 十二生肖妙品欣赏系列01子鼠精灵.pdf $perl-MFile::Find-e'findsub{rename$_,"$2$3".".pdf"if/(.*)\s(\d+)\s(.*)\.pdf$/;},"."' $ls 01子鼠精灵.pdf
中文标点符号替换(中文在此占3个字符位) 女人当国$ls 女人当国01:选秀入宫.mp3 女人当国16:“议政王”的奥妙.mp3
将'mp3'文件修改为:01.选秀入宫.mp3,像如下格式: perl-MFile::Find-E'findsub{rename$_,"$1.$3".".mp3"if/(\d{2})([^u4E00-u9FA5]{3})(.*).mp3$/;},"."' 女人当国$ls 01.选秀入宫.mp3 13.京城风云动.mp3
... 秦琼:“战将”最终成“门神”(上).mp3 将'“”)'引号去掉 perl-MFile::Find-E'findsub{my$o=$_;s/“|”|)//g;rename$o,$_},"."'
秦琼:战将最终成门神(上).mp3
将':('替换为'.' perl-MFile::Find-E'findsub{my$o=$_;s/:|(|,/\./g;rename$o,$_},"."' 秦琼.战将最终成门神.上.mp3
在数字编号后加入'.' 01石头里生出美猴王.mp3 perl-MFile::Find-E'findsub{my$o=$_;s/(^\d+)/$1\./;rename$o,$_},"."' 01.石头里生出美猴王.mp3
将前置中文字符去掉 独立书店_01.台北.书店之城.1.mp3 perl-MFile::Find-E'findsub{my$o=$_;s/([^u4E00-u9FA5]{12}_)//g;rename$o,$_},"."' 01.台北.书店之城.1.mp3