Windows PowerShell 2.0之条件表达式的分析


与算数表达式类似,条件表达式使用一系列操作求值用来计算结果。并且得到一个布尔类型的结果,即真和假。如果有时得出的结果是一个具体的数字,解析引擎在解析时也会执行布尔类型转换。即0为假,非0为真,所以在程序中直接以数值为流的控制条件。

在PowerShell中的真和假与其他类C语言中的true和false不同的,其条件表达式返回的结果是$true和$false。下例说明如何比较一个变量值是否小于7:

PS C:\> $num= 5
PS C:\> $num-lt 7
True
PS C:\> $num- 7
-2

其中采用了不同的操作符,得出的结果分别是布尔类型和数字类型。-lt操作符检查运算符左边的值是否小于右边的值。

在条件表达式中可以包含属性引用和方法调用,如:

PS C:\> $user=@{"Name"="LiMing";"Age"=30}
PS C:\> $user.Age -lt 20
False
PS C:\> $user.ContainsValue("LiMing")
True

也可以调用PowerShell的函数、cmdlet和脚本块,如启动一个notepad.exe进程并且检查其运行的进程数是否少于两个。一些网络进程会占用固定的端口号,如果前一个进程僵死,在启动新的进程之前需要检查是否存在一个进程;否则后面启动的程序不能获取已经被占用的端口而报错:

PS C:\> Get-Process notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     48       2     1208       4084    54     0.89   2188 notepad

PS C:\> (Get-Process notepad).Length -lt 2
True
PS C:\> notepad.exe
PS C:\> (Get-Process notepad).Length -lt 2
False

开始看到只有一个notepad.exe的进程,第2个命令判断当前进程中的notepad.exe的数量是否少于两个,结果返回布尔类型的真值。当再次启动一个进程时,返回值就变为假。

值比较

PowerShell未采用算数符号如<、>、<=、>=及=进行值比较,首先它必须以Shell的形式出现;其次才作为一门编程语言。所有的Shell都把>、>>和<操作符保留为命令的输出和输入重定向,PowerShell也如此。PowerShell使用类似操作名的缩写的开关作为比较运算符。

(1)-eq:相等(equal)操作符,在前后两个对象相等时返回True;否则返回False,如:

PS C:\> 3 -eq 3
True
PS C:\> 3 -eq 4
False

(2)-ne:不相等(not-equal)运算符,相等操作符的逆运算符,如:

PS C:\> 3 –ne 3
False
PS C:\> 3 –ne 4
True

(3)-lt:小于(less-than)运算符,左侧操作数小于右侧时返回True,如:

PS C:\> 3 –lt 4
True

(4)-gt:大于(greater-than)操作符,左侧操作数大于右侧时返回True,如:

PS C:\> 5 –gt 4
True

(5)le:小与等于(less-than-or-equal)操作符,判断第1个值是否小与等于第2个值,如:

PS C:\> 3 –le 4
True
PS C:\> 3 –le 4
False

(6):-ge大于等于(greater-than-or-equal)操作符,用来检测第1个值是否大于等于第2个值,如:

PS C:\> $a=4
PS C:\> $a -ge 4
True
PS C:\> $a--
PS C:\> $a -ge 4
False

上例中的$a语句作为一个变量的引用来使用,变量被赋值为4,然后比较是否大于等于4。

可以比较两个字符串,操作会按照字母表顺序处理。即逐个字符比较,由相同字符开始的字符串序列较短的一个将会是较小值。以下是几个比较字符串的例子:

PS C:\> "a" -lt "b"
True
PS C:\> "a" -lt "ba"
True
PS C:\> "aa" -lt "aaa"
True
PS C:\> "cat" -gt "dog"
False

需要注意的是默认情况下,字符串比较时忽略大小写,如“LiMing”等于“LIMING”:

PS C:\> "LiMing" -eq "LIMING"
True

如果需要大小写敏感比较,可以使用操作符-ceq、-clt、-cle、-cgt和-cge。这些操作符在大小写不敏感操作符前加了c,这样便于记忆。大小写敏感比较时,小写字母小于大写字母。下例比较两个字符串,其区别仅仅是大小写:

PS C:\> "john" -lt "JOHN"
False
PS C:\> "john" -gt "JOHN"
False
PS C:\> "john" -eq "JOHN"
True
PS C:\> "john" -ceq "JOHN"
False
PS C:\> "john" -cgt "JOHN"
False 
PS C:\> "john" -clt "JOHN"
True

“john”在大小写不敏感比较时等于“JOHN”;在大小写敏感的比较时小于“JOHN”。

在PowerShell中明确的大小写不敏感比较操作符是在默认操作符前加前缀i,即-ieq、-ilt、-ile、-igt和-ige,其作用是提高程序的可读性。

隐式类型转换

在PowerShell中自动转换的通常规则是对于两个不同类型变量组成的表达式,自动将右侧的变量转换为左侧变量的类型,之后计算表达式的值。下例比较字符串和数字类型:

PS C:\> 2 -eq "2"
True
PS C:\> "2" -eq 2
True

该例分别把字符串和数字隐式转换为数字和字符串,然后执行比较。

