代码改变世界

Windows PowerShell 2.0语言的内置类型系统

2010-11-16 00:51 @天行健中国元素 阅读(...) 评论(...) 编辑 收藏

PowerShell不仅允许用户操作各种类型的对象,而且可以通过统一方式来修改这些类型。当然也包括内置类型,这样可以更贴近日常工作中用到的对象类型。PowerShell的类型在很大程度上依靠.NET框架,并且所有内置类型即.NET的类型。Shell的设计者扩展这些类型并为语言添加了特殊的语法快捷方式来让用户更方便地使用它们。

1 字符串和字符串操作

字符串值在任何编程语言中均为常用数据类型之一,主要是因为字符串包含自由形态的数据,而且经常被用做系统间的数据传输。PowerShell的字符串建立在.NET的System.String对象类型之上,这意味着字符串包含.NET字符串对象的所有操作;另外以.NET为基础,字符串包含很多增加表现力和操作能力的表达式。

字符串简单操作

创建一个字符串只需要用一对单引号‘’或者双引号“”括起字符序列,如下例:

PS C:\> 'This is a String'
This is a String
PS C:\> "This is a String"
This is a String

两种类型的引号允许在不做任何转义处理即可在字符串中包含引号本身,如下例:

PS C:\> 'John said :"It is ok!"'
John said :"It is ok!"
PS C:\> "Let's go"
Let's go

如果字符串中会因包含复杂且混合的引号而变得臃肿,则只需要对引号进行转义处理,即把符号重复一遍即可:

PS C:\> "John said:""OK."""
John said:"OK."
PS C:\> 'Let''s go'
Let's go

另外一种处理引号转义的方法是使用转义字符反引号“`”(注意不包含双引号本身,这个符号是在键盘上和~在同一个按键上,在英文中被称为“backtick或backquote”),如下例:

PS C:\> "John said:`"OK`""
John said:"OK"

注意反引号的转义作用仅在双引号字符串中有效,通常用于很难从键盘输入的字符的转义,如以下字符。

(1)`o:通常被用做一个字符串结束符,此符号无法打印的。

(2)`a:警告符号,即ASCII码为7的字符。在屏幕上显示一个包含这个符号的字符串时,计算机的喇叭将会发出“哔哔”的警告,此符号无法打印。包含这个字符的字符串示例如下:

PS C:\> "John said:`"OK`"`a"
John said:"OK"

(3)`b:退格(同backspace),删除前一个字符,如:

PS C:\> "aa`b bb"
a bb

(4)`f:换页或分页符,此符号将会让打印机在新的一页上开始打印,无法打印。

(5)`n:换行符,使打印机或终端在新的一行开始打印,如:

PS C:\> "aa`nbb"
aa
bb

(6)`r:回车符,使打印机或终端在行首开始打印,它不会生成新行,并且后面打印的字符将会在当前行的开始处打印,所以可能会覆盖之前的输出。在Windows系统中,该符号经常配合`n来在文本文件中开始一个新行。`r`n组合将会生成一个新行。

(7)`t:水平制表符,根据当前控制台的配置向前移动几个字符,如:

PS C:\> "aa`tbb"
aa      bb

(8)`v:垂直制表符,英语打印或者格式化控制台输出。

在控制台打印时,反引号很有用,可以将其作为一个新行的转义字符。如果在命令的行末添加一个反引号,对于Shell,命令将会在下一行继续,如:

PS C:\> Get-Item C:\WINDOWS\System32\WindowsPowerShell\v1.0\types.ps1xml `
>> |Get-Content |Measure-Object
>>

Count    : 3187
Average  :
Sum      :
Maximum  :
Minimum  :
Property :

插入字符串

在很多脚本和Shell语言中字符串可作为变量替换插入是一个重要的特性,Shell将会展开这些变量,并用其值来替换字符串中的变量。使用这个特性来格式化输出,以在将其在传递到其他程序之前格式化,如:

