一段自动搜索所有SVN工作目录进行更新的PowerShell脚本
时间:2010-12-02 来源:Ivony...
dir -r |?{ $_.Mode -like "*d*"} |cd -passthru |?{ (gci |%{ $_ -as [string] }) -contains ".svn" } |%{gl; svn cleanup; svn update }
然后我来一段段的解释:
dir –r
dir是Get-ChildItem的别名,这里的-r是-Recurse的缩写,PowerShell脚本所有参数和命令都不区分大小写,并且在没有歧义的前提下可以缩写。
这个指令的含义就是获取当前位置(省略-Path参数默认就是当前目录)所有子项,并且递归获取子项的子项。也就是类似于dir /s的效果,只不过这里会返回这些项(文件或目录)供后面的使用而不是显示在屏幕上了事。
然后是|?{ $_.Mode –like “*d*” }
|是管道操作符,意思是把前面指令的输出当作后面指令的输入,后面的指令是?,这个?是Where-Object的别名,所以其实你可以写成这样:
Get-ChildItem | Where-Object { …
当然这样太罗嗦了。Where-Object有很多参数,帮助中是这样说的:
Where-Object [-FilterScript] <scriptblock> [-InputObject <psobject>] [<CommonParameters>]
其中 –InputObject 这个参数是管道输入,换言之通过管道运算符传来的东西会被自动当成这个参数后面的<psoobject>,每个PowerShell的指令的输入参数都不同,你可以通过help where-object –full看到这些信息:
......
-InputObject <psobject>
指定要筛选的对象。还可将对象通过管道传递给 Where-Object。
是否必需? False
位置? named
默认值
是否接受管道输入? true (ByValue)
是否接受通配符? False
......
注意这里的是否接受管道输入是true,有些PowerShell指令有多个接受管道输入的参数,那他们一般都会是互斥的。
OK,现在我们知道dir -r的结果会被当作Where-Object的-InputObject参数了,这个Where-Object的主要用途就是从输入中筛选符合条件的项,这里的筛选脚本是花括号里面括起来的东西:
{ $_.Mode –like “*d*” }
$_代表迭代的每一项,也就是dir -r结果里面的每一项,那么这里是判断每一项的Mode属性是不是包含一个”d”字符,也就是这个东西是不是目录。
筛选后的结果会被发送给|cd -passthru
cd是Set-Location的别名,也就是改变当前工作目录。结合前面的脚本,这里等于会让PowerShell到当前目录的所有子目录去跑一圈,不过如果只是跑一圈不干活那这个脚本是什么意义都没有的。所以cd带了一个参数叫做 -passthru。这个参数的意思是在执行完改变工作目录的操作后,把输入继续抛到后面去执行。
那么后面是最关键的部分了:
|?{ (gci |%{ $_ -as [string] }) -contains ".svn" }
这里又是一个Where-Object筛选,它执行的脚本是:
{ (gci |%{ $_ -as [string] }) -contains ".svn" }
一点点来解释,gci是Get-ChildItem的另一个别名(一开始的dir也是Get-ChildItem),请注意这里的gci被包在了{}脚本块里面,所以他没有什么管道输入,和一开始的dir一样,他是用空的-Path参数来执行的,换言之这里的gci其实就是gci .,由于前面有一个cd操作,所以这里的.会是所有的子目录。
也就是说:
第一个dir -r结合筛选会获取当前目录所有子代目录,
然后cd -passthru会改变工作目录到每一个子代
在每一个子代,又都会执行gci来获取所有子文件(目录)。
gci的结果被管道到%{ $_ -as [string] },%是ForEach-Object的别名,ForEach-Object和Where-Object类似,只不过Where-Object是对每一项执行一个判断,只返回符合条件的项,而ForEach-Object是对每一项执行一个操作返回操作结果,也就类似于LINQ是select和where的区别。
这个操作是 -as [string]也就是把项变成字符串,也就是文件名(目录名)
(gci |%{ $_ -as [string] })
经过这个复杂的操作后,gci的结果就由文件(目录)集合变成了字符串集合,这个时候再对其执行 -contains ".svn",判断这个字符串集合里面有没有".svn"这样的字符串。换言之,有没有名为.svn的文件或目录。
所以{ (gci |%{ $_ -as [string] }) -contains ".svn" }这个脚本的结果就是,这个目录的子文件(目录)包不包含".svn",因为只有包含.svn的才是svn的工作目录。
接下来执行的操作就比较简单了:
|%{gl; svn cleanup; svn update }
对于筛选出来的结果(此时结果已经不重要了,因为已经通过cd设置为了工作位置,所以里面没有$_了)执行一段脚本。
gl是Get-Location的缩写,也就是获取当前的位置,执行这个操作主要是为了显示一下当前在哪个目录干活。
然后执行svn cleanup和svn update。
最后我把这些操作写成了一个VS的启动脚本,这样每次通过这个脚本启动VS就能确保我的项目都是最新的了:
$logsfile = ("$home\Documents\svn-update-logs", (get-date -format "yyyyMMddHHmmss") -join "\") , "log" -join "."
pushd
cd 'C:\Users\Ivony\Documents\Visual Studio 2010\Projects'
dir -r |?{ $_.Mode -like "*d*"} |cd -passthru |?{ (gci |%{ $_ -as [string] }) -contains ".svn" } |%{gl; svn cleanup; svn update } | tee $logsfile
popd
echo "completed." | tee $logsfile
echo "completed."
echo "logs file: $logsfile"
echo "starting IDE"
ii "C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"
希望对同样很懒的你有帮助。
在这个基础上,也不难做出什么关机自动提交的脚本。。。。