需要记住的是隐式类型转换总是转换右侧的值,所以会伴生很多不合逻辑的结果。如两个变量是相等仅仅取决于比较的顺序,如下例:

PS C:\> 2 -eq "02"
True
PS C:\> "02" -eq 2
False

第1个表达式将字符串“02”转换为数字2,这样两个值相等;第2个表达式将数字2转换为字符串“02”,这样两个字符串不等。下例使用其他比较运算符。

PS C:\> 6 –lt "04"
True
PS C:\> "06" -lt 4
True

上例子中的数字6并不小于4(数字4由字符串“04”转换而来)。在第2个表达式中,作为一个字符串“06”中的0是比字符串“4”在字母表中先出现的,所以小于操作符返回True值。

逻辑和位操作

使用逻辑运算符将多个条件组合到一个复杂的表达式中。

(1)-and

与操作符,在操作符两边的操作数均为$true时返回$true。下例检测一个文件是否具有.txt后缀并且大小超过10 KB:

PS C:\> dir *.txt


    Directory: C:\

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---          2009/1/3      6:01      12100 largetext.txt

PS C:\> $file=Get-Item largetext.txt
PS C:\> $file.Extension -eq ".txt" -and $file.Length -gt 10KB
True

注意任何一个条件返回$false,将会使得整个表达式返回$false.:

PS C:\> $file.Length -gt 10KB
False
PS C:\> $file.Extension -eq ".txt" -and $file.Length -gt 10kb
False

(2)-or

或操作符,在任何一个操作数为$true时返回$true,下例检验文件是否为今天创建或者修改的:

PS C:\> $file.CreationTime.Date -eq [datetime]::Today -or `
>> $file.LastAccessTime.Date -eq [datetime]::Today
>>
True

其中使用CreationTime和LastAccessTime属性获取创建和访问时间,使用DateTime的Date属性来截断所需的日期。

(3)-xor

异或操作符,如果有一个操作数是$true,那么表达式返回$true;如果两个操作数均为$true,则返回$false,下例检查一个文件是否具有.txt后缀,或者只读的。如果两个条件同时满足,则排除在外:

PS C:\> attrib +r test.txt
PS C:\> attrib -r test2.txt
PS C:\> $file1=Get-Item test.txt
PS C:\> $file2=Get-Item test2.txt
PS C:\> $file1.Extension -eq ".txt" -xor $file1.IsReadOnly
False
PS C:\> $file2.Extension -eq ".txt" -xor $file2.IsReadOnly
True

上例为test.txt设定只读属性,并且清除test2.txt的只读属性。

(4)-not或者!

取反操作符,只有一个操作数,作用将其取反。如果操作数为$true,则返回$false。下例检查一个文件是否为只读:

PS C:\> $file=Get-Item test2.txt
PS C:\> $file.IsReadOnly
False
PS C:\> -not $file.IsReadOnly
True
PS C:\> !$file.IsReadOnly
True

(5)-band和-bor

按位操作与(-band)和按位或(-bor)操作符,仅用于整数。会按位比较两个操作数对应的位,结果为$true的位凑成的整数。尽管PowerShell不支持二进制数,二进制操作可以被理解为输入的数以二进制的形式出现并执行对应的位操作。下例使用-band按位与数字3和1:

PS C:\> 3 -band 1
1

数字3的二进制表示形式是11,1的二进制表示形式是01。-band操作符逐位执行与操作,两个数的二进制表示只有右面的一位同时为1,所以结果为二进制的01,同样在十进制中也是1。

11
-band    01
=        01

-bor的用法如下:

PS C:\> 2 -bor 1
3

数字2的二进制表示形式是10,1的表示形式是01。这样二进制的或操作将返回二进制的11,即十进制中的3:

10
-bor      01
=         11

与位操作在脚本语言中与外部交互时使用,如传递给程序一个标志位并验证其是否成功设置,为此需要执行按位与(-and)操作。更常见的例子是与.NET对象交互时返回一个枚举标志位,这些枚举的值是经过仔细挑选的,它们占用不同的位,而且其中的多位能够用二进制或操作合并为一个值。上面第1个例子中存在一个文件属性的枚举对象。文件属性(FileInfo)对象的属性将返回一个System.IO.FileAttributes枚举对象,其组成如表4-1所示。

如果在检测一个文件的属性时得到的值是十六进制的3,则说明这个文件同时具有只读和隐藏属性。因为1按位或2按位得到的值是3,即二进制中的11。下例使用这种方法来检测一个具有只读属性的文件:

PS C:\> $file=Get-Item C:\pagefile.sys -force
PS C:\> $file.Attributes -band 0x01
0
PS C:\> $file.Attributes -band 0x02
2
PS C:\> $file.Attributes -band 0x04
4

第1个操作的返回值是0,说明不是只读的;第2个操作返回非零值,说明已设置相应位,即该文件具有隐藏和系统属性。需要注意的一点是在使用Get-Item获取隐藏对象文件时使用了-force开关。

为了检测文件是否具有隐藏和系统属性,可以用6按位与其属性值,即2和4按位获得结果:

PS C:\> $file.Attributes -band 0x06
6

通常情况下为提高程序的可读性,会使用如下命令:

PS C:\> $file.Attributes -band (0x02 –bor 0x04)
6

笔者推荐使用变量来实现操作意图,如:

PS C:\> $hidden=0x02
PS C:\> $system=0x04
PS C:\> $file.Attributes -band ($hidden -bor $system)
6

布尔转换

包括位操作符在内的多个操作符返回数字类型的值,PowerShell可以自动将其转换为布尔类型的值,转换规则是任何非空值将会被转换为$true。非空的概念可以被延伸到更宽泛的范围,下的即PowerShell将会在需要时隐式转换为布尔值。也可以在任何值前加[bool]来显式执行强制类型转换,转换规则如下。

(1) 任何非零值将会被转换为$true,如:

PS C:\> [bool] 1
True
PS C:\> [bool] 5
True
PS C:\> [bool] 5000
True
PS C:\> [bool] 0
False

这意味着可在条件表达式中使用数字,PowerShell的解释引擎将会自动完成转换,如:

PS C:\> $true -and 3000
True

(2) 非零长度的字符串将会被转换为$true,如:

PS C:\> [bool] "true"
True
PS C:\> [bool] "false"
True
PS C:\> [bool] "something else"
True
PS C:\> [bool] ""
False

(3) 至少有一项的集合会返回$true,如:

PS C:\> [bool] (1,2)
True
PS C:\> [bool] ("one","two")
True
PS C:\> [bool] @()
False

(4) 其他对象将会被转换成$true,除非它们为$null。假设Word正在运行,下例将其对象转换为布尔类型的值:

PS C:\> [bool] (Get-Process winword)
True

可以依此类推,非$null值的对象会被转换为false,如:

PS C:\> $user=@{"Name"="LiMing";"Age"=20}
PS C:\> [bool]$user
True
PS C:\> [bool]$null
False

前面使用强制类型转换的代码只是为了描述PowerShell如何执行类型转换,即使未指定强制类型转换,PowerShell也会自动完成转换。

隐式类型转换通过一对取反操作符(!!),如把对象转换为布尔类型:

PS C:\> !!2
True
PS C:\> !!"test"
True
PS C:\> !!0
False
PS C:\> !!$null
False
PS C:\> !!(Get-Process winword)
True

靠近对象的一个感叹号将原始值隐式类型转换为布尔值并且将其取反,靠外侧的感叹号再次对结果取反。使用双感叹号能够在命令行中快速执行转换,但是在大型的脚本中最好还是使用强制类型转换,以提高其可读性。

字符互转换的作用

如前所述,-like和-match字符串操作符为真,可以用其检测字符串是否由特定模式组成或其中是否包含所需的字符串形式。当需要把各式对象转换为字符串时,这是很有用的,并且可以使条件表达更加紧凑和富有表现力。下例仍获取文件属性,System.IO.FileAttributes枚举对象可以很容易地转换为字符串:

PS C:\> $file=Get-Item C:\pagefile.sys -force
PS C:\> $file.Attributes
Hidden, System, Archive
PS C:\> [string] $file.Attributes
Hidden, System, Archive

可以使用-like操作符来检测这个文件是否为隐藏或者系统文件:

PS C:\> $file.Attributes -like "*Hidden*"
True
PS C:\> $file.Attributes -like "*System*"
True

也可以使用-or操作符检测这个文件存在隐藏或者系统属性:

PS C:\> $file.Attributes -like "*Hidden*" –or  $file.Attributes -like "*System*"
True

也可以使用-match操作符,通过正则表达式同时检测两个属性:

PS C:\> $file.Attributes -match "Hidden|System"
True

集合与条件表达式

绝大多数编程语言把集合作为单独的对象来处理,允许用户通过检查集合对象或使用对象集合内部的对象来书写条件表达式。PowerShell允许在条件表达式的左边使用集合。Shell解释引擎将会把条件表达式逐个应用到集合的成员上,结果是包含返回真值的成员新集合。事实上这个特性可以用来过滤集合,如下例:

PS C:\> 1,2,3,4,5 -lt 3
1
2
PS C:\> 1..6 -le 4
1
2
3
4

在包含任何类型对象的集合上使用条件表达式,下例获取字符串“LiMing”:

PS C:\> "LiMing","XiaoPang","WangGang","XiaoHu","LiMing" -eq "LiMing"
LiMing
LiMing

所有的条件表达式的规则在这里均可用,只是条件表达式的对象改为对象的集合,下例说明隐式类型转换的作用:

PS C:\> 3,4,5,6 -eq "4"
4

总 结

如果说算法是程序的灵魂,那么控制流就是程序运行的骨架。控制流直接决定了程序运行路径,控制流的内容将会贯穿整个PowerShell学习的全过程,本文主要讲解了条件表达式、分支语句和循环,掌握了这些内容之后读者就可以写出在日常工作中用到的完整功能的命令。

赛迪网地址:http://news.ccidnet.com/art/32857/20100610/2083135_1.html

作者: 付海军

版权:本文版权归作者所有

转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢

要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

个人网站: http://txj.shell.tor.hu/


发表回复