变量

使用变量保存运行中需要的数据。

类型系统

PowerShell中的类型对应.NET类型,常用类型在PowerShell中可以使用简写。

类型名称 .NET类型名称 C#关键字 说明
int System.Int32 int 32位有符号整数
short System.Int16 int 16位有符号整数
double System.Double double 双精度浮点数
float System.Single float 单精度浮点数
decimal System.Decimal decimal 小数
string System.String string 字符串
bool System.Boolean bool 逻辑类型
char System.Char char 字符
datetime System.DateTime - 时间日期
timespan System.TimeSpan - 时间段
guid System.Guid - 全局唯一标识符
switch SwitchParameter - 开关选项
psobject PSObject - PowerShell增强类型
pscustomobject PSCustomObject - PowerShell自定义增强类型
regex System.Text.ReguarExpressions.Regex - 正则表达式
xml System.Xml.XmlDocument - Xml文档
type System.Type - 类型信息
object System.Object object 对象,所有类型的根

定义变量

语法:

[类型]$变量名 = 初始值

PowerShell中,变量使用$开头,无需提前申明。变量不区分大小写

其中的类型说明是可选的,可以不需要类型。如果不说明类型,变量的类型是object,可以保存任意类型。

直接使用变量时,会将变量输入到字符串。

如果变量有类型,赋予的值可以和变量的类型不一致,此时会产生隐式的转换。

$s = "hello"
$s = 123
$s # 输出变量
[int]$a = "100"
$a
$a | Get-Member # 查看变量的类型
$a = "def"  #将def转换为数字失败,赋值出错

定义变量后,可以重新定义并改变类型。

[int]$a = "100"
[string]$a = "abc"
$a

通过New-Variable可以对变量进行设定,如定义只读变量。

New-Variable "b" 100 -Option Readonly
$b
$b = 100 # 不能修改只读变量

可以使用变量保存命令结果。

$files = dir *.txt
$files

管理变量

可以通过驱动器variable:查看变量。

dir variable:

也可以删除变量

$a = "abc"
dir variable:a
rm variable:a
dir variable:a

通过Remove-Variable命令,也可以删除变量。

$a = "abc"
Remove-Variable a -Force

使用成员

PowerShell变量都是.NET对象,可以使用.访问其成员。

[int]$a = 100
$a.ToString("x") # 64
$a.GetType().FullName # System.Int32

注意:如果要使用命令结果中的成员,需要将命令括起来,否则成员访问会被当做参数提供给命令。

(Get-Process -Id 4).Name

整数

PowerShell允许10进制数字,0x开头的16进制数字,0b开头的二进制数字。

特别的,PowerShell允许在整数后边添加字节大小后缀,如kb,mb,会换算成字节大小。

$size = 1gb
$size #1073741824

支持字节大小简化了文件大小计算。

找出大于4兆的图片

dir *.jpg | where Length -gt 4mb

小数

PowerShell支持小数。

小数可以是小数点格式或科学计数格式。 小数点整数部分如果是0,可以忽略。

$a = 3.14
$b = 0.19e100

小数默认是double类型,可以添加类型说明改变类型。

[decimal]$a = 3.14
$a

字符串

字符串可以使用单引号'或者使用双引号"包围。 要使用特殊字符,如换行、制表符,需要使用反引号`开头的转义字符。如`t表示制表符。 使用单引号的字符串不会被进一步解释,使用双引号的字符串会被解释,其中包含的$开头部分会被解释为代码。 在双引号中要使用$字符自身,需要两个字符$$

$a = 100
$b = 200
$c = '$a+$b'
$c #$a+$b
$d = "$a+$b"
$d #100+200

可以看到,变量d的字符串中变量被替换了

双引号$引用的代码如果和文本连起来了,为了区分变量,需要使用括号。 在访问对象的成员时,一般都需要添加括号。

$s = "abc"
"$s def" # 有空格,可以区分,不需要括号
"$($s)def" # 没有空格,没有括号会被理解为访问变量`$sdef`。最左边的`$`是为了将括号解释成代码,而非括号本身。
"($s)def" # 这里的括号是一般字符,不被进一步解释,它也可以区分变量。和上面不同的是,结果会包含括号。细心理解区别。
"$宿主名称:$($host.Name)" # 使用括号可以访问变量的成员,不加括号会将`.Name`解释一般字符。

PowerShell还支持使用@包裹的多行字符串。 @包裹的字符串可以是单引号'或双引号",后者会被解释。 首个@后需要换行,最后一个@需要在新行,字符串为两个换行间的字符,不包括换行自身。

$s = @'
山重水复疑无路,
柳暗花明又一村。
'@
$s

创建.NET对象

使用New-Object命令可以创建.NET对象

$wc = New-Object System.WebClient
$wc.DownloadString("https://www.baidu.com)
$wc.Dispose();

创建对象后,可以和C#一样,使用其成员。成员名称不区分大小写,会匹配最适合的成员。

创建泛型类型时,泛型参数使用方括号括起来

$dict = New-Object "System.Collections.Generic.Dictionary[string,string]"
$dict.Add("Apple","苹果")
$dict.Add("Orgame","橘子")
$dict

如果要访问.NET中的静态成员,需要使用[类型全名]::成员来访问。

[System.DateTime]::Now # 访问时间
[System.Environment]::OSVersion # 访问系统版本
[System.Threading.Thread]::CurrentThread # 访问当前线程
[System.Environment]::Exit(0) # 甚至可以结束PowerShell程序

使用New-Object命令还可以创建COM对象

$obj = New-Object -ComObject SAPI.SpVoice # 系统的语音合成
$obj.Speak("山重水复疑无路,柳暗花明又一村。") # 读文本

类型转换

PowerShell作为脚本语言,有非常强的灵活性,不同类型之间可以进行隐形的转换,转换的范围远远大于C#等强类型语言。

字符串可以转换成数字,时间,正则表达式和枚举等;不同类型也可以转换成逻辑类型。

[int]$a = "100" # 字符串隐式转换成数字
[datetime]$date = "2019-1-1" # 字符串隐式转换尝时间
[System.DayOfWeek]$day = "Sunday" # 字符串隐式转换成枚举
[bool]$flag = "" # 字符串隐式转换为逻辑类型,空为假,非空为真
[bool]$flag = @() #数组隐式转换为逻辑类型,0元素为假,有元素为真

要谨慎使用隐式转换,难以理解,可能会产生意想不到的错误。

数组、字典和集合

数组

数组是一系列类型相同的变量。

语法:

[元素类型[]]变量名 = @(元素1,元素2,...,元素n)
$arr = @(1,2,3)
$arr = 1,2,3
$arr.Length # 访问数组长度
$arr[1] # 访问数组第2个元素
$arr[-1] # 访问数组最后一个元素

如果限定了类型,使用其他类型赋值时会尝试转换,失败则赋值失败。

[int[]]$arr = 1,2,3
$arr[0] = "100"
$arr[1] = "def" # 类型转换失败,赋值失败

链接数组

可以使用+运算符链接数组。

链接数组时,运算符右侧的操作数可以是元素。

1,2+3 # 2个元素的数组链接1个元素
1,2+3,4 # 2个元素的数组链接另一个2个元素的数组。

如果需要在元素中使用加法+,需要使用括号调整优先级。

1,(2+3),4 # 3个元素的数组,第2个元素是2+3

复杂索引

数组[]方括号中的索引可以是数组。 下标支持..范围语法,范围可以是负数,表示从结尾计算。 范围可以由大到小,表示倒着访问。 支持使用+链接索引的数组,同时访问多个索引数组的对应的元素。 复杂索引不仅可以用于数组,也可以用于列表。

$arr = 1..10
$arr[0] # 访问第1个元素
$arr[0..4] #访问前5个元素
$arr[-5..-1] # 访问后5个元素
$arr[-1..-5] # 倒着访问后5个元素
$arr[0,1+5..8+9] # 访问第1、第2、第6至第6和第10个元素。

字典

字典是多个键值对的组合,其中键不能重复。字典又叫做哈希表。

语法:

变量名 = @{
    键名1 = 值1;
    键名2 = 值2;
    键名n = 值n;
}
$p = @{
    Name = "张三";
    Age = "30";
    Gender = "男"
}
$p.Count
$p.Name
$p["Age"]

.NET中的集合

PowerShell可以使用任何.NET中的集合类型。

使用泛型列表

$list = New-Object System.Collections.Generic.List[int]
$list.Add(100)
$list.Add("500")
$list

列表可以动态调整大小,是很常用的集合类型。

使用泛型字典

$dict = New-Object "System.Collections.Generic.Dictionary[string,object]"
$dict.Add("Name","张三")
$dict.Add("Age",30)
$dict.Add("Gender","男")
$dict.Name
$dict

泛型字典和字典使用相似,初始化方式有所区别,并且是强类型的。 由于泛型类型中有,,需要将类型名写成字符串形式。 泛型字典索引同样可以使用数组的语法,支持负数索引和数组索引。

使用泛型栈

$stack = New-Object System.Collections.Generic.Stack[int]
$stack.Push(100)
$stack.Push(200)
$stack.Pop()
$stack.Pop()

额外成员

PSObject类型是PowerShell中的增强类型,可以容纳任何对象,并支持额外添加成员。

使用Add-Member可以向变量添加额外的成员,添加后的对象实际是PSObject,虽然使用上没有变化。 使用Get-Member可以查询成员,包括类型原有的成员和添加的额外成员。

别名成员是和一个变量关联的成员,可以像普通成员一样访问。使用别名和原成员一致。

$s = "abc"
$s | Add-Member -MemberType AliasProperty -Name Count -Value Length # 添加别名属性
$s.Count # 使用别名属性访问

附加属性是和一个变量关联的属性,可以像普通属性一样访问。

$s = "abc"
$s | Add-Member -MemberType NoteProperty -Name MyName -Value "附加的值" # 添加附加属性
$s.MyName # 访问附加属性的值

脚本属性是和变量关联的一段代码,可以向普通属性一样访问,访问时是调用了一段代码。类似于.NET中的getter。 在脚本属性的脚本中,使用$this引用原对象。

$a = 100
$a | Add-Member -MemberType ScriptProperty -Name Hex -Value {$this.ToString("x")} -Force # 添加脚本属性
$a.Hex # 访问附加脚本属性的值

PSCustomObject

PSCustomObject是一个空类型,没有任何成员,但允许在其上添加成员。

$o = [PSCustomObject]@{
    Name     = 'Kevin'
    Language = 'Powershell'
    State    = 'Texas'
}

PowerShell会为其添加附加属性。该类型适合作为自定义类型。

许多命令执行过程中,都使用了额外的成员,例如Select-Object,其结果的类型就是包含额外成员的PSCustomObject类型。

dir | select Name,Length

变量的作用域

每个变量只在都有其使用范围。

变量的作用域分为:全局,函数和脚本。

全局作用域是在函数、脚本外定义的变量,在任何地方都可以访问。

$s = "abc"

function F()
{
    $s # 函数内部可以访问全局变量
}

F

函数作用域是函数的参数和函数内定义的变量,只能在函数内部访问。

function F()
{
    $s ="hello"
}
F
$s # 函数外部不可以访问函数内部的变量。在函数调用结束后,变量已被回收。

脚本作用域是在脚本文件中定义的变量,只能在脚本内访问。

一个脚本很像一个函数,内部的变量也只能内部使用。

在脚本内部可以访问全局变量,但对其的赋值操作会失效。但可以访问对象改变其状态。

脚本 003.01.ps1

$list.Add(200) # 修改列表的状态
$list = New-Object System.Collections.ArrayList # 重新赋值列表
$list = New-Object System.Collections.ArrayList
$list.Add(100)
./003.01.ps1 # 运行脚本
$list # 查询结果 100,200  

在脚本中,可以访问到全局对象,可以向其添加元素修改其状态,但是不能够进行赋值覆盖原有对象。

和函数的参数一样,传递的是副本。对于值类型是值的副本,对于引用类型,是引用的副本。