抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

PowerShell入门指南(三)·一门新的编程语言


作为独立的编程语言

作为一门独立的语言来说,PowerShell 是非常地Powerful,我们先来了解一下它的特点:

特点

  • 破天荒的方便

    诸如存储计算中 GBMBKB 单位等;数组声明中的 1..n 和下标为 -1 的处理;还有所见即所得,通俗易懂的动词+名词结构的Cmdlet(PowerShell命令的称呼)。
    还有自带的文档支持也很是丰富,只要熟练掌握 Get-Help 命令,其他命令的用法均可通过 Get-Help 查到

  • 面向对象

    良心啊,这个语言竟然是面向对象的
    与面向过程相比,面向对象更方便更容易描述现实世界,也算赶上了时髦。

  • 依托 .NET

    正所谓大树下面好乘凉,PowerShell 绑上 .NET 这个大款了,借助 .NET 平台强大的类库,几乎让一切都成为可能。

  • 强大的兼容性

    完全兼容 Windows 平台上其它调用,如可执行文件(exe),批处理bat/cmd和VBscript等, 在 LinuxmacOS 上也能很好地工作。

  • 基于平台的可扩展性

    微软有个优点,与应用相比,它更喜欢做平台。PowerShell 早已变成一个平台,在 PowerShell 刚发布的第二年,微软的 System Center Operations Manager 和 SharePoint 就提供了针对该平台的组件,后来的活动目录,Hyper-V,Windows Azure,Office 365就更不用说了。除了微软,亚马逊的云平台管理,Dell的out-of-hand 管理,也都提供了基于 PowerShell 的管理组件。PowerShell 俨然变成了一个标准,变成了一个规范。


使用Get-Help 快速入门

下面用一个简单例子说明如何Get-Help,设想这样一个场景:
你想通过命令行查看所有进程,你第一个反应应该是用一个跟Process相关的命令来达到此目的,所以你可以会尝试执行:

1
2
3
4
5
6
7
8
9
10
11
Get-Command *Process
#结果
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Debug-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Enter-PSHostProcess 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Exit-PSHostProcess 3.0.0.0 Microsoft.PowerShell.Core
Cmdlet Get-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Start-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Stop-Process 3.1.0.0 Microsoft.PowerShell.Management
Cmdlet Wait-Process 3.1.0.0 Microsoft.PowerShell.Management

得知处理进程的命令有这些
然后再用 Get-Help Get-Process -full 就能得到 Get-Process 的详细用法以及使用范例

基本语法

背景

PowerShell 是一个强类型(变量一旦定义,其本身类型不可改变就是强类型,反之就是弱类型)的动态脚本语言,支持面向对象,支持调用系统API和 .NET 库。
受到了 Python, Ksh, Perl, C#, CL, DCL, SQL, Tcl, Tk, Chef, Puppet 等语言的影响,结合了以上语言的部分特性。

PowerShell 代码的外观和操作方式与C#的相似程度最高,不过也有它自己的特色。

关于空白字符

Python 等语言不同,**PowerShell 的解释器不会考虑代码中的空格或制表符**(这些字符统称空白字符)。这样一来,格式化代码就有很大的自由度,但是遵循某些规则将有助于提高代码的可读性。

代码块和代码基本结构

PowerShell 代码由一系列语句构成,每条语句可以使用一个分号结束,当然也可以不写。按照习惯是不写分号的,因此要用换行来区分不同的语句,如果必须写到一行中,那就在每一句后加个分号

和C语系的大多数语言类似,PowerShell 是一种块结构的语言,这些块用 {} 来界定,代码块可以包含任意多条语句,或者不包含任何语句,下面的示例还使用了缩进格式,这样能大大提高可读性,实际上编译器会自带缩进代码。一般情况下,每个代码块都有自己的缩进级别,代码块之间还能嵌套。

1
2
3
4
5
6
7
8
9
{
<#代码行1#>
<#代码行2#>
{
<#代码行3#>
<#代码行4#>
}
<#代码行5#>;<#代码行6#>;
}

当然 PowerShell 的缩进不是强制的。

PowerShell 代码中,另一种常见的语句是注释,注释并不是能执行的语句,而是对代码的描述说明性文本。当代码运行时,解释器会忽略这些内容。
代码最好有注释,特别是处理较复杂的工作时,注释可以为正在进行的操作添加提示,例如“这行代码要求用户输入一个字符”、“此段代码是 LNP 编写的”。
PowerShell 有两种添加注释方法

  • 行注释: #
  • 块注释: <##>
1
2
3
4
5
6
7
8
9
10
11
#这是一行注释

<#
这是注释块
123ABC
#>

ls #列出当前目录下的文件和文件夹,是Get-ChildItem的别名

#注意下面语句会产生错误
<#块注释由"#>"结尾#>

第一个 #> 后面的部分会被认为是 PowerShell 代码,因此出现错误。

还有特别的注意一点,**PowerShell 代码是不区分大小写的**,因此只要拼写正确的命令(或变量),而无需关心大小写即可执行,不过最好还是有一定规范。


PowerShell 脚本的基本结构

PowerShellPython 一样,允许使用控制台直接输入命令进行交互,也可以事先把代码写入一个文件再作为脚本运行。
一个 PowerShell 脚本仅仅是一个包含 PowerShell 代码的文本文件。如果这个文本文件执行, PowerShell 解释器会逐行解释并执行它的的语句。PowerShell 脚本有点像以前 CMD 控制台上的批处理文件。可以通过非常简单的文本编辑工具创建 PowerShell 脚本。

PowerShell脚本文件的扩展名是 .ps1


执行策略限制

PowerShell 一般初始化情况下都会禁止脚本执行。脚本能否执行取决于PowerShell的执行策略。

1
2
3
4
5
6
7
8
PS E:> ./MyScript.ps1

无法加载文件 E:MyScript.ps1,因为在此系统中禁止执行脚本。有关详细信息,请参阅 "get-help about_signing"
所在位置 行:1 字符: 15
+ .MyScript.ps1 < <<<
+ CategoryInfo : NotSpecified: (:) [], PSSecurityException
+ FullyQualifiedErrorId : RuntimeException

只有管理员才有权限更改这个策略。非管理员会报错。

