相关文章推荐
谦逊的开心果  ·  [ffmpeg] ...·  1 年前    · 

本文将举例对比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 )
> 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 ]
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

使用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 ]
> sleep 10
> processToCheck=$(ps -e |grep application |awk '{print $1}' )
> if [ -z "$processToCheck" -or \
> "$processToWatch" != "$processToCheck" ]
> echo "Process application is not running"
> return

而在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