Windows PowerShell 学习笔记其三(字符串与数组)

字符串介绍

两种类型

PowerShell 下的字符串主要有两种类型:非扩展(nonexpanding)和扩展(expanding)型。
这里的“扩展”和“非扩展”指的是对字符串中包含的变量转义符是否进行解析。
扩展型字符串需要用双引号括起来,而非扩展型字符串使用单引号

实际效果如下:

1
2
3
4
5
6
PS > $username = "starky"
PS > echo "hi, my name is $username. `nAnother line"
hi, my name is starky.
Another line
PS > echo 'hi, my name is $username. `nwithin the same line'
hi, my name is $username. `nwithin the same line

在上面的例子中,双引号包裹的字符串对变量 $username 和转义符 `n(等同于 bash 里的 \n)进行了解析,自动替换为变量和转义符代表的内容。
而单引号包裹的字符串则只是将美元符、反引号等符号视为普通的字符,不做任何处理直接打印输出。

PS:PowerShell 里的转义符是反引号 ` 而不是 bash 里的反斜杠

多行格式化文本

PowerShell 支持创建 here string,即多行格式化文本,类似 Python 里的三引号。只需要将多行文本包裹在成对的 @""@ 符号中即可。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
PS > $mystring = @"
>> This is the first line
>> of a very long string. A "here string"
>> lets you create blocks of text
>> that span several lines.
>> "@
>>
PS > $mystring
This is the first line
of a very long string. A "here string"
lets you create blocks of text
that span several lines.

字符串中的动态内容

前面有提到过,由双引号包裹的字符串为“扩展”型字符串,可以对其中包含的变量等自动进行替换。
其实也可以在字符串中,以 $(expression) 的格式插入表达式或一系列 PowerShell 命令,示例如下:

1
2
PS > "1 + 2 equals $(1 + 2)"
1 + 2 equals 3

还可以使用 PowerShell 的字符串格式化操作符在字符串中插入动态内容,它遵循和 .NET 一样的字符串格式化规则。示例如下:

1
2
3
4
5
PS > $header = "Report for Today"
PS > $mystring = "{0}`n{1}" -f $header,('-' * $header.Length)
PS > $mystring
Report for Today
----------------

示例 2:

1
2
3
4
5
6
PS > $number1 = 10
PS > $number2 = 32
PS > "$number2 divided by $number1 is $($number2 / $number1)"
32 divided by 10 is 3.2
PS > "{0} divided by {1} is {2}" -f $number2, $number1, ($number2 / $number1)
32 divided by 10 is 3.2

字符串操作

检索与替换

PowerShell 提供了多种用于字符串搜索和匹配的方法。

  • -like
    -like 操作符用于判断字符串是否匹配特定的模式,该模式中可以包含通配符

    1
    2
    PS > "Hello World" -like "*llo W*"
    True
  • -match
    -match 操作符用于判断字符串是否匹配特定的正则表达式

    1
    2
    PS > "Hello World" -match '.*l[l-z]o W.*$'
    True
  • Contains()
    Contains() 方法用于判断一个字符串是否包含了另一个较短的字符串:

    1
    2
    PS > "Hello World".Contains("World")
    True
  • IndexOf()
    IndexOf() 方法可以用来获取一个字符串在另一个字符串中的位置索引:

    1
    2
    PS > "Hello World".IndexOf("World")
    6
  • Replace()
    Replace() 方法用于将字符串中的一部分替换为另一个字符串:

    1
    2
    PS > "Hello World".Replace("World", "PowerShell")
    Hello PowerShell

另外,使用 PowerShell 的 -replace 操作符搭配上正则表达式,可以完成更加高级的替换任务:

1
2
PS > "Hello World" -replace '(.*) (.*)', '$2 $1'
World Hello

分割、合并、修剪
  • -split
    -split 操作符可以用来将指定字符串分割成一系列的字符片段:

    1
    2
    3
    4
    5
    6
    7
    8
    PS > "a-b-c-d-e-f" -split "-c-"
    a-b
    d-e-f
    PS > "a-b-c-d-e-f" -split "b|[d-e]"
    a-
    -c-
    -
    -f
  • -join
    -join 操作符用于将多个字符片段合并为一个完整的字符串

    1
    2
    3
    4
    5
    6
    PS > $list = "Hello","World"
    PS > $list
    Hello
    World
    PS > $list -join ", "
    Hello, World
  • Trim()
    Trim() 方法用于去除字符串两边的空白字符:

    1
    2
    3
    PS > $text = " `t  Test String`t  `t"
    PS > "|" + $text.Trim() + "|"
    |Test String|

列表、数组与哈希表

创建数组和列表