查看脚本执行策略,可以通过在 PowerShell 控制台直接输入:
Get-ExecutionPolicy
更改脚本执行策略,可以管理员启动PowerShell,在控制台输入:
Set-ExecutionPolicy <策略>
策略|解释
-|-
Unrestricted|权限最高,可以不受限制执行任何脚本。
Default|为Powershell默认的策略,即Restricted
Restricted|不允许任何脚本执行
AllSigned|所有脚本都必须经过签名才能在运行
RemoteSigned|本地脚本无限制,但是对来自网络的脚本必须经过签名

如果要使用脚本功能又要兼顾安全性,我们就选择RemoteSigned
即在以管理员身份允许的 PowerShell 输入
Set-ExecutionPolicy RemoteSigned

1
2
3

执行策略可以防止您执行不信任的脚本。更改执行策略可能会使您面临 about_Execution_Policies帮助主题中所述的安全风险。是否要更改执行策略?
[Y] 是(Y) [N] 否(N) [S] 挂起(S) [?] 帮助 (默认值为“Y”): y

运行 PowerShell 脚本

当您的脚本编写成功后您可能第一次会像下面的方式运行它,也就是只输入脚本的文件名,会报错。

1
2
3
4
5
6
7
8
9
10
11
PS E:> MyScript.ps1