PS C:\> $processCount=(Get-Process).Count
PS C:\> "$processCount Porcesses Running in the system."
44 Porcesses Running in the system.

如果不需要将美元符表达式作为变量名,则使用反引号转义美元符,如:

PS C:\> "`$processCount is the property that we need."
$processCount is the property that we need.

另外一种方法是用单引号字符串来代替,变量插入功能仅在双引号字符串中有效,如:

PS C:\> '$processCount is the property that we need.'
$processCount is the property that we need.

字符串插入功能的示例如下:

PS C:\> @"
>> Processes
>> ------------
>> $processCount
>> "@
>>
Processes
------------
44

可以类推到单引号字符串,这里使用单引号字符串时字符串插入功能无效。在字符串中可以使用复杂的变量表达式,但是必须严格遵循格式化规则,变量分解会在第1个单词结束。使用一个复杂表达式时,需要将其放在$()代码块中,如:

PS C:\> "$($process.Count) process running in the system."
44 process running in the system.

如果在上例中未添加$(),即仅仅以$process.Count变量和字符串的组合输出,则结果是$process。之前为这个变量赋值为Get-Process,即当前运行程序。输出是这些运行程序的清单,并不是目标中需要的运行程序的数目。

注意变量在$()块中仍然需要添加美元符的前缀,下例试图完全摆脱$process变量:

PS C:\> "$($process.Count) process running in the system."
44 process running in the system.

在字符串中可以嵌入任何合法的PowerShell表达式,如:

PS C:\> "Total due:$(12 * 5000)"
Total due:60000

甚至可以用嵌入的表达式来动态改变一个变量来输出,如:

PS C:\> $times=0
PS C:\> "Operation performed $($times++;$times++;$times) times"
Operation performed 2 times
PS C:\> $times
2

在表达式$($times++;$times++;$times)中先后两次使用自增运算符改变$times变量值,表达式的值相当于执行前两个操作后的$times值。

通配符匹配

通配符匹配和使用正则表达式匹配类似,只是不需要任何强大的内置正则表达式。这种方法的优点是自动且便于使用,如下例:

PS C:\> "Test string" -like "Test*"
True
PS C:\> "Simple string" -like "Test*"
False

其中的星号代表零到多个任意字符,可以用问号来精确匹配单个字符,如下例:

PS C:\> "notepad.exe" -like "notepad.???"
True
PS C:\> "notepad.exe" -like "notepad.?"
False
PS C:\> "notepad.exe" -like "notepad.??"
False
PS C:\> "notepad.exe" -like "?otepad.exe"
True

也可以使用字符串序列,下例检测字符串是否以任意字母开头并以“s”或“n”结束:

PS C:\> "notepads" -like "[a-z]*[sn]"
True
PS C:\> "notepadz" -like "[a-z]*[sn]"
False
PS C:\> "-notepads" -like "[a-z]*[sn]"
False

正则表达式匹配

正则表达式匹配与通配符匹配类似,区别是使用-match操作符并提供一个正则表达式参数。下例说明如何来匹配类似域名的字符串:

PS C:\> "www.baidu.com" -match "(www\.)?\w+\.(com|org|net|cn)"
True
PS C:\> "baidu.com" -match "(www\.)?\w+\.(com|org|net|cn)"
True
PS C:\> "baidu.cn" -match "(www\.)?\w+\.(com|org|net|cn)"
True

表达式的字符串特征为以“www.”开头,其后为一个或者多个字母数字、一个点(.),以及“com”、“org”、“net”或者“cn”。注意点(.)在正则表达式中有特殊作用,它代表任何字符,所以使用一个反斜杠(\)对其进行转义。

【提示】

关于正则表达式的更多信息可以访问http://www.regular-expressions.info,其中涵盖了正则表达式的不同编程语言和环境。在不同的平台上正则表达式的语法或多或少会有些相似,其中包含了一些很微妙的差异,请读者务必注意。所浏览到的文章是关于.NET正则表达式的,绝大多数关于.NET的文章集中在http://www.regular-expressions.info/dotnet.html中。PowerShell使用.NET的正则表达式,请务必留意。