创建和初始化一个数组在 PowerShell 里非常简单,用很常见的赋值语句即可:

1
2
3
4
5
PS > $myArray = 1,2,"HelloWorld"
PS > $myArray
1
2
HelloWorld

用上述方法创建的数组是没有数据类型限制的,即该数组在初始化时可以包含任何类型的数据。

创建指定长度的数组,可以使用 New-Object 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS > $myArray = New-Object "int32[]" 4
PS > $myArray[3] = 3
PS > $myArray
0
0
0
3
PS > $myArray[4] = 4
索引超出了数组界限。
所在位置 行:1 字符: 1
+ $myArray[4] = 4
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [], IndexOutOfRangeException
+ FullyQualifiedErrorId : System.IndexOutOfRangeException

创建指定类型的数组,可以使用 .NET 框架提供的强类型的集合:

1
2
3
4
5
6
7
8
9
10
PS > $list = New-Object Collections.Generic.list[Int]
PS > $list.add(10)
PS > $list.add("Hello")
无法将“Add”的参数“item”(其值为“Hello”)转换为类型“System.Int32”:“无法将值“Hello”转换为类型“System.Int32”。
错误:“输入字符串的格式不正确。””
所在位置 行:1 字符: 1
+ $list.add("Hello")
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

多维数组

PowerShell 可以使用 @() 形式的语法创建多维数组

1
2
3
4
5
6
7
8
PS > $jagged = @(
>> (1,2,3,4),
>> (5,6,7,8)
>> )
PS > $jagged[0][0]
1
PS > $jagged[1][3]
8

也可以使用下面的方式:

1
2
3
4
5
6
7
8
9
10
11
12
PS > $multidimensional = New-Object "int32[,]" 2,4
PS > $multidimensional[0,0] = 1
PS > $multidimensional[1,3] = 8
PS > $multidimensional
1
0
0
0
0
0
0
8

操作数组中的元素

可以通过位置索引(从 0 开始)获取数组中的某个元素:

1
2
3
4
5
PS > $myArray = 1,2,"Hello World"
PS > $myArray[0]
1
PS > $myArray[2]
Hello World

当然也可以对数组进行分片操作,即获取数组中的某“一段”元素:

1
2
3
4
5
6
7
PS > $myArray
1
2
Hello World
PS > $myArray[1..2]
2
Hello World

在对数组进行分片时,PowerShell 提供了如下的一个小技巧,可以对输出后的元素进行灵活的排序:

1
2
3
4
5
6
7
8
PS > $myArray = 0,1,2,3,4,5
PS > $myArray[3..5 + 2 + 0..1]
3
4
5
2
0
1

Foreach-Object

如果需要挨个访问数组中的每一个元素,可以使用 Foreach-Object 命令:

1
2
3
4
5
PS > $myArray = 1,2,3
PS > $sum = 0
PS > $myArray | Foreach-Object { $sum += $_ }
PS > $sum
6

当然也可以稍微复杂点,通过位置索引和 for 循环访问数组的每一个元素:

1
2
3
4
5
6
7
PS > $myArray = 1,2,3
PS > $sum = 0
PS > for($i = 0; $i -lt $myArray.Count; $i++) {
>> $sum += $myArray[$i]
>> }
PS > $sum
6

排序

使用 Sort-Object 命令可以对数组元素进行排序后再输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PS > dir


目录: D:\Program\python


Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2019/2/28 22:43 pyqt
d----- 2019/3/3 0:17 speech
-a---- 2019/3/6 22:13 26434 test.html
-a---- 2019/3/6 22:00 174 test.md

PS > Get-ChildItem | Sort-Object -Descending Length | select Name,Length

Name Length
---- ------
test.html 26434
test.md 174
pyqt
speech

使用 Get-ChildItem 获取当前目录下所有文件的列表,再把该列表传递给 Sort-Object ,根据文件占用空间的大小(Length)逆序输出 Name 和 Length 项。

在使用 Sort-Object 对元素进行排序时,可以自由选择排序依据的条件。如根据首字母对字符串进行排序:

1
2
3
4
5
PS > "Hello","World","And","Shell" | Sort-Object
And
Hello
Shell
World

根据次字母对字符串进行排序:

1
2
3
4
5
PS > "Hello","World","And","Shell" | Sort-Object { $_.Substring(1,1) }
Hello
Shell
And
World

数组与运算符
确定数组与元素的包含关系

确定数组与元素的包含关系,可以使用 -contains 或者 -in 操作符:

1
2
3
4
5
6
7
8
PS > "Hello","World" -contains "Hello"
True
PS > "Hello","World" -contains "Shell"
False
PS > "Hello" -in "Hello","World"
True
PS > "Shell" -in "Hello","World"
False

合并数组

可以使用算术运算符 + 对数组进行合并操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS > $firstArray = "Element 1","Element 2","Element 3"
PS > $secondArray = 1,2,3
PS > $firstArray + $secondArray
Element 1
Element 2
Element 3
1
2
3
PS > $array = 1,2
PS > $array += 3,4
PS > $array
1
2
3
4

匹配数组中的元素

可以使用 -eq-match-like 操作符对数组中的元素进行匹配:

1
2
3
4
5
6
7
8
9
10
PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array -eq "Item 1"
Item 1
Item 1
PS > $array -like "*1*"
Item 1
Item 1
Item 12
PS > $array -match "Item .."
Item 12

其中 -eq 表示完全匹配,-like 支持使用通配符-match 支持正则表达式。

更复杂的匹配条件可以使用 Where-Object

1
2
3
PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array | Where-Object { $_.Length -gt 6 }
Item 12

移除数组中的元素

为了移除数组中符合特殊规则的元素,可以使用 -ne-notlike-notmatch 等比较操作符:

1
2
3
4
5
6
7
8
9
10
11
12
13
PS >$array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS >$array -ne "Item 1"
Item 2
Item 3
Item 12
PS > $array -notlike "*1*"
Item 2
Item 3
PS > $array -notmatch "Item .."
Item 1
Item 2
Item 3
Item 1

获取大于或小于特定值的元素

-gt-ge-lt-le 等比较操作符可以用来获取数组中大于或小于某个特定值的元素。

PS:其中 -gt 表示大于(great than),-ge 表示大于等于(great and equal),-lt 表示小于(less than),-le 表示小于等于(less and equal)。

1
2
3
4
5
6
7
PS > $array = "Item 1","Item 2","Item 3","Item 1","Item 12"
PS > $array -ge "Item 3"
Item 3
PS > $array -lt "Item 2"
Item 1
Item 1
Item 12

ArrayList

通过类似 $array = 1,2,3,4 这种赋值的方式创建的数组,其长度固定的。
可以通过位置索引访问其中的某个值,并对它重新赋值。但是不能直接添加或者删除数组中的元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS > $array = 0,1,2,3
PS > $array[1]
1
PS > $array[0]=1
PS > $array
1
1
2
3
PS > $array.Add(4)
使用“1”个参数调用“Add”时发生异常:“集合的大小是固定的。”
所在位置 行:1 字符: 1
+ $array.add(4)
+ ~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException

如果需要在数组中添加或者删除元素,可以使用 +-ne-match-gt 等比较操作符,获取数组中匹配某个条件的元素,并将它们赋值给新的变量,原数组中元素的值则不受影响:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PS > $original = 1,2,3,4
PS > $new1 = $original + 5
PS > $new1
1
2
3
4
5
PS > $new2 = $original -ne 4
PS > $new2
1
2
3
PS > $new3 = $original -gt 2
PS > $new3
3
4
PS > $original
1
2
3
4

在面对长度很大的数组时,上述的添加、移除、搜索等操作就会稍微显得效率较低。所以 PowerShell 提供了 ArrayList 数据类型,可以直接对数组本身进行添加、删除等操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS > $collection = New-Object System.Collections.ArrayList
PS > [void] $collection.Add("Hello")
PS > [void] $collection.AddRange(("World","How","Are","You"))
PS > $collection
Hello
World
How
Are
You
PS > $collection.RemoveAt(1)
PS > $collection
Hello
How
Are
You

[void] 可以省略操作执行后返回的状态值。

哈希表

可以使用 @{} 形式的语法创建哈希表(或关联数组)。

1
2
3
4
5
6
7
8
9
PS > $myHashtable = @{ Key1 = "Value1"; "Key 2" = 1,2,3 }
PS > $myHashtable["New Item"] = 5
PS > $myHashtable

Name Value
---- -----
Key 2 {1, 2, 3}
Key1 Value1
New Item 5

按哈希表的键或值排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
PS > $myHashtable = @{}
PS > $myHashtable["Hello"] = 3
PS > $myHashtable["Ali"] = 2
PS > $myHashtable["Alien"] = 4
PS > $myHashtable["Duck"] = 1
PS > $myHashtable

Name Value
---- -----
Hello 3
Duck 1
Alien 4
Ali 2


PS > $myHashtable.GetEnumerator() | Sort Name

Name Value
---- -----
Ali 2
Alien 4
Duck 1
Hello 3

PS > $myHashtable.GetEnumerator() | Sort Value

Name Value
---- -----
Duck 1
Ali 2
Hello 3
Alien 4

参考书籍

Windows PowerShell Cookbook, 3rd Edition