本文将举例对比PowerShell和Unix Shell,通常是Linux Bourne Shell(包括sh、ksh和bash等)。二者存在非常大的差异,***不同是PowerShell将对象作为基本的操作单元,而Unix Shell将字符串作为基本单元;相似之处是二者均有数量巨大内置命令,而且允许用户扩展。
1 实例:终止进程
在Unix操作系统中为了终止所有以字母“p”开头的进程,需要在命令行中运行下面的命令:
$ ps -e | grep " p" | awk '{ print $1 }' | xargs kill
通过ps命令获取了当前进程的清单并将获取的文本输出到grep命令中,该命令搜索文件名以“p”开头的进程。将输出发送给awk命令,从中选取第1列(这里是进程的ID)并输出给xargs命令;xargs命令会对每个进程执行kill命令,从而终止所有以“p”开头的进程。尽管实现了功能,但是整个命令却不可靠。因为ps命令的执行效果在不同操作系统中不同(甚至在相同的操作系统的不同版本中执行也会有差异)。如果不支持-e选项的ps在执行时包含进程ID的列,则不一定是第1列,此时命令行的执行会出现问题。
类似地,如果要在PowerShell中执行相同的命令,只需要执行下面的操作:
PS C:\> get-process p* | stop-process
这里的命令查找所有以“p”开头的进程,并将其终止。Get-Process cmdlet带的参数是需要匹配的进程名,得到的结果对象被直接传递给Stop-Process cmdlet,这样即可结束对应的对象进程。
2 实例:结束过滤的进程
为查找并杀死占用内存超过10 MB的进程,在Unix命令行下需要执行如下命令:
$ ps -el | awk '{ if ( $6 > (1024*10)) { print $3 } }' | grep -v PID | xargs kill
此命令的执行成功取决于用户已知道ps –el命令将会在第6列中返回进程占用的内存大小(以KB为单位)并且在第3列中包含PID属性,同时需要去掉ps命令输出的第1行。
接下来查看在PowerShell中对应的脚本:
PS C:\> get-process | where { $_.WS -gt 10MB } | stop-process
可以看到基于对象的命令相比基于文字的命令的好处,即不必关心包含进程占用内存大小或包含进程ID的部分所在的列。内存占用量能够通过进程名称引用,Where cmdlet可检查输入的对象并取得对象的属性。
3 实例:计算目录大小
在这个实例中将计算某个目录中包含文件的大小,遍历文件、获取其长度属性并叠加到一个变量中,***打印变量。在Unix系统中的处理方式如下:
$ tot=0; for file in $( ls ) > do > set -- $( ls -log $file ) > echo $3 > (( tot = $tot + $3 )) > done; echo $tot
上例使用set命令为每个空格分隔的元素创建变量,这是在awk命令出现之前通常使用的Unix命令。如果使用了awk命令,将会减少相当的代码量,如下所示:
$ ls –l | awk ‘{ tot += $5; print tot; }’ | tail -1
这样降低了命令的复杂性,但是需要用户知道其中的长度属性是在第5列,不同版本的awk可能会有差异。在PowerShell中的循环也很简单,虽然也需要逐个遍历,但是获取长度属性很方便。因为长度作为文件对象的属性存在,不需要关心其所在列,相似的脚本如下:
PS C:\> get-childitem | measure-object -Property length -Sum Count : 53 Average : Sum : 489648208 Maximum : Minimum : Property : length
其中使用的Measure-Object cmdlet将根据输入的-Property确定要操作的属性,根据输入的选项,如-Sum、-Maximum、-Minimum和-Average对前面指定的对象属性做求和、求***值、求最小值,以及求平均值。这里为了和前面的Unix脚本相匹配,只需要指定Length属性及-Sum选项即可。
4 实例:操作动态值
很多由系统提供的对象通常是动态,而不是静态的。即获取某个对象后不需要稍后再次获取其数据,因为该数据根据系统条件的改变不断更新。并且修改这些对象也会立即在系统中生效,此类对象称之为“实时对象”。
例如,需要获取处理器处理时间的占用情况,传统Unix Shell中的ps命令会反复运行以不断取得进程的运行状况。对于能够访问实时进程的对象,只需要获取处理对象一次。一旦对象被系统更新,只需要持续重新读取相同的属性即可。下例以10秒为间隔获取应用程序占用的内存大小,首先查看Unix Shell脚本的处理方式:
$ while [ true ] do msize1=$(ps -el|grep application|grep -v grep|awk '{ print $6}') sleep 10 msize2=$(ps -el|grep application|grep -v grep|awk '{print $6}') expr $msize2 - $msize1 msize1=$msize2 done
使用PowerShell操作的方式如下:
PS C:\>> $app = get-process applicationName PS C:\>> while ( $true ) { >> $msize1 = $app.VM >> start-sleep 10 >> $app.VM - $msize1 >> }
显然,PowerShell的脚本更加简单易读。
5 实例:监视进程寿命
如果在Unix下确定特定的进程是否继续在运行,则需要收集进程清单并与另外一个清单对比。例如:
$ processToWatch=$( ps -e | grep application | awk '{ print $1 }' $ while [ true ] > do > sleep 10 > processToCheck=$(ps -e |grep application |awk '{print $1}' )
> if [ -z "$processToCheck" -or \
> "$processToWatch" != "$processToCheck" ]
> then
> echo "Process application is not running"
> return
> fi
> done
而在PowerShell下只需要执行下面的操作:
PS C:\> $processToWatch = get-process applicationName PS C:\> $processToWatch.WaitForExit()
能够看到PowerShell只需获取对象并等待对象退出即可提示。
6 实例:确定软件是否存在预发布的测试版
由于预发布的测试版本往往不稳定,可能存在各种缺陷。从而导致系统安全风险,所以有必要区分此类软件。Unix可执行文件中并不保存此类信息,所以不必讨论Unix Shell下的情况。对于Windows来说,需要特定的工具检查当前运行的进程中是否存在预发布的测试版本。例如:
PS C:\> Get-Process | where { >> $_.mainmodule.FileVersioninfo.isPreRelease} >> Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 627 17 29236 5120 177 7.66 560 Powerword
这个实例使用了层叠的属性,从进程对象(MainModule)中获取的属性中检查。FileVersionInfo属性是MainModle的引用,其属性IsPreRelease是确定软件是否为预发布版本的根据。如果该属性为真,则Get-Process cmdlet输出的对象将会被输出到控制台。
7 实例: 转换字符串大小写
在Unix Shell中通常会用下面的方式转换特定字符串的大小写:
$ echo "this is a string" | tr [:lower:] [:upper:]
或者使用:
$ echo "this is a string" | tr '[a-z]' '[A.Z]'
在PowerShell中则更为简便,如:
PS (1) > "this is a string".ToUpper()
这是直接使用字符串对象的ToUpper()方法转换将字符串中的大写字母转换为小写字母。如果需要将大写字母转换为小写字母,则使用ToLower()方法即可。
8 实例:在字符串中插入字符
例如,需要将字符串“ABC”插入到字符串“string”的首字母之后,得到类似“sABCtring”的字符串。在Unix Shell下使用sed命令:
$ echo "string" | sed "s|\(.\)\(.*)|\1ABC\2|"
在PowerShell中则使用正则表达式:
PS (1) > "string" -replace '(.)(.*)','$1ABC$2' sABCtring
在PowerShell中更简单的方法是对字符串对象使用Insert()方法直接完成插入操作,如:
PS (2) > "string".Insert(1,"ABC") sABCtring
【编辑推荐】