替换字符串或子字符串

PowerShell提供了-replace操作符替换出现的字符串,.NET中的String.Replace()方法将字符串作为一个参数,替换出现的目标字符串的情况。如下例:

PS C:\> "one.test,two!test".Replace(".test","-->DONE")
one-->DONE,two!test

-replace带一个正则表达式并且在其中调用Regex.Replace()。注意在替代模式下,根据不同情况需要转义正则表达式中的特殊字符。在下例中的-replace并没有达到预期的目的:

PS C:\> "one.test,two!test" -replace ".test","-->DONE"
one-->DONE,two-->DONE

这是因为正则表达式的点(.)作为用于匹配任意一个字符,所以字符串中的感叹号被替换。为了改正这个错误,我们需要对点转义:

PS C:\> "one.test,two!test" -replace "\.test","-->DONE"
one-->DONE,two!test

数字类型

PowerShell支持所有.NET内置的数字类型,为方便读者理解,在描述时与C语言相对比。

整型

System.Int32如同C语言中的int类型,是一个32位整型数值,创建方法是键入一个阿拉伯数字:

PS C:\> (3).GetType().FullName
System.Int32

浮点数

单精度和双精度的浮点数对应于C语言中的single或float,以及double,常用的类型是double,通过使用相应的数字序列来实现:

PS C:\> (3.0).GetType().FullName
System.Double
PS C:\> (-3E2).GetType().FullName
System.Double
PS C:\> (4.5E-2).GetType().FullName
System.Double

需要注意科学计数法需要指定尾数和指数。

十进制数

System.Decimal对应于十进制数,创建十进制数据需要在数字后面添加d的后缀,如:

PS C:\> 2d
2
PS C:\> 2.5d
2.5
PS C:\> 2.5d * 10000000000000000000000
25000000000000000000000.0

十六进制数

创建十六进制数需要添加前缀0x与其他进制的数字区别,如:

PS C:\> 0x10
16
PS C:\> 0xABCDEF
11259375

PowerShell增加了进制乘法器后缀,能够通过在数字后添加KB、MB及GB来分别得到1 024、1 048 576和1 073 741 824的乘积。下例获取运行中程序的内存使用情况:

PS C:\> Get-Process winword

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    509      24    55328     119452   653   568.67   6124 WINWORD

PS C:\> (Get-Process winword).VM -ge 650MB
True
PS C:\> (Get-Process winword).VM -ge 654MB
False
PS C:\> (Get-Process winword).VM -ge 653MB
False
PS C:\> (Get-Process winword).VM / 1MB
652.7890625

winword占用的内存大约是653 MB。以650 MB探测,结果为True(真);用654 MB和653 MB来探测,返回False(假)。用获取的内存值除以1 MB,结果值介于652 MB和653 MB之间。也可以用类似的方法来获得计算机中的文件和对象,如:

PS C:\> (Get-Item F:\pagefile.sys -Force).Length / 1GB
1.998046875

Pagefile.sys是个系统文件,正常情况下不允许一般进程访问。所以我们使用-Force参数来强行访问它,这样的访问是安全且被允许的。

字符串类型的数字

将数字类型格式化并转换为字符串类型使用-f字符串操作符,其中特别的格式化选项如下。

(1)货币:使用{0:c}格式化字符串会根据当前系统的区域和语言设置在数值前面增加相应的币符标志,下例中第1个语句是根据当前计算机默认系统语言为“中文(中国)”的情况下格式化数值的输出;第2个语句是将系统语言改为“英语(美国)”后的输出,PowerShell读取系统中的配置来根据对应输出相应的币符:

PS C:\> "{0:c}" -f 2.5
¥2.50
PS C:\> "{0:c}" -f 2.5
$2.50