无法将“MyScript.ps1”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括
路径,请确保路径正确,然后重试。
所在位置 行:1 字符: 13
+ MyScript.ps1 < <<<
+ CategoryInfo : ObjectNotFound: (MyScript.ps1:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

Suggestion [3,General]: 未找到命令 MyScript.ps1,但它确实存在于当前位置。Windows PowerShell 默认情况下不从当前位置加载命令。如果信任此命令,请改为键入 "./MyScript.ps1"。有关更多详细信息,请参阅 "get-h
elp about_Command_Precedence"

解决办法很简单,如果脚本在当前工作目录,请在脚本文件明前添加./,或者使用绝对路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
PS E:> .\MyScript.ps1

2019年3月27日 18:33:03
C:\Program Files\Common Files
files count
20

PS E:> E:MyScript.ps1

2019年3月27日 18:33:11
C:\Program Files\Common Files
files count
20
通过重定向创建脚本

如果想要执行的脚本不是很长,我们甚至可以直接在控制台中要执行的语句重定向给一个脚本文件。

1
2
3
PS E:> '"Hello,World!"' > MyScript.ps1
PS E:> ./MyScript.ps1
Hello,World!

这样有个缺点,就是您的代码必须放在闭合的引号中。这样的书写方式一旦在脚本内部也有引号时,是一件很痛苦的事。甚至您还可能希望在脚本中换行。下面的Here-strings例子不错,也就是将脚本文件通过@' '@闭合起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PS E:> @'
>> Get-Date
>> $Env:CommonProgramFiles
>> #Script End
>> "files count"
>> (ls).Count
>> #Script Really End
>>
>> '@ > myscript.ps1
>>
PS E:> .MyScript.ps1

2019年3月27日 18:15:10
C:\Program Files\Common Files
files count
20

Here-String以 @'开头,以'@结束.任何文本都可以存放在里面,哪怕是一些特殊字符,空号,白空格。但是如果您不小心将单引号写成了双引号,PowerShell 将会把里面的变量进行解析。

通过编辑器创建脚本

其实最方便的还是使用文本编辑器直接编写代码,保存成PS1文件,右键即可执行。
这里推荐使用Visual Studio Code(以下简称VSC),VSC提供了PS1的自动补全(安装插件)、语法高亮、自动缩进、格式化代码、断点调试等功能。


变量

变量可以临时保存数据,因此可以把数据保存在变量中,以便进一步操作。PowerShell 的变量定义非常方便。
我们可以用 $变量名=初值 的方法定义变量,解释器会根据所赋的初值判断变量类型,类似于C#的 var 关键字或C++11中的 auto 关键字PowerShell 不需要显示地去声明,可以自动创建变量,只须记住变量的前缀为$.
创建好了变量后,可以通过变量名输出变量,也可以把变量名存在字符串中。但是有个例外:单引号中的字符串不会识别和处理变量名。

选择变量名

PowerShell 中变量名均是以美元符 $ 开始,剩余字符可以是数字、字母、下划线的任意字符,并且PowerShell变量名也对大小写不敏感($a$A 是同一个变量)。
某些特殊的字符(比如$等)在 PowerShell 中有特殊的用途,一般不推荐使用这些字符作为变量名。当然你硬要使用,请把整个变量名后缀用花括号括起来。

1
2
3
PS C:/> ${"I"like $}=5.1
PS C:/> ${"I"like $}
5.1

不能定义和保留变量名称相同的变量
使用ls variable: 列出当前使用的所有变量,刚启动的 PowerShell 执行此命令能看到 PowerShell 的所有自动化变量(一旦打开 Powershell 就会自动加载的变量,后面将会详细解释这些变量的作用)

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Name                           Value
---- -----
$
? True
^
args {}
ConfirmPreference High
ConsoleFileName
DebugPreference SilentlyContinue
Error {}
ErrorActionPreference Continue
ErrorView NormalView
ExecutionContext System.Management.Automation.EngineIntrinsics
false False
FormatEnumerationLimit 4
HOME C:\Users\aaaaa
Host System.Management.Automation.Internal.Host.InternalHost
InformationPreference SilentlyContinue
input System.Collections.ArrayList+ArrayListEnumeratorSimple
MaximumAliasCount 4096
MaximumDriveCount 4096
MaximumErrorCount 256
MaximumFunctionCount 4096
MaximumHistoryCount 4096
MaximumVariableCount 4096
MyInvocation System.Management.Automation.InvocationInfo
NestedPromptLevel 0
null
OutputEncoding System.Text.ASCIIEncoding
PID 8796
PROFILE C:\Users\aaaaa\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
ProgressPreference Continue
PSBoundParameters {}
PSCommandPath
PSCulture zh-CN
PSDefaultParameterValues {}
PSEdition Desktop
PSEmailServer
PSHOME C:\Windows\System32\WindowsPowerShell\v1.0
PSScriptRoot
PSSessionApplicationName wsman
PSSessionConfigurationName http://schemas.microsoft.com/powershell/Microsoft.PowerShell
PSSessionOption System.Management.Automation.Remoting.PSSessionOption
PSUICulture zh-CN
PSVersionTable {PSVersion, PSEdition, PSCompatibleVersions, BuildVersion...}
PWD C:\Users\aaaaa
ShellId Microsoft.PowerShell
StackTrace
true True
VerbosePreference SilentlyContinue
WarningPreference Continue
WhatIfPreference False

查看变量类型

变量可以自动存储任何PowerShell能够识别的类型信息,可以通过 $变量名.GetType() 查看和验证 PowerShell 分配给变量的数据类型

1
2
3
4
5
6
PS C:/> $num=10
PS C:/> $num.gettype() #不区分大小写

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType

删除变量

如果不想继续使用自定义的变量,可以使用del variable:变量名的方法删除变量,注意此处无$符号

1
2
3
4
5
6
$a=0
$a -eq $null
False
del variable:a
$a -eq $null
True

PowerShell支持的变量类型和C#大体相同(没有了short、uint、ulong等),大多都继承自System.ValueType类( .NET类),其基本数据类型包括

整型

类型 名称 允许的值 所属类
byte 无符号整数(1字节) 0~255之间的整数 System.Byte
sbyte 有符号整数(1字节) -128~127之间的整数 System.SByte
int16 有符号短整型(2字节) -32768~32767之间的整数 System.Int16
uint16 无符号短整型(2字节) 0~65535之间的整数 System.UInt16
int 有符号整型 -2147483648~2147483647之间的整数 System.Int32
uint32 无符号整型 0~4294967295之间的整数 System.UInt32
long 有符号长整数(8字节) -9223372036854775808~9223372036854775807之间的整数 System.Int64
ulong 无符号长整数(8字节) 0~18446744073709551615之间的整数 System.UInt64

其实 intlong 、以及下面的 float 都是 .NET的语法糖,真正的原生类型名是int32int64single 之类的类名

浮点型

浮点数标准形式(floatdouble)
$+/-m×2^e$
其中m为尾数,e为阶码,尾数是一个非负数,阶码是一个整数
PowerShell还支持一个特别的浮点数类型 decimal,其形式为
$+/-m×10^e$

类型 名称 指数 m范围 e范围 近似最小值 近似最大值 所属类
float 单精度浮点数 2 0~$2^{24}$ -149~104 $1.5×10^{-45}$ $3.4×10^{38}$ System.Single
double 双精度浮点数 2 0~$2^{53}$ -1075~970 $5.0×10^{-324}$ $1.7×10^{308}$ System.Double
decimal 16字节浮点数 10 0~$2^{96}$ -28~0 $1.0×10^{-28}$ $7.9×10^{28}$ System.Decimal

其他简单类型

除了数值类型以外,还有3种基本类型

类型 名称 允许的值 所属类
char 字符型 一个Unicode字符,存储0~65535之间的整数 System.Char
bool 布尔型 布尔值: $true$false(必须加$符号) System.Boolean
enum 枚举 限定取值一组命名常量的独特的值类型 System.Enum
datetime 时间型 包含日期、时间的类型 System.DateTime
string 字符串 一组字符 System.String

注意 C/C++的 char 仅支持ASCII里面的256个字符, PowerShellC#char 是支持Unicode的
PowerShell和C#的string类型并不是继承自 System.ValuType 类,而是继承自 System.Object,因此严格来说 string 类型并非是简单类型。
**PowerShell的转义字符是 ` 而不是 \**,这也是和C#的一个区别

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#使用char
PS D:/>[char]$c="A"
$c
A
#使用bool
PS D:/>$boolean=$true
$boolean
True

#使用enum
enum fruit
{
apple
banana
cherry
durian
}
[fruit]$en=[fruit]::apple
$en
apple
$item="cherry"
[fruit]$enu=[fruit]::($item)
$enu
cherry

#这种使用方法是错误的
PS D:/> [fruit]$enum=banana

banana : 无法将“banana”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
所在位置 行:1 字符: 12
+ [fruit]$en=banana
+ ~~~~~~
+ CategoryInfo : ObjectNotFound: (banana:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

#当尝试使用非枚举值进行赋值时
PS D:/> [fruit]$en=[fruit]::peach

由于枚举值无效,无法将 Null 转换为类型“fruit”。请指定以下枚举值之一,然后重试。可能的枚举值为“apple,banana,cherry,durian”。
所在位置 行:1 字符: 1
+ [fruit]$en=[fruit]::peach
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException

PS D:/> "\n 123 `n 123"
<#输出
\n 123
123
#>

PS D:/>$date=get-date
$date
$date.gettype()
<#输出
2019年3月28日 19:32:30

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DateTime System.ValueType
#>

赋值和返回值

赋值操作符为 =,几乎可以把任何数据赋值给一个变量,甚至一条cmdlet命令
,因为 PowerShell 支持面向对象,对象可以包罗万象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PS D:\powershell\test> $item=ls
PS D:\powershell\test> $item


目录: D:\powershell\test


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2017/11/24 12:52 136 4-3.cpp
-a---- 2019/3/27 15:13 65 anapple.txt


PS D:\powershell\test> $item.gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array

$item为一个对象数组

弱类型与强类型

一般对 PowerShell 变量重新赋值时,变量类型会自动改变,这是弱类型语言的特点;
PowerShell 依托的 .NET是强类型的,所以 PowerShell 可以使用强类型。
强类型语言在速度上略逊于弱类型语言,但是强类型定义语言带来的严谨性又能避免不必要的错误。
可以在变量前添加类型限定符使该变量变为强类型,可以确保变量的类型不会随着赋值而改变

1
2
3
4
5
6
7
8
9
[int]$num=123 #正确
[int]$num=ls <#错误
无法将“System.Object[]”类型的“System.Object[]”值转换为“System.Int32”类型。
所在位置 行:1 字符: 1
+ [int]$num=ls
+ ~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException
#>

类型转换

PowerShell 能够非常方便地将字符串等基本类型转换成期望的类型。之所以神奇,是因为 PowerShell 本身做了很多辛苦的工作,按照优先级:

  1. 直接赋值:输入类型和期望类型一致,可以直接交付。
  2. 基于语言的类型转换:当目标类型为void,Boolean,String , Array , Hashtable , PSReference (i.e.: [ref]), XmlDocument , DelegateEnum 时,基于语言的类型转换开始工作。
  3. Parse 转换:如果目标类型包含了 Parse() 方法,则采用它。
  4. Static Create 转换:如果目标类型包含静态的Create,则采用它。
  5. 构造函数转换:如果目标类型定义了构造函数,采用它。
  6. Cast 转换:如果目标类型定义了从源类型的显式或者隐式的操作符,则采用它。
  7. IConvertible 接口转换:如果目标类型实现了支持源类型IConvertible 接口,则采用它。
  8. IDictionary 转换:如果源类型是词典或者哈希表,会尝试创建一个实例,然后来填充name和value属性。
  9. PSObject 属性转换:如果源类型是PSObject,通过目标类型的默认的构造函数创建一个实例,然后使用PSObject中的属性名称和值来填充实例的属性。
  10. TypeConverter 转换:如果存在注册的 TypeConverterPSTypeConverter 来处理转换,则使用它。

注意对浮点数向整数进行类型转换时,会自动四舍五入!!
如果要C++/C#那种向下取整的方法请使用 [math]::Floor() 函数
看几个转换的例子

1
2
3
4
5
PS D:/test> $s=12.56
PS D:/test> [int]$s
13
PS D:/test> [convert]::ToInt32($s)
13

[convert]::ToInt32() 是 .NET System.Convert 类提供的转换函数
convert 类中的转换函数格式为:TO+原生类型名(),这里的原生类型名指的是各个类型实际类名
常用:

  • [convert]::ToInt32()
  • [convert]::ToSingle()
  • [convert]::ToDouble()
  • [convert]::ToBoolean()
  • [convert]::ToString()

convert 类提供了一系列方法来完成不同变量之间的转换,获得函数列表及使用方法请参考微软的 .NET文档Convert类 方法列表

运算符

注意逻辑运算符和比较运算符的写法,不支持 C#&& || == != < > >= <=这些运算符

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#加 减 乘 除 取余
+ - * / %
#自增、自减运算符,和C#或C++完全一致
++$i
$i++
$i--
--$i
#且
-and
#并
-or
#非
-not
!
#样例
$true -and $false -or -not 0
!$true
#输出
True
False
#等于 (equal to)
-eq
#不等于 (not equal to)
-nq
#大于 (greater than)
-gt
#大于等于 (greater than or equal to)
-ge
#小于 (less than)
-lt
#小于等于 (less than or equal to)
-le

#对于字符串类型,可以在比较运算符前加i或c表示是否区分大小写,
#i表示对大小写不敏感,c表示敏感,不写默认不敏感
"a" -eq "A"
"a" -ceq "A"
#输出
True
False

#判断变量是否为兼容类型(同一类型或其父类型),类型格式为 [类型名]
-is
#示例
$a=0
$a -is [int]
$b=1,2,3
$b -is [array]
$a -is [ValueType]
#输出
True
True
True # System.Int32类继承自System.ValueType类,因此int是其父类型ValueType的子类型


条件分支

if else

ifelse 用法和C#完全一样,除了大括号不允许省略,多分支时还多了个elseif 可用,和Pythonelif 作用相同

1
2
3
4
5
6
7
8
9
if($true -and $true) {
$a=0
}
elseif($a -eq $b) {
$a=1
}
else {
$a=2
}

switch

PowerShell的switch非常灵活,使用起来较为方便
相对C#或C++,PowerShell的switch不需要写`case:``,但是必须写大括号

1
2
3
4
5
6
$a="Beijing"
switch($a)
{
"Beijing" {$res="北京"}
"Shanghai" {$res="上海"}
}

默认比较运算符为-eq,你也可以使用下面的例子自定义比较条件,必须保证表达式返回boolen类型($true和$false)

1
2
3
4
5
6
7
8
9
10
$v=18
switch($v)
{
{$_ -lt 10} {"小于10"} # $_ 表示当前的传入的变量 直接写的字符串会被输出
10 {"等于10"}
{$_ -gt 10} {"小于10"}
}
<#输出
大于10
#>

循环

接下来介绍循环

for循环

PowerShellfor 循环类似于C#,看一个样例:

1
2
3
4
for($i=0;$i -lt 10;$i++)
{
Write-Output "Hello"
}

do-while循环

DoWhile 可能产生死循环,为了防止死循环的发生,因此我们必须确切的指定循环终止的条件。指定了循环终止的条件后,一旦条件不满足就会退出循环。
do-while() 会先执行再去判断,能保证循环至少执行一次。

1
2
3
4
5
6
7
8
9
10
11
do 
{
$n=Read-Host "请输入数字"
}
while($n -ne 0)
<#输出
请输入数字: 1
请输入数字: 2
请输入数字: s
请输入数字: 0
#>
只使用while
1
2
3
4
5
6
7
8
9
10
11
12
$n=5
while($n -gt 0)
{
$n
$n=$n-1
}
#输出
5
4
3
2
1
跳出循环

使用 continue 关键字,可以终止当前循环,跳过 continue 后其它语句,重新下一次循环。
跳出循环语句使用 break 关键字

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
$n=1
while($n -lt 6)
{
if($n -eq 4)
{
$n=$n+1
continue

}
else
{
$n
}
$n=$n+1
}
1
2
3
5
$n=1
while($n -lt 6)
{
if($n -eq 4)
{
break
}
$n
$n++
}

还有一种循环 foreach ,等到我们讲到数组再说


数组

定义数组

PowerShell 中创建数组可以使用逗号

1
2
3
4
5
6
PS C:/Powershell> $nums=2,0,1,2
PS C:/Powershell> $nums
2
0
1
2

对于连续的数字数组可以使用一个更快捷的方法:

1
2
3
4
5
6
7
PS C:/Powershell> $nums=1..5
PS C:/Powershell> $nums
1
2
3
4
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
33
PS C:/Powershell> $array=1,"2019",([Guid]::NewGuid()),(get-date)
PS C:/Powershell> $array
1
2019

Guid
----
ea5f17c6-c0a2-42ba-9de4-e4d03bebffaf

DisplayHint : DateTime
Date : 2019/3/29 0:00:00
Day : 29
DayOfWeek : Friday
DayOfYear : 88
Hour : 23
Kind : Local
Millisecond : 120
Minute : 23
Month : 3
Second : 39
Ticks : 636894986191207086
TimeOfDay : 23:23:39.1207086
Year : 2019
DateTime : 201932923:23:39

PS C:/Powershell> $array|foreach{$_.gettype()}
#查看数组各元素类型
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
True True String System.Object
True True Guid System.ValueType
True True DateTime System.ValueType
空数组和单元素数组

对数组元素可以查看它的公有属性,比如长度
只需要输入$数组名.Count即可显示数组长度

空数组
1
2
3
4
5
PS C:/Powershell> $a=@()
PS C:/Powershell> $a -is [array]
True
PS C:/Powershell> $a.Count
0
单元素数组
1
2
3
4
5
PS C:Powershell> $a=,"moss"
PS C:Powershell> $a -is [array]
True
PS C:Powershell> $a.Count
1

使用数组

遍历
直接法

将数组作为单独一行将会自动遍历这个数组的所有元素

1
2
3
4
5
6
7
8
9
10
11
12
PS C:/> $a=1..10
PS C:/> $a
1
2
3
4
5
6
7
8
9
10
for 循环遍历
1
2
3
4
5
6
7
8
9
10
11
PS C:/> $a=1..5
PS C:/> for($i=0;$i -lt 5;$i++) {
>> $a[$i] }
1
2
3
4
5
}


