[用writer在csdn发图总是对齐有问题,再加上偶尔发不了图,干脆以后只发纯文字的了,要看偶的照片,请到live space去吧:http://from2003.spaces.live.com/]
有一堆的文件,名字是这样的:[abcd]2008.01.01.测试.txt,打算将收尾去掉,改成这个样子:2008.01.01.txt。在linux下应该很简单,大不了写个脚本就搞定了,不过我想在windows下搞定。一直想研究一下PowerShell,这回打算用这个工具实现我的目的。
谁知折腾了两天,居然还没搞定,倒是PowerShell略微入门了一些。感觉上PowerShell确实强大,跟通常意义上的脚本有很大区别。最根本之处在于其所有Cmdlet的输出都是对象,这个概念刚开始恐怕不好理解,不过深入了解后就会发现其灵活之处。不象其他脚本的命令输出是字符串,管道之后的命令只能处理前面输出的字符串,PowerShell的Cmdlet输出的是对象,管道后面可以充分利用对象的丰富信息来工作。比如说dir(其实就是get-childitem命令),输出的是一堆的文件或者目录的对象,文件的话是System.IO.FileInfo对象,目录的话是 System.IO.DirectoryInfo对象,这些都是标准的.NET对象,所以可以使用这些对象的属性和方法,如果对.NET/C#编程熟悉的话就更有优势了。举个例子来说,如果想列出当前目录的只读文件,可以这样:
dir|where-object{$_.IsReadOnly}
列出目录是这样:
dir|where-object{$_.PSIsContainer}
如果想知道目录下有多少文件
get-childitem|where-object{$_.PSIsContainer}|foreach-object{"$_.fullname: " + $_.getfiles().length}
前面说了,PowerShell的输出一切皆对象,而Get-Member可以知道输出的每一个项是什么对象以及对象有什么属性和方法。
话说回来,PowerShell也有让人费解的地方,跟其他微软产品似乎风格不同,PowerShell跟有点像*NIX的东西,很多让人眼花缭乱的缩写跟Windows SDK的长长的函数名成鲜明对比,象ls、cp这些*NIX下的命令居然也可以用,当然只不过是别名而已,让人瞠舌的是几乎每个内置的命令都有别名,有的还有不止一个的别名,比如Get-ChildItem的别名就有gci、ls、dir三个,而?是Where-Object的别名,%是Foreach-Object的别名,这些恐怕就会让初学者相当迷惑了。来看前面的例子,取当前目录的每个子目录下的文件数,如果改成简单的方式,就是下面这样子:
ls|?{$_.PSIsContainer}|%{$_.fullname: " + $_.getfiles().length}
看上去是不是跟Perl有的一拼了?再看下面的例子,又有点awk的味道,这是计算所有子目录的文件数(当然也许有更简单的办法,这只是为了示例):
ls|?{$_.PSIsContainer}|%{$count=0}{$count+=$_.getFiles().length}{"Total Files:" + $count}
搞出这个例子之后我似乎对PowerShell又有所领悟,有点佩服微软了,PowerShell确实是一个强大的脚本语言。
好,闲话少说,最开始的要求还没有解决呢。开始我用的rename-item,谁知对方括号不太感冒,折腾了半天也没搞定,总是报这样的错误:
PS F:/temp> Rename-Item 'F:/temp/`[10`]' 'F:/temp/10'
Rename-Item : 指定路径 F:/temp/`[10`] 下的对象不存在。
所在位置 行:1 字符: 12
+ Rename-Item <<<< 'F:/temp/`[10`]' 'F:/temp/10'
直到某一刻网上搜到可以用move-item,才最后搞定,各种酸辛就不道了,总之这是rename-item的一个bug,不支持方括号,只能用move-item。倒~
最后的结果是
ls *.avi|%{ if($_.fullname -match '(.*)/[.*/](/d/d/d/d/./d/d/./d/d).*avi'){mv -literalpath $_.fullna
me "$($matches[1]+$matches[2]).avi" }}
还是那句话,实践是学习知识的最好途径,特别是碰到bug的时候。似乎偶下次面试的时候也可以写上“精通PowerShell”了啊,吼吼。