(2)百分比:格式化字符串为{0:p},如:

PS C:\> "{0:p}" -f 0.2
20.00%

(3)修正保留小数位:用井号#来作为数字占位符,下例在小数点后面保留3位有效数字,其中采用四舍五入:

PS C:\> "{0:#.###}" -f 3.4567
3.457
PS C:\> "{0:#.###}" -f 3.4564
3.456

(4)科学计数法:格式化字符串{0:e}规格化一个底数并得到幂指数。默认情况下,底数总会保留小数点后6位。而且最后一位不四舍五入,如:

PS C:\> "{0:e}" -f 0.000333456789123456
3.334568e-004
PS C:\> "{0:e}" -f 333.456789
3.334568e+002
PS C:\> "{0:e}" -f 33345.6789
3.334568e+004
PS C:\> "{0:e}" -f 33345678.9
3.334568e+007
PS C:\> "{0:e}" -f 333456789123456
3.334568e+014

其他在.NET中的格式化字符串还有很多,具体情况请参阅MSDN中的System.Globalization.NumberFormatInfo类文档。

细心的读者应该能够看到,在不知道PowerShell语言的具体细节特点的情况下,可以用了一些方法来获取一些信息,即完全把PowerShell完全当作一个黑盒子,靠构造的特殊输入获得一些相关的信息。学习一种语言,更多的时候需要靠自己的细心来获得一些设计者在设计语言时的巧妙想法。

数组与集合

在编程中数组和其他序列化的集合起着重要的作用,本节交替使用“数组”和“有序集合”来讲解PowerShell适配器工作机制中类似的序列化集合。

为创建数组,罗列其所有的元素并用逗号分隔,如下例:

PS C:\> 1,2,3,4,5
1
2
3
4
5
PS C:\> (1,2,3,4,5).GetType().FullName
System.Object[]

需要注意的一点是数组是Object类型,这意味着能够操作其中的对象,如下例:

PS C:\> 1,2.5,"Apples",(Get-Process winword)
1
2.5
Apples

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
    369      14    15008      11788   384     2.31   1120 WINWORD
    524      25    62500     128564   558   747.91   6124 WINWORD

PS C:\> (1,2.5,"Apples",(Get-Process winword)).Count
4

上述代码创建了一个包含整数、double精度的浮点数、字符串及Process对象的对象数组,这4种类型的对象综合起来就是一个int、double、string和System.Diagnostics.Process实例。

创建数组的常见语法是把成员对象包含在@()块中,如:

PS C:\> @("one","two")
one
two

下面的方法是创建空数组的唯一方式:

PS C:\> (@()).Count
0
PS C:\> (@()).GetType().FullName
System.Object[]

可以通过起始和结束数字的数值区间序列来建立数组,程序自动填充剩余的数值,如:

PS C:\> 5..1
5
4
3
2
1
PS C:\> 1..5
1
2
3
4
5
PS C:\> (0..255).Count
256

这种语法结构对于列举端口、地址和各种类型的有序序列很有用。创建的数组语法可以识别嵌套的数组语法并且将其转换为一个对象,这样允许在合适的位置添加数组和序列,如:

PS C:\> 1,(5..7),2
1
5
6
7
2
PS C:\> 1,(10..7),2
1
10
9
8
7
2

可以使用方括号[]来访问数组元素,如:

PS C:\> $a=2,3,4
PS C:\> $a[1]
3
PS C:\> $a=2,3,4
PS C:\> $a[0]
2
PS C:\> $a[1]
3
PS C:\> $a[2]=5
PS C:\> $a[2]
5

注意可以通过直接为其赋值来修改数组元素。数组下标从0开始编号,最后一个元素的编号是数组大小减1。

可以在方括号中传递多个编号,返回值是包含这些编号对应的数组元素,称为“数组片段”。下例获取数组的第1个和最后一个元素:

PS C:\> $a=2,3,4
PS C:\> $a[0,2]
2
4

上例的问题是需要知道数组有3个元素,所以要获取编号为2的元素。PowerShell允许从结尾开始以负数编号来定位元素,这样在上例中可以不用指定数组长度的大小而定位最后一个元素:

PS C:\> $a=2,3,4
PS C:\> $a[0,-1]
2
4

同样我们可以通过传递索引值的范围来创建数组片段,下例从第3个元素开始取得5个元素:

PS C:\> $a=(1,2,3,4,5,6,7,8)
PS C:\> $a[2..6]
3
4
5
6
7

把数组A赋值给数组B的元素将会扩大数组B,这是在特定位置插入多个元素的简便方法,如:

PS C:\> $a=1,2,3
PS C:\> $a[1]=10,20,30
PS C:\> $a
1
10
20
30
3

数组对象也可用加运算符串联,将一个对象或者数组“加在”数组上将会返回包含所有对象的一个新的数组,如:

PS C:\> $a=1,2
PS C:\> $a=$a+3
PS C:\> $a
1
2
3

上例在变量中增加一个元素。PowerShell支持快捷操作符“+=”,如:

PS C:\> $a+=4
PS C:\> $a
1
2
3
4
PS C:\> $a+=5,6
PS C:\> $a
1
2
3
4
5
6

下例从对象集中搜索元素:

foreach($item in (1,2,3,4)){if($item -eq 3){echo "Found"}}

该例在从数组元素中查找时重复相同的循环,更简便的方法是使用PowerShell提供两个特定操作符-contains和-notcontains,如:

PS C:\> (2,3,4) -contains 3
True
PS C:\> (2,3,4) -notcontains 3
False
PS C:\> (2,3,4) -notcontains 5
True

这些操作符也可以用在不同的变量类型,如:

PS C:\> (2,"some value",4) -contains "some value"
True
PS C:\> (2,"some value",4) -contains "Some valuE"
True

从上例中可以看到匹配对大小写不敏感。

要小心地转换内置类型,因为可能会得到完全不同的结果,如:

PS C:\> (2,3,4) -contains "3"
True
PS C:\> (2,3,4) -contains "3.0"
True

如果需要查找字符串并避免类型带来的影响,则需要使用循环或Where-Object命令,如:

PS C:\> (2,3,4)| Where-Object{$_ -is [string] -and $_ -eq "3.0"}
PS C:\> (2,"3.0",4)| Where-Object{$_ -is [string] -and $_ -eq "3.0"}
3.0

字典和哈希表

字典也被称为“联合数组”,是一种序列字段映射到序列值的对象,其中包含大量键-值组合对并允许快速通过键查找到值。字典是个存储着包含现实世界逻辑记录相关数据的实体。在PowerShell中创建字典的语法结构和创建数组类似,不同之处是使用分号和花括号的组合来分隔。下面是一个创建个人记录字典的实例,其中的两个键元素分别是“Name”和“Address”:

PS C:\> $d=@{"Name"="John";"Address"="12 Easy St."}
PS C:\> $d

Name                           Value
----                           -----
Name                           John
Address                        12 Easy St.

在建立字典对象时,PowerShell通过调用System,Collection.Hashtable对象来实现。哈希表执行.NET中的IDictionary接口,PowerShell的扩展类型系统允许使用与所有IDictionary对象一样的方式来工作,如:

PS C:\> $d.GetType().FullName
System.Collections.Hashtable

字典没有限制对象类型,可以使用任何键和值,下例添加不同的对象到哈希表中:

PS C:\> $d=@{"Name"="John";"Age"=30`
>> ;"File"=(Get-Item C:\PowerShell\Test.txt)}
>>
PS C:\> $d

Name                           Value
----                           -----
Name                           John
Age                            30
File                           C:\PowerShell\Test.txt

$d中包含字符串、整型和FileInfo对象。事实上,并没有限制只有字符串才能作为字典的键。可以使用系统的计算器和Word来创建一个Process对象集,并映射到对应的字符串,如:

PS C:\> $set = @{(Get-Process Winword)="MS Word";(Get-Process calc)="calc"}
PS C:\> $set

Name                           Value
----                           -----
System.Diagnostics.Process ... MS Word
System.Diagnostics.Process ... calc

可以用@{}表达式来创建空字典:

PS C:\> $empty=@{}
PS C:\> $empty.GetType().FullName
System.Collections.Hashtable

访问字典值的重要方法是用方括号括起的键名,如:

PS C:\> $d=@{"Name"="John";"Age"=30;"Address"="12 Easy St."}
PS C:\> $d["Name"]
John

字典可以作为对象来访问子对象,使用点分隔符访问其值,如:

PS C:\> $d=@{"Name"="John";"Age"=30;"Address"="12 Easy St."}
PS C:\> $d.Name
John

字典对象支持多种方式来访问键值,如使用变量作为键:

PS C:\> $property="Name"
PS C:\> $d.$property
John

甚至是一个表达式:

PS C:\> $d.$($property)
John
PS C:\> $d.$("Na" + "me")
John

字典支持在方括号中放置多个键名,返回的对象为包含值的数组。下例从之前建立的字典中获取人名和住址:

PS C:\> $d=@{"Name"="John";"Age"=30;"Address"="12 Easy St."}
PS C:\> $d["Name","Address"]
John
12 Easy St.

为字典添加元素仅仅添加了键-值对,而不添加对象或者数组,如:

PS C:\> $d.Department="Accounting"
PS C:\> $d["NO."]=123456789
PS C:\> $d

Name                           Value
----                           -----
Department                     Accounting
Name                           John
Age                            30
Address                        12 Easy St.
NO.                            123456789

如果要移除一个元素,使用Remove()方法并提供键名,如:

PS C:\> $d.Remove("Age")
PS C:\> $d.Remove("NO.")
PS C:\> $d

Name                           Value
----                           -----
Department                     Accounting
Name                           John
Address                        12 Easy St.

Contains()和ContainsKey()方法用来字典中是否存在给定的键,如:

PS C:\> $d.Contains("John")
False
PS C:\> $d.Contains("Name")
True
PS C:\> $d.ContainsKey("Name")
True

ContainValue()是用来查找一个键值是否存在:

PS C:\> $d.ContainsValue("Name")
False
PS C:\> $d.ContainsValue("John")
True

另外,字典对象使用Keys和Values属性来向外展示键名和键值,如:

PS C:\> $d.Key
PS C:\> $d.Keys
Department
Name
Address
NO.
PS C:\> $d.Values
Accounting
John
12 Easy St.
123456789

可以使用数组检索来替代调用ContainsKey()和ContainsValue(),如:

PS C:\> $d.Keys -contains "Name"
True
PS C:\> $d.Values -contains "John"
True

需要记住的是-contains会在后台自动执行类型转换,而ContainsKey()和ContainsValue()不会。

类似于.NET对象集,字典的Count属性包括内部存放的键-值对的数量,如:

PS C:\> $d=@{"Name"="John";"Age"=30;"Address"="12 Easy St."}
PS C:\> $d.Count
3

总 结

PowerShell作为强大且复杂的语言,并没有过于严格或对新手不友好。因为它把所有的对象类型通过适配器和扩展统一存取,所以用户能够找到快速入手的途径。最常用的对象是获取实体扩展,从而提高了脚本编写者的创造性,并降低了学习的难度。本文针对PowerShell常见的字符串、数字、数组、集合、字典和哈希表等内置类型进行介绍,并对其常见的操作进行描述,便于读者的理解和掌握。

 

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

 

作者: 付海军
出处:http://fuhj02.cnblogs.com
版权:本文版权归作者和博客园共有
转载:欢迎转载,为了保存作者的创作热情,请按要求【转载】,谢谢
要求:未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
个人网站: http://txj.shell.tor.hu