foreach 遍历法

还有一种遍历的方法,用到了之前提到的 foreach 语句
意思是使用变量 $n$a 元素进行迭代,这实际上是一种对可迭代对象的访问算法
C# 也有这种语法

1
2
3
4
5
6
7
8
9
10
11
$a="A","B","C","D","E"
foreach($n in $a)
{
$n+" "+$n
}
#输出
A A
B B
C C
D D
E E

foreach还有一种遍历的写法

这里我们需要先了解一下管道
管道的符号 |
管道允许将它左侧命令的输出结果发送到右侧做命令的参数
管道并不是什么新事物,以前的Cmd控制台也有重定向的命令,例如Dir | More可以将结果分屏显示。
传统的Cmd管道是基于文本的,但是 PowerShell 是基于对象的

列出当前目录下的目录和文件,然后根据文件名降序排列,再投影(数据库术语)文件名,文件大小,文件的修改时间:

1
2
3
4
5
6
7
8
9
10
11
12
PS D:/test> ls | Sort-Object -Descending Name | Select-Object Name,Length,LastWriteTime


目录: D:/test


Name Length LastWriteTime
---- ------ -------------
out.txt 523 2019/2/24 22:46:35
lang.json 6415 2019/2/16 23:49:24
anapple.txt 65 2019/3/27 15:13:52
4-3.cpp 136 2017/11/24 12:52:28

可迭代对象(比如数组)可以由管道送到一些命令上进一步处理
foreach就可以接受管道送来的可迭代对象,并进行遍历

1
2
3
4
5
6
7
8
9
10
11
$array="A","B","C","D","E"
$array|foreach
{
$_+"s"
}
#输出
As
Bs
Cs
Ds
Es
将数组逆序输出
1
2
3
4
5
PS C:/Powershell> $books="A1","B2","C3"
PS C:/Powershell> $books[($books.Count)..0]
C3
B2
A1
访问某些元素

与C#相同,数组的元素可以使用索引寻址,第一个元素的索引为0,第i个元素的索引为i-1,最后一个元素的索引为Count-1,但是 PowerShell 为了使用方便,直接可以将 -1 作为最后的一个元素的索引(参考了 Python 的语法)

1
2
3
4
5
6
7
8
9
PS C:/Powershell> $books="1A","2B","3C"
PS C:/Powershell> $books[0]
1A
PS C:/Powershell> $books[1]
2B
PS C:/Powershell> $books[($book.Count-1)]
3C
PS C:/Powershell> $books[-1]
3C
从数组中选择多个元素
1
2
3
4
5
6
7
8
9
10
PS C:/Powershell> $result=ls
PS C:/Powershell> $result[0,3,5,12]
Directory: C:Powershell

Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 2011/11/23 17:25 ABC
-a--- 2011/11/24 20:04 26384 a.txt
-a--- 2011/11/24 20:27 12060 alias.ps1
-a--- 2011/11/24 17:37 7420 name.html
给数组添加元素

因为PowerShell数组在内存中是顺序存储的,所以数组的大小必须是确定的,这样才方便分配存储空间,所以给数组增加元素其实相当于创建一个新的数组,只不过之后会把原来的副本删除。在当前数组追加元素可以使用 += 操作符。

1
2
3
4
5
6
7
PS C:/Powershell> $books="A1","B2","C3"
PS C:/Powershell> $books+="D4"
PS C:/Powershell> $books
A1
B2
C3
D4
删除指定位置元素

采用截断重连法删除指定元素

1
2
3
4
5
6
7
8
9
10
11
PS C:/Powershell> $num=1..4
PS C:/Powershell> $num
1
2
3
4
PS C:/Powershell> $num=$num[0..1]+$num[3] #相当于删掉了第三项
PS C:/Powershell> $num
1
2
4

复制数组

数组属于引用类型,使用默认的的赋值运算符在两个变量之间赋值只是复制了一个引用,两个变量共享同一份数据。这样的模式有一个弊病如果其中一个改变也会株连到另外一个。所以复制数组最好使用 Clone() 方法( System.Array 类的成员函数),除非有特殊需求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PS C:/Powershell> $chs=@("A","B","C")
PS C:/Powershell> $chsBak=$chs
PS C:/Powershell> $chsBak[1]="H"
PS C:/Powershell> $chs
A
H
C
PS C:/Powershell> $chs.Equals($chsBak)
True
PS C:/Powershell> $chsNew=$chs.Clone()
PS C:/Powershell> $chsNew[1]="Good"
PS C:/Powershell> $chs.Equals($chsNew)
False
PS C:/Powershell> $chs
A
H
C

强类型数组

PowerShell 数组一般具有多态性,如果你不指定元素的具体类型,解释器会自动选择合适的类型存储每个元素。如果要统一限制所有元素的类型,可是使用类型名和一对方括号作为数组变量的类型。这样每当赋值时,会自动类型检查。如果目标数据类型不能转换成功,就会抛出一个异常,这样的数组被称为强类型数组
定义方法[类型[]]$数组名=初值

1
2
3
4
5
6
7
8
9
10
PS C:/Powershell> [int[]] $nums=@()
PS C:/Powershell> $nums+=2012
PS C:/Powershell> $nums+=12.3
PS C:/Powershell> $nums+="999"
PS C:/Powershell> $nums+="can not convert"
Cannot convert value "can not convert" to type "System.Int32". Error: "Input string was not in a correct format."
At line:1 char:6
+ $nums <<<< +="can not convert"
+ CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException
+ FullyQualifiedErrorId : RuntimeException

命令返回数组

当我们把一个命令的执行结果保存到一个变量中,可能会认为变量存放的是纯文本。
但是,事实上 PowerShell 会把文本按每一行作为元素存为数组。如果一个命令的返回值不止一个结果时, PowerShell 也会自动把结果存储为数组

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
33
34
35
36
37
38
39
40
41
42
43
PS C:/Powershell> $IPcfg=ipconfig
PS C:/Powershell> $IPcfg
<#输出

Windows IP 配置


以太网适配器 以太网:

媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . : lan

无线局域网适配器 本地连接* 1:

媒体状态 . . . . . . . . . . . . : 媒体已断开连接
连接特定的 DNS 后缀 . . . . . . . :

无线局域网适配器 本地连接* 2:

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : ****
IPv4 地址 . . . . . . . . . . . . : ****
子网掩码 . . . . . . . . . . . . : ****
默认网关. . . . . . . . . . . . . :

以太网适配器 VMware Network Adapter VMnet8:

连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::f88d:cd97:5343:9713%16
IPv4 地址 . . . . . . . . . . . . : ****
子网掩码 . . . . . . . . . . . . : ****
默认网关. . . . . . . . . . . . . :

无线局域网适配器 WLAN:

连接特定的 DNS 后缀 . . . . . . . : ****
本地链接 IPv6 地址. . . . . . . . : fe80::954:3964:3731:997e%18
IPv4 地址 . . . . . . . . . . . . : ****
子网掩码 . . . . . . . . . . . . : ****
默认网关. . . . . . . . . . . . . : ****
#>
PS C:/Powershell> $IPcfg.Count
37
使用数组存储结果

判断一个变量是否为数组

1
2
3
4
5
6
7
8
PS C:/Powershell> $ip=ipconfig
PS C:/Powershell> $ip -is [array]
True
PS C:/Powershell> "abac" -is [array]
False
PS C:/Powershell> $str="字符串"
PS C:/Powershell> $str.ToCharArray() -is [array] #转换为字符数组
True
使用真实的对象操作

为什么不愿把IPconfig返回的结果称为对象,因为它不是真正Cmdlet命令(事实上ipconfig是一个单独的程序),真正的 PowerShell 命令返回的数组元素可不止一个字符串,它是一个内容丰富的对象。

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
PS D:/test> $list=ls
PS D:/test> $list


目录: D:/test


Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2017/11/24 12:52 136 4-3.cpp
-a---- 2019/3/27 15:13 65 anapple.txt


PS D:/test> $list.gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array


PS D:/test> foreach($i in $list){$i.name}
4-3.cpp
anapple.txt

PS D:/test> $list[0].gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True FileInfo System.IO.FileSystemInfo

上面的例子中数组的每一个元素存放的是一个 System.IO.DirectoryInfo 对象。
当我们输出这些对象时,PowerShell 会自动帮我们把它转换成友好的文本格式。
对于任何一个对象都可以使用 Format-List * 来查看它所有的属性和方法。

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
33
34
35
36
37
38
39
40
41
42
PS D:/test> $list[0]|fl * 
# "|"符号是管道符,将左边返回结果做参数发送到右侧,fl是Format-List的简称

PSPath : Microsoft.PowerShell.Core\FileSystem::D:test\4-3.cpp
PSParentPath : Microsoft.PowerShell.Core\FileSystem::D:\test
PSChildName : 4-3.cpp
PSDrive : D
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : False
Mode : -a----
VersionInfo : File: D:\test\4-3.cpp
InternalName:
OriginalFilename:
FileVersion:
FileDescription:
Product:
ProductVersion:
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language:

BaseName : 4-3
Target : {}
LinkType :
Name : 4-3.cpp
Length : 136
DirectoryName : D:\test
Directory : D:\test
IsReadOnly : False
Exists : True
FullName : D:\test\4-3.cpp
Extension : .cpp
CreationTime : 2019/3/27 15:07:32
CreationTimeUtc : 2019/3/27 7:07:32
LastAccessTime : 2019/3/27 15:07:32
LastAccessTimeUtc : 2019/3/27 7:07:32
LastWriteTime : 2017/11/24 12:52:28
LastWriteTimeUtc : 2017/11/24 4:52:28
Attributes : Archive

函数

函数是自定义的 Powershell 代码,有三个原则:

  • 简短:函数名简短,并且显而易见。
  • 聚合:函数可以完成多个操作。
  • 封装和扩展:将一批 Powershell 语句进行封装,实现全新的功能需求。
    函数的结构由三部分组成:函数名,参数,函数体

定义函数

脚本中函数的定义方法

函数可以在文本编辑器上编写,写完以后复制进 PowerShell 控制台即可。如果控制台设置为快速编辑模式,从记事本复制后,直接在控制台鼠标右键即可完成粘贴(Windows 10默认开启了快速编辑模式)

1
2
3
4
function FuncName(args[]) #括号可省略
{
<#代码段#>
}
控制台上多行输入定义函数
1
2
3
4
5
PS C:/PowerShell> function MyPing
>> {
>> PING.EXE -n 1 $args
>> }
PS C:/PowerShell>
把函数精简成一行

我们可以将一个函数定义在一行上,但是这样阅读和理解起来就不方便,所以要在每条命令后加分号进行分割(最后一句可以不写;)

1
2
PS C:/PowerShell> function cd...{ cd.. ; cd.. }
PS C:/PowerShell> cd...
使用函数作为别名

假如 PowerShell 不支持 Get-SystemVersion 命令,你可以通过定义函数实现这个功能:

1
2
3
4
5
6
7
8
9
10
function get-systemversion
{
$psversiontable.BuildVersion
}

Get-SystemVersion

Major Minor Build Revision
----- ----- ----- --------
10 0 17134 590
更新函数

如果要更新已经定义好的函数,简单的方法是重新定义,这样新的定义会覆盖旧的定义。但是如果函数代码没有保存副本,可以先将函数定义导出到ps文件,然后就可以编辑了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PS C:/PowerShell> function MyPing
>> {
>> PING.EXE -n 1 $args
>> }

PS C:/PowerShell> $function:MyPing | Out-File myPing.ps1
PS C:/PowerShell> $function:MyPing

PING.EXE -n 1 $args

PS C:/PowerShell> $function:MyPing | Out-File myPing.ps1
PS C:/PowerShell> .myPing.ps1
必须指定 IP 地址。
PS C:/PowerShell> notepad.exe $$
删除函数

控制台定义的函数只会在当前会话生效,一旦控制台退出,会自动消失。在不关闭控制台的条件下删除一个已经定义好的函数,可是使用虚拟驱动器的方法:

1
2
3
4
5
6
7
8
9
10
11
function cc{"hello"}
PS C:/> cc
hello
PS C:/> del function:cc
PS C:/> cc
cc : 无法将“cc”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
所在位置 行:1 字符: 1
+ cc
+ ~~
+ CategoryInfo : ObjectNotFound: (cc:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

输入输出函数

用于脚本文件的编写

输入

PowerShell提供了 Read-Host 命令,可以接收返回用户在控制台输入的字符

1
2
3
4
5
6
7
$name=read-host "请输入你的名字" 
#会把提示信息打印到PS控制台,也可以不写提示信息
<#执行+输入 结果:
请输入你的名字: Nougat
#>
$name
Nougat

注意到

  1. 提示信息(如果有)后面自动加了个冒号;
  2. 用户键入任何信息都被作为该命令的返回结果;
  3. 可以把键入的信息传递给一个变量;
输出

有两种输出命令
Write-HostWrite-Output
若输出字符串不包含空白字符可以不加引号

Write-Host

当需要展示一个特定信息,比如使用其他颜色来吸引人们的注意力的时候,可使用 Write-Host 命令
Write-Host 和其他Cmdlets一样使用管道,但是它不放置任何数据道管道中。反而会直接写到宿主应用程序的界面。正如此,可以使用-ForegroundColor-BackgroundColor 参数将前景和背景设置为其他颜色:

1
2
write-host "啊哈" -ForegroundColor White -BackgroundColor Red
啊哈

注:不是每个使用PowerShell的应用程序都支持其他颜色,也并不是每个应用程序都支持所有颜色
该输出方法不适用于常规的输出结果,因为 Write-Host 命令输出到屏幕的任何东西都无法被捕捉。若执行远程命令或无人值守命令(纯自动化), Write-Host 可能不会按照你的预期工作。因此,此命令仅仅用于与人进行直接交互。

Write-Output

Write-Output命令会将对象发送给管道。由于它不会直接发送到显示界面,所以不允许你指定其他任何的颜色。

它是PowerShell默认使用的一个Cmdlets,默认输出方式即使用该命令,即使你没有指定,PowerShell会在底层将信息传递给Write-Output命令(就是一行直接写一个变量就能直接输出的情况),另外这个命令还有两个别名 writeecho

1
2
3
4
5
6
write-host ACB #无空白字符可以不写引号 
ACB
write "ABC"
ABC
echo "ABD"
ABD

Write-Output输出基本过程为:

  1. Write-Output命令将string类型的对象Hello World!放入管道中;
  2. 管道中只有这个string对象,其会直接到达管道的末端,也就是Out-Default命令;
  3. Out-Default命令将对象传递给Out-Host命令;
  4. Out-Host命令要求PowerShell的格式化系统格式化该对象。
  5. Out-Host将格式化的结果集放在显示界面上

还有一点,在输出多个对象时,Write-Host会以空格隔开各对象
Write-Output会以换行隔开各对象

1
2
3
4
5
6
write-host "ABC" "23232"
ABC 23232

write-output "ABC" "23232"
ABC
23232
其他的输出方式

Write-Warning/Verbose/Debug/Error

具体参考:微软官方文档

处理函数参数

PowerShell 函数可以接受参数,并对参数进行处理。函数的参数有3个特性:

  1. 任意参数:内部变量$args 接受函数调用时接受的参数,$args是一个数组类型;
  2. 命名参数:函数的每一个参数可以分配一个名称,在调用时通过名称指定对应的参数;
  3. 预定义参数:函数在定义参数时可以指定默认值,如果调用时没有专门指定参数的值,就会保持默认值;
$args 万能参数

给一个函数定义参数最简单的是使用$args这个内置的参数。它可以识别任意个参数。尤其适用那些参数可有可无的函数。

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
function sayHello
{
if($args.Count -eq 0)
{
"No argument!"
}
else
{
$args | foreach {"Hello,$($_)"}
}
}
#无参调用
sayhello
#输出
No argument!

#一参调用
sayhello "World!" #也可以写成sayhello("World!")
#输出
Hello,World!


#多参数调用
$str="it's me."
sayhello 123 "aha" $str #如果字符串不包含空白字符(比如空格),引号可以不写
<#输出
Hello,123
Hello,aha
Hello,it's me.
#>

因为 $arg 是一个数组,可以用它很方便的写出求和函数

1
2
3
4
5
6
7
8
function Add
{
$sum=0
$args | foreach {$sum=$sum+$_}
$sum
}
Add 10 7 3 100
#120
使用固定参数
1
2
3
4
5
6
7
8
9
10
function StringContact($str1,$str2)
{
return $str1+$str2
}

StringContact LN P
#StringContact -str1 word -str2 press
#StringContact("word","press")
LNP
wordpress
给参数定义默认值
1
2
3
4
5
6
7
function stringContact($str1="LN",$str2="P")
{
return $str1+$str2
}

stringContact
LNP
使用强类型参数

通过之前的例子发现将用户的参数传递给函数显得比较混乱。罪魁祸首就是PowerShell的参数解释器,它可以自动处理和分配参数给函数。
函数的参数解释器比较傲慢,它对你提供的参数的信息完全不关心。它只会粗略地将参数进行分割,并且最大限度的进行自动类型转换。事实上,这种类型转换很多时候并不完美。所以最好提前能够对参数进行强类型限制

限制数字类型

下面的函数执行后,会抛出异常
因为 subtract 的参数定义了强类型,参数的类型可能引起函数的处理结果改变。

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
function subtract([int]$value1,[int]$value2)
{
return $value1-$value2
}
subtract LN P
#执行结果报错
subtract : 无法处理对参数“value1”的参数转换。无法将值“LN”转换为类型“System.Int32”。错误:“输入字符串的格式不正确。”
所在位置 行:1 字符: 10
+ subtract LN P
+ ~~
+ CategoryInfo : InvalidData: (:) [subtract],ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,subtract

subtract 8.1 7.9
0
<#结果为0,这是因为PowerShell对结果整型化时进行了四舍五入
但是如果将上面的函数的参数定义为Double型#>
function subtract([double]$value1,[double]$value2)
{
return $value1-$value2
}
subtract 8.1 7.9
0.199999999999999
<#输出 0.199999999999999的原因就是 .NET使用IEEE754标准存储浮点数,
由于这些小数转化二进制会无限循环,会根据一定精度截取尾数,
这个问题引发了这种奇怪的结果,
其实著名的0.1+0.2!=0.3也是这个原因导致的#>
限制日期类型

函数的参数解释器会自动尝试将字符串转换成日期类型,如果转换失败就是抛出异常
看下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function DayOfWeek([datetime]$date)
{
return $date.DayOfWeek
}

DayofWeek '1927-8-1'
Monday

DayofWeek 2008-8-1
Friday

DayofWeek 'abc'
DayOfWeek : 无法处理对参数“date”的参数转换。无法将值“abc”转换为类型“System.DateTime”。错误:“该字符串未被识别为有效的 DateTime。有一个未知单词(从索引 0 处开始)。”
所在位置 行:1 字符: 12
+ DayofWeek 'abc'
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [DayOfWeek],ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,DayOfWeek
Switch 参数

Powershell 函数最简单的参数类型为布尔类型,除了使用 Bool 类型,也可以使用 Switch 关键字。
下面的函数逆转字符串,但是可以通过 $try 参数进行控制,如果没有指定 $try 的值,默认值为 $false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function  tryReverse( [switch]$try , [string]$source ) 
#此处switch完全可以换成bool
{
[string]$target=""
if($try)
{
for( [int]$i = $source.length -1; $i -ge 0 ;$i--)
{
$target += $source[$i]
}
return $target
}
return $source
}
tryReverse -source www.google.com
tryReverse -try $true -source www.google.com
#输出
www.google.com
moc.eloog.www

指定函数的返回值

一个或多个返回值

PowerShell 不像其它的编程语言,它的函数可以有多个返回值。如果你直接调用函数,返回值会在控制台输出。当然你也可以将结果存储在一个变量中进一步处理
下面的例子演示返回一个值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Square([double]$num)
{
return $num*$num
}
#在控制台输出结果
Square 9.87
97.4169

#将结果赋值给变量
$value=Square 9.87
$value
97.4169

#返回值为Double类型
$value.GetType().FullName
System.Double

下面的例子演示返回多个值

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
33
function gbMeasure($amount)
{
"$amount GB=$($amount) GB"
"$amount GB=$($amount*1gb/1mb) MB"
"$amount GB=$($amount*1gb/1kb) KB"
"$amount GB=$($amount*1gb) B"
}

#函数返回4个值
gbMeasure 1
1 GB=1 GB
1 GB=1024 MB
1 GB=1048576 KB
1 GB=1073741824 B

#将所有的返回值存储在一个变量中
$result=gbMeasure 1
$result

1 GB=1 GB
1 GB=1024 MB
1 GB=1048576 KB
1 GB=1073741824 B

#所有的返回值会自动存储在一个数组中
$result=gbMeasure 1
$result.GetType().Name
Object[]

#通过索引访问每个返回值
$result=gbMeasure 1
$result[3]
1 GB=1073741824 B

总结一下,如果一个函数返回一个值,像其它编程语言一样,这个值包括它的类型信息会直接返回。但是如果遇到多个返回值,PowerShell会将所有的返回值自动构造成一个对象数组。
可以通过索引访问数组

Return语句

Powershell 会将函数中所有的输出作为返回值,但是也可以通过return语句指定具体的返回值。
Return 语句会将指定的值返回,同时也会中断函数的执行,return后面的语句会被忽略

看一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function test($num)
{
1
9
return 10
4
6
}

test
1
9
10
<# 1 和 9 作为输出会返回
return语句中的10 也会返回
return 语句后的4和6会被忽略#>
访问返回值

一个函数返回了一个值还是多个值,是可以验证的。下面的例子会产生随机数,如果没有指定个数,默认会返回一个随机数,否则会返回指定个数的随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function lottery([int]$number=1)
{
$rand = New-Object system.random
for ($i=1; $i -le $number; $i++)
{
$rand.next(1,50)
}
}

# 参数为空时,返回值不是数组:
$result = lottery
$result -is [array]
False

# 如果指定多个随机数时,返回值是数组类型:
$result = lottery 10
$result -is [array]
True
$result.count
10
从函数的返回值中消除输出

函数默认会将函数中的所有输出作为函数的返回值返回,这样很方便。但有时可能会将不必要的输出误以为返回值。写脚本程序时,可能需要自定义一些函数,这个函数可能只需要一个返回值,但是为了提高函数的可读性,可能会在函数增加一些注释输出行,或者使用write-host

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
function Test()
{
"Try to calculate."
"3.1415926"
"Done."
}

#保存在变量中输出,
$value=Test
$value
#输出
Try to calculate.
3.1415926
Done.

#如果要过滤注释,只输出,不作为返回值,
#可以使用Write-Host命令
function Test()
{
Write-Host "Try to calculate."
"3.1415926"
Write-Host "Done."
}
# 在变量值中保存返回值,在控制台输出注释行
$value=Test
#控制台输出
Try to calculate.
Done.

# 测试返回值
$value
3.1415926

恭喜你!到此 PowerShell 基础入门算是完成了!
之后将介绍 PowerShell 的进阶使用

附录

PowerShell Math类常用函数表
函数名 调用写法 所在类 重载 描述 参数
sin [math]::sin(x) System.Math 1 返回x弧度的正弦值 double
cos [math]::cos(x) System.Math 1 返回x弧度的余弦值 double
abs [math]::abs(x) System.Math 7 返回x的绝对值 所有数值型
sqrt [math]::sqrt(x) System.Math 1 返回x的平方根 double
pow [math]::pow(x,n) System.Math 1 返回x的n次幂 两参数都是double
log [math]::log(x) System.Math 1 返回x的自然对数 double
log [math]::log(x,n) System.Math 1 返回x的以n为底的对数 double
min [math]::min(x,y) System.Math 11 返回x,y中的最小值 所有数值型
max [math]::max(x,y) System.Math 11 返回x,y中的最大值 所有数值型
floor [math]::ceiling(x) System.Math 2 返回x向下取整的结果 double、decimal
ceiling [math]::ceiling(x) System.Math 2 返回x向上取整的结果 double、decimal
math 类其他函数及详细用法参见微软文档: Math类

评论