Windows PowerShellpowershell deep learning

PowerShell is built on .NET:

  • Windows PowerShell 5.1 is built on top of the .NET Framework v4.5.
  • PowerShell is an open source project built on .NET Core.

安装

Windows

以下为安装PowerShell 7.x的方法(Windows内置PowerShell 5.1,PowerShell 7.x可与之并存)。

  1. msi安装,下载安装包点击执行或运行以下命令:

    msiexec.exe /package PowerShell-7.2.4-win-x64.msi /quiet \
        USE_MU=1 ENABLE_MU=1 # 通过Windows Update自动更新PowerShell
    
  2. winget安装:

    winget install Microsoft.PowerShell
    
  3. 安装为.NET全局工具

    dotnet tool install|update --global PowerShell # [Admin] 安装PowerShell 7.x
    
  4. 通过应用商店安装(有运行权限限制)。

Linux
  1. 从软件源安装

    sudo apt install -y wget apt-transport-https software-properties-common
    wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"
    sudo dpkg -i packages-microsoft-prod.deb # Register the repository GPG keys
    sudo apt update && sudo apt install -y powershell
    
  2. 直接从发布页面下载软件包安装。

安装相关路径:

  • $PSHOME=/opt/microsoft/powershell/7/
  • 用户配置文件:~/.config/powershell/profile.ps1
  • 用户模块目录:~/.local/share/powershell/Modules

PowerShell命令

PowerShell命令命名规则为Verb-Noun(动词-名词)形式。

探索PowerShell命令

  • Get-Verb:获取命令库中多数命令的开头动词(有助于用户使用命令);

  • Get-Command:返回所有已安装的命令。也可以添加参数以过滤列举内容(参数支持通配符*):

    Get-Command -Name|Verb|Noun <cmdlet> \            # 按名称/动词/名词匹配
                -CommandType Cmdlet, Function, Alias  # 按命令类型匹配
                -Module <module>                      # 按模块匹配
                -ParameterType Process                # 根据命令接受的主要参数的类型筛选
    Get-Command Get-Process -Syntax # 获取命令语法
    
  • Get-Alias:获取命令别名的信息。

    Get-Alias -Definition Get-Command,Get-Member # 获取别名
    Get-Alias ls,dir                             # 获取全名
    

    在脚本中尽量使用全名而非别名,以增强可读性。

  • Get-Member:对输出对象进行操作,并返回对象的属性和方法可根据参数过滤)。

    $s = Get-Service -Name w32time
    $s | Get-Member  # 返回对象的类型,及其包含属性的方法的名称、类型和定义
    
  • Get-Help <cmdlet-name>:获取命令的文档信息

模块管理

包管理模块

PowerShellGet用于查找、安装、更新和发布模块、脚本等内容。

错误:A parameter cannot be found that matches parameter name AllowPrerelease.:检查是否安装/导入PowershellGet模块。

安装/更新PowerShellGet的两种方法:

  1. 安装Nuget将自动安装PowerShellGet

    Install-PackageProvider -Name NuGet -Force # with Admin
    
  2. 直接安装PowerShellGet

    Install-Module -Name PowerShellGet -AllowClobber -Force
    
安装模块
Install-Module [-Name] <ModuleName*> 
  -Scope CurrentUser|AllUsers   # AllUsers needs Admin previllege
  -AllowPrerelease 
  -WhatIf
  -MinimumVersion 2.0.1 # -RequiredVersion -MaximumVersion
  -Force         # 安装同名不同版本模块
  -AllowClobber  # 覆盖同名模块  
Update-Module # 参数与Instal-Module相似
Uninstall-Module [-Name] <ModuleName>

安装模块前可以先进行搜索。首先查找本地是否已安装相应模块:

Get-Module -ListAvailable [-Name <ModuleName>]  # 从当前当前会话查找模块

-ListAvailable:列出指定模块的所有本地安装版本(可能包含系统内置版本和用户安装版本)。

如果本地不存在指定模块,可查找Nuget仓库(可使用Nexus搭建镜像仓库)。

Find-Module -Name <ModuleName>  # -> PSRepositoryItemInfo

如果本地存在指定模块,找到的模块会被加载到当前会话。

Find-Command -Name <CmdName> -ModuleName <ModName> # -> PSGetCommandInfo
	-AllowPrerelease 
	-RequiredVersion  # -MinimumVersion -MaximumVersion

Find-ModuleFind-Command的返回的结果可以传递给Install-Module执行安装。

导入模块

位于$env:PSModulePath路径下加模块会自动被加载。其他模块,在命令行或脚本中执行导入:

Import-Module [-Name] <ModuleName>
$m = Get-Module -ListAvailable PowershellGet, Dism
Import-Module -ModuleInfo $m
Import-Module -Name c:\ps-test\modules\test -Verbose

Import-Module

离线手工安装模块

可以从PowerShell Gallery下载模块的nupkg包。.nupkgzip格式封装了模块数据以及NuGet相关的描述数据(非模块本身的数据)。NuGet相关数据包括:

  • _rels/:包含模块的依赖声明(.rels文件);
  • package/:NuGet相关数据;
  • [Content_Types].xml:描述扩展模块如何与NuGet共同使用;
  • <name>.nuspec:包的元数据。

手工安装:将.nupkg中非NuGet相关数据解压到$env:PSModulePath中的一个路径下,并仅以包名命名该模块的文件夹1

文档

PowerShell文档介绍了PowerShell的cmdlet、函数、脚本及模块,并解释了PowerShell 语言的元素等概念。在命令行中使用Get-Help cmdlet来显示帮助主题。

Get-Help cmdname -examples|detailed|full|online
help cmdname   # => Get-Help cmdname | more
help Get-Command -Full | Out-GridView  # 在独立的文档查看器中查看帮助文档

如果本地没有帮助文件,Get-Help 会显示自动生成的有关 cmdlet、函数及脚本的帮助。

help不是Get-Help的别名:每次仅显示一页内容,需要手动翻页。

Get-Help会搜索与cmdname匹配的相关命令(类似于Get-Command),cmdname可包含通配符(==如果前后未添加*,则在名称中间添加*是无效的通配符==)。因此,当查找结果多于一条,将显示结果列表;反之显示查找结果的详细信息。

获取命令的参数信息:

help cmdname -Parameter <ParameterName>
获取本地文档

PowerShell 中默认不包含帮助文件,但可以联机查看帮助主题,或使用 Update-Help cmdlet将帮助文件下载到本地或在网站发布更新的时候更新本地文档。

Update-Help (Microsoft.PowerShell.Core) - PowerShell | Microsoft Docs

Update-Help \
	[-UICulture en-US,zh-CN]   # 指定文档语言
	[-Module Microsoft.PowerShell*] # 更新指定模块的文档(默认更新所有已安装的模块)
	-SourcePath path   # Save-Help

开发环境

Debugging with Visual Studio Code

变量

PowerShell accepts and returns .NET objects, rather than text.

设置变量:

$JAVA_HOME="C:\tools\java"
Set-Variable -Name 'JAVA_HOME' -Value 'C:\tools\java'  # -> set*

*setSet-Variable的别名,与cmd的变量设置命令set无关。

==变量名不区分大小写==。

输出变量:

$JAVA_HOME            # => echo $JAVA_HOME
$JAVA_HOME.ToUpper()  # 调用变量对象的属性或方法
$v=Get-Variable -Name 'JAVA_HOME'  # get (Name,Value) object
$v.Value

数据类型

空值

Everything you wanted to know about $null - PowerShell | Microsoft Docs

字符串

字符串需要使用''""进行表示,否则相应内容将被视为shell命令。==字符串使用+拼接==。

Everything you wanted to know about variable substitution in strings - PowerShell | Microsoft Docs

裁剪字符串
str.Trim()          # TrimEnd/TrimStart
str.Trim("a", " ")  # 可指定多个裁剪模式(单个字符)

返回新的对象。

分割字符串
$array="abcdefghi".split("de")  # -> -split运算符

数组

许多命令(如Get-Process)的返回值都是一个数组。手动构造数组方法:

$data=@('Zero','One','Two','Three')  # 空数组 $data = @()
$data=('Zero','One','Two','Three')   # => $data = 'Zero','One','Two','Three'

数组元素可换行声明,此时两行间的元素不需要通过,分隔。

通过迭代构造数组:

$array = 1..5 | ForEach-Object { "ATX-SQL-$PSItem" }
$array = foreach ( $node in (1..5)){ "ATX-SQL-$node" }

数组默认类型为[PSObject[]],即其元素都继承自PSObject。在创建数组时可指定严格的类型限制:

[int[]] $numbers = 1,2,3

预分配内存的数组:

$data = [Object[]]::new(4)

嵌套数组:

$data = @(
    @(1,2,3),  # 嵌套数组元素换行时仍需","分隔
    @(4,5,6),
    @(7,8,9)
)
$data[1][2]  # -> 6

多维数组:

[string[,]]$rank2 = [string[,]]::New(3,2)
数组属性
Write-Output -NoEnumerate $data | Get-Member # 获取数组的属性和方法
$data | Get-Member  # 获取数组元素的属性和方法
$data.Count  # 数组元素数量 => $data.Length
$data.Rank   # 数组维数

不仅数组可以获取长度,标量对象也可(返回1),空数组长度为0(特别地$null.count->0,注意区别)。

访问元素
$data[i]      # 0-based index
$data[0,2,3]  # 获取子数组(可添加重复编号以生成重复元素)
$data[1..3]   # 切片(区间可反向,即"3..1:")
$data[-1]     # 负索引
$data[i,-j]   # => [i,i-1,...0,-1,...-j] (与Python负索引语义不同)

访问不在数组长度范围内元素将返回$null而非产生异常。

i..j将自动生成一个整数数组。

访问数组元素属性(链式调用):

$data[0].PropName
$data.PropName   # 返回所有元素的PropName返回值组成的新数组
更新数组
$data[2] = 'dos'  # 索引编号超过数组长度将产生异常
$data.Clear()     # 重置数组元素的值为默认值
迭代数组
$data.foreach({"Item [$PSItem]"})          # => $data.foreach{"Item [$PSItem]"}
$data | ForEach-Object {"Item: [$PSItem]"} # $PSItem => $_
foreach ( $node in $data ){ "Item: [$node]" }
for ( $index = 0; $index -lt $data.count; $index++){
    "Item: [{0}]" -f $data[$index]
}
switch( $data ) {...}  # 对数组每个元素执行分支判断

当数组元素类型为值类型时,使用for语句块可在迭代数组时对其进行更新。反之,如果为引用类型,则其他迭代语句也可对数组元素的属性进行更改。

ForEach-Object -Parallel[7.x]:管道并行。

数组对象运算

数组对象运算都将产生新的数组对象。

  1. 字符串拼接:使用连接符将数组内容拼接为一个字符串对象。

    $data -join '-' 
    
  2. 替换:

    $data -replace 'ATX','LAX'  # 对数组每个元素执行替换
    
  3. 查找:返回True|False-contains, -notcontains-in, -notin

    $data -contains 'green'  # => 'green' -in $data
    
  4. 比较或匹配过滤(-eq, -ne, -match):数组与对象比较返回匹配对象(或子数组):

    $data -eq 'green'
    $servers -match 'SQL' # => $servers | Select-String SQL
    

    判断数组对象是否为$null,应该使用$null -eq $array避免上述语法对数组的$null值的筛选。

  5. 追加:

    $data = $data + 'four'  # $data += 'four'
    
  6. 拼接:

    $array=$array1+$array2
    $array = $array * 3    # 复制并拼接(使用这种方法可构造初始值为固定值的数组)
    
数组过滤

基于数组元素或元素属性的值过滤元素。如果数组元素本身就是字符串对象,则直接在迭代语句中使用$_

$data | Where-Object {$_.Name -eq w32time}
$data.Where({$_.FirstName -eq 'Kevin'})   # 使用成员函数
$data | Select-Object -First 3
分组统计
$data | Group-Object -Property ModuleName | 
        Sort-Object -Property Count -Descending

字典(hashtable)

$hashtable = @{}
$hashtable['key'] = $value 

字典可传递给命令,以代替逐个传递命令行参数,方便动态构造传入参数。

Everything you wanted to know about hashtables - PowerShell | Microsoft Docs

运算符

具有相同优先级的运算符从左到右依次计算。例外:赋值运算符、类型转换运算符、和取反运算符(!-not-bnot)从有至左计算。

可以使用()来显式限定优先计算的部分表达式。

运算符优先级从高到低2

  • $(), @(), (), @{}

  • . ?. (member access)

  • :: (static)

  • [0] ?[0] (index operator)

  • [int] (cast operators)

    [datetime]$birthday = "1/10/66"   # 将birthday转换为datetime类型
    
  • -split (unary)

  • -join (unary)

  • , (comma operator)

  • ++ --:自增运算符,支持前置或后置。

  • ! -not:逻辑取反运算。

  • .. (range operator)

  • -f (format operator)

    "{0:N2} - {1}" -f 10 1.5
    
  • - (unary/negative)

  • * / %

  • + -

  • 以下命令具有同等优先级:

    • -split -join (binary):分割字符串(合并见数组运算)。

      $array="abcdefghi" -split "de" # -> $array="abcdefghi".split("de")
      
    • -is -isnot:判断是否为.Net Framework类型。

      42 –is [int]
      
    • -as:类型转换。

      $a = 42 –as [String]
      
    • -eq -ne -gt -ge -lt -le:比较运算符。涉及字符串比较的运算符默认为大小写不敏感的,带前缀c的运算符(-ceq)为大小写敏感的(具有同等优先级)。

      不支持==,!=,>,<=,<,<=

    • -match -notmatch:正则表达式匹配,返回是否匹配;

    • -like -notlike:通配符匹配;

    • -in -notIn-contains -notContains

    • -replace:字符串替换。

      "abcde" -replace "bc", "TEST"  # replace bc with TEST
      # aTESTde
      
  • -band -bnot -bor -bxor -shr -shl:位运算,-shl, -shr(移位)。

  • -and -or -xor:逻辑运算。

  • 以下命令非真正的运算符而是PowerShell命令语法的一部分,在命令解释执行过程中具有最低优先级。

    • . (dot-source)

    • & (call)

    • ? <if-true> : <if-false> (Ternary operator)

    • ?? (null-coalese operator)

    • | (pipeline operator)

    • > >> 2> 2>> 2>&1

    • && || (pipeline chain operators)

    • = += -= *= /= %= ??=:赋值运算符。

      $result=$a*$b
      

容器类型

数组定义后无法增加元素,PowerShell可调用多种.NET容器类型以解决此问题。

ArrayList

$myarray = [System.Collections.ArrayList]::new()
[void]$myArray.Add('Value')   # [void] to suppress return value.

List

ArrayList不支持泛型,因此类似于数组默认存储PSObject元素。List类型支持泛型(类似于数组声明时限定类型):

$mylist = [System.Collections.Generic.List[int]]::new()
$mylist = [System.Collections.Generic.List[int]]@(1,2,3) # 将数组转换为List

对于类型元素多样的情况,也可使用PSObject作为类型参数。

ListArrayList都支持元素的的增加和删除。

[void]$myList.Remove("Two")      # -> True|False: if element is found
[void]$drives.Remove($drives[2]) # remove reference type

StringBuilder

https://powershellexplained.com/2017-11-20-Powershell-StringBuilder/

其他容器类型

Types.md (github.com)

时间日期

date=Get-Date \
	-Format "dddd MM/dd/yyyy HH:mm K" \  # 返回个格式化字符串。
   -UFormat "%A %m/%d/%Y %R %Z" \
	-DisplayHint Date \  # 仅显示日期
	-Year 2020 -Month 12 -Day 31 \ # 用户初始化
 | Specifier | Definition |
 | --- | --- |
 | `dddd` | Day of the week - full name |
 | `MM` | Month number |
 | `dd` | Day of the month - 2 digits |
 | `yyyy` | Year in 4-digit format |
 | `HH:mm` | Time in 24-hour format -no seconds |
 | `K` | Time zone offset from Universal Time Coordinate (UTC) |
 
 | Specifier | Definition |
 | --- | --- |
 | `%A` | Day of the week - full name |
 | `%m` | Month number |
 | `%d` | Day of the month - 2 digits |
 | `%Y` | Year in 4-digit format |
 | `%R` | Time in 24-hour format -no seconds |
 | `%Z` | Time zone offset from Universal Time Coordinate (UTC) |

.NET format specifiers (/dotnet/standard/ba se-types/custom-date-and-time-format-strings?view=netframework-4.8)

date.DayOfYear
$timelabel = Get-Date -Format 'yyyy-MM-ddTHHmmss'
New-Item -Path C:\Test\$timelabel -Type Directory

内置变量

$PSVersionTable   # Powershell版本信息
$PROFILE          # Powershell配置文件路径

对象

使用Get-Noun方法获取对象,其中None代表对象类型。

对象成员

获取对象类型,及其成员属性/方法信息。

$Obj | Get-Member
$Obj | Get-Member -MemberType {Method|Property...} -Name name

Get-Member仅显示默认显示的成员。如果要显示所有成员,可使用Select-Object筛选对象属性,返回仅包含指定属性(模式)的对象。

$Obj | Select-Object -Property {*|FriendlyName,Issuer}
                     -ExpandProperty SerialNumber  # 将返回值转换为字符串而非对象

*获取对象所有属性,反之给出要获取的属性名列表(可包含通配符)。

如果管道输入一个序列,则返回每个对象的属性值。

对象方法

对象方法通常用于修改对象。

访问对象属性
$x=$myObject.PropertyName

动态访问对象的属性:

$PROP = 'Name'
$myObject.$PROP   # $myObject."Name" => $myObject.Name

Providers

提供访问数据的统一接口,数据以类似文件系统的树形结构组织。

Get-PSProvider列出所有支持的数据接口类型,包括文件系统、证书目录、注册表等。特别地,==PowerShell的函数、环境变量==等也支持该接口。

Get-PSProvider
# Name           Capabilities                          Drives
# ----           ------------                          ------
# Registry       ShouldProcess, Transactions           {HKLM, HKCU}
# Alias          ShouldProcess                         {Alias}
# Environment    ShouldProcess                         {Env}
# FileSystem     Filter, ShouldProcess, Credentials    {C, D, E, F}
# Function       ShouldProcess                         {Function}
# Variable       ShouldProcess                         {Variable}
# Certificate    ShouldProcess                         {Cert}
# WSMan          Credentials                           {WSMan}

Get-PSDrive获取所有支持类型的数据源实例信息。

Get-PSDrive
# Name      Used (GB)  Free (GB) Provider      Root               
# ----      ---------  --------- --------      ----               
# Alias                          Alias                            
# Cert                           Certificate   \                  
# C            256.94     674.00 FileSystem    C:\                
# D            310.46    1552.56 FileSystem    D:\                
# E            920.11    6531.80 FileSystem    E:\                
# F                              FileSystem    F:\                
# Env                            Environment                      
# Function                       Function                         
# HKCU                           Registry      HKEY_CURRENT_USER  
# HKLM                           Registry      HKEY_LOCAL_MACHINE 
# Variable                       Variable                         
# WSMan                          WSMan                            

省略了CurrentLocation属性,调整了显示顺序。

使用Get-ChildItem读取数据源路径下的数据。

Get-ChildItem -Path Cert:\LocalMachine\CA
Get-ChildItem -Path Function:

路径的根为Name:,其中Name为数据源实例名称,如C:Cert:等。

Services

Get-Service <Name>

调用服务的方法:

$service=$(Get-Service -Name w32time)
$service.Stop()

成员方法可用于修改获取的对象,而通常没有相关cmdlet直接修改对象。

自定义对象类型

$obj = [pscustomobject]@{
   FirstName='Kevin';
   LastName='Marquette'
}
添加和删除属性
$myObject | Add-Member -MemberType NoteProperty -Name 'ID' -Value 'KevinMarquette'
$myObject.psobject.properties.remove('ID')

添加方法

$scriptBlock = {...}  # 使用this引用目标对象
Add-Member -MemberType ScriptMethod -InputObject $myObject -Name 'ToHashtable' -Value $scriptBlock

语法

语句

使用;在一行书写多条语句;

换行

,{}[]();='"可以支持命令跨行书写。

ps_cmd (expr)           # "(...)" 优先计算子表达式并返回值

如果命令中包含需要首先展开的参数,则使用&对参数进行展开,然后执行展开后的命令语句。

& $str_cmd $str_opts    # 执行字符串参数所表示的命令

==在行末使用`强制命令换行以增强可读性==,换行命令间可包含多行注释内容<#...#>

引号

In PowerShell, you should always use single quotes instead of double quotes unless the contents of the quoted string contains a variable that needs to be expanded to its actual value. By using single quotes, PowerShell doesn't have to parse the contents contained within the quotes so your code runs a little faster.

注释

单行注释:#后续内容为注释;

多行注释:<# comment #>

管道

通过管道传递对象(变量、数组)将多个命令连接起来。

dir | Sort-Object -Descending | Select-Object -First 1

使用管道操作符连接的命令可在操作符后换行,以方便阅读。

命令必须产生输出才能传递给管道,使用-PassThru参数强制命令输出内容。通过帮助文档可查看命令的输入和输出要求(INPUTOUTPUT),查看参数帮助文档以确定参数值是否接受管道输入以及接受输入的类型(Accept pipeline input? True (ByValue, ByPropertyName))。

Get-Command -ParameterType ServiceController

帮助文档中可能会给出命令的输入输出说明(是否接受管道输入)。

非PowerShell命令输出的文本行会被自动转换为字符串对象,因此后续命令可基于字符串对象进行计算(如过滤)。

左侧过滤优先

优先使用命令参数过滤返回结果(除非过滤条件没有对应参数支持);对返回结果进行过滤(Where-Object)可能产生较大开销。

右侧格式化优先

尽可能最后对数据执行格式化输出(如Format-Table)。

流程控制

分支

if (<result1-to-be-matched> -eq (<test-expression>)) {<action>}

Everything you wanted to know about the if statement - PowerShell | Microsoft Docs

switch [-regex| -wildcard| -exact] [-casesensitive](<test-expression>)
{
    <result1-to-be-matched> {<action>}
    <result2-to-be-matched> {<action>}
    default { <action-scriptblock> }
}

默认为精确比较(-exact)。如果输入对象为数组,则对每个元素进行计算

Everything you ever wanted to know about the switch statement - PowerShell | Microsoft Docs

循环

for ($i = 1; $i -lt 5; $i++) {
	Write-Output "Sleeping for $i seconds"
	Start-Sleep -Seconds $i
}

数组迭代方法

do{ statements } until (cond_expr)
do{ statements } while (cond_expr)
while (cond_expr) { statements }

跳转

使用breakcontinuereturn控制循环执行(loop, switch)。

labeled continue

用于连续中断多层循环。

:labelA for ($i = 1; $i -le 10; $i++) {
    :labelB for ($j = 1; $j -le 10; $j++) {
        :labelC for ($k = 1; $k -le 10; $k++) {
            if ($conditionA) {
                continue labelA   # 连续中断
            else if ($conditionB) {
                continue labelB   # 等效于brek
            } else {
                $condition = Update-Condition
            }
        }
    }
}

异常处理

try { CmdName -ErrorAction Stop } catch {...}

Only terminating errors are caught.

Everything you wanted to know about exceptions - PowerShell | Microsoft Docs

函数

函数命名遵循Verb-Noun原则(通过Get-Verb查看常用动词)。

function Get-Version {
    [CmdletBinding()] # << turns into an advanced function
    param (
      [Parameter(Mandatory)]                # 必须提供的参数
      [String[]]$ComputerName,              # 声明类型以在运行时自动执行参数类型校验
      
      [ValidateNotNullOrEmpty()]             # 可选参数
      [string[]]$ComputerName = $some_value, # 可选参数的默认值
      
      [Parameter(Mandatory,ValueFromPipeline)] # 接受管道传递参数(by value
      [String[]]$ComputerName, # ValueFromPipelineByPropertyName(by name)
	)
	dynamicparam {<statement list>}
	begin { begin_statements }
	process { process_statements }
	end { end_statements }
}
function Get-Version([type1]$param1[,[type2]$param2]) {}

通过param声明输入参数,参数命名尽量与内置标准命令方式一致,多个参数以,分隔;对于较为简单的参数声明,也可以直接在函数名后声明。

输入参数

参数类型
  • 命名参数:通过param()语句声明的参数,可以指定默认值,为指定默认值的参数在运行时必须提供参数值,否则函数报错。
  • 位置参数:非命名参数,通过$args[i]数组访问。可通过@args将所有参数传递给其他函数或命令。
  • 开关参数:通过param()声明类型为[switch]的参数。不需要为该参数提供值,当该参数选项出现在命令时,参数的的值为True
  • 动态参数:
从管道读取参数

beginprocessend语句块用于处理管道输入。beginend会在函数开始和结束时分别被执行一次,而process语句块则对每一个管道输入对象$_执行一次。

如果声明了以上语句块,则函数的所有语句都必须位于语句块中。未声明语句块的情况,所有语句相当于位于end语句块中。

过滤器

过滤器(Filters)可被看作只有process代码块的函数。

返回值

函数的返回值可以在终端显示、赋值给变量或转递给其他函数/命令。函数中可通过调用的命令返回数据,或通过调用return返回数据对象。return语句会立即令函数返回。

高级函数

高级函数具有自动添加的公共参数,例如DebugVerbose

Everything you wanted to know about ShouldProcess - PowerShell | Microsoft Docs

cmdlets

注释文档

函数注释文档可位于:函数体开头(位于{之后的行);函数体结束前(位于}之前),或==函数定义关键字前function的行(不可有空行隔开)==。

脚本注释文档位于脚本开始或结束。

脚本模块注释文档.psm1与函数注释文档语法一致。

<#
.SYNOPSIS
    函数用途简要说明。

.DESCRIPTION
    函数功能的详细描述。

.PARAMETER ComputerName
    参数说明

.EXAMPLE
     使用示例。

.INPUTS
    输入类型

.OUTPUTS
    输出类型

.NOTES
    Author:  Mike F Robbins
    Website: http://mikefrobbins.com
    Twitter: @mikefrobbins
#>

PARAMETEREXAMPLE可声明多次。

命名空间

在脚本开始位置引入命名空间,方便引用其中的类型和方法。

using namespace System.Collections.Generic
$myList = [List[int]]@(1,2,3)

作用域

about Scopes - PowerShell | Microsoft Learn

输入输出

Get-Content [-Path] filepath      # -> cat, type
Write-Output 'message' > filename # -> echo
'message' > filename
'message' | Out-File -FilePath filename

不同级别的输出函数:

Write-Verbose -Message "message"  # 仅在开启Verbose选项时输出
Write-Debug
Write-Information
Write-Warning
Write-Error

重定向

输出重定向

n>, n>>:输出流n=* All output;1 Success output;2 Errors;3 Warning messages;4 Verbose output;5 Debug messages。

吸收输出
  • expr | Out-Nullexpr > $null
  • [void] expr
  • $null=expr

格式输出

当输出为多个对象组成的列表格式(即key:value形式),可通过Format-Table将其转换为表格形式:

$obj | Select-Object -Property Name,Type,Status | Format-Table

反之,可以将表格输出转换为列表输出。

Format-List

Format-*命令会将输出对象封装为Foramt对象,因此可能不再能传递给其他命令

文本模式输出

将对象转换为文本输出(可用于传统基于文本的命令进行后续处理):

Find-Module *cim* | Out-String -Stream | grep CimSession

-Stream将输出按行分解便于后续处理。

-Width设置输出宽度,避免换行或截断。

网络应用

The Invoke-WebRequest cmdlet sends HTTP and HTTPS requests to a web page or web service. It parses the response and returns collections of links, images, and other significant HTML elements.

Invoke-WebRequest # alias => curl,wget
	-Uri <uri>
	-Headers <dict>  # http header
	-Body <obj>      # http body
	-InFile <file>   # read request from file
	-OutFile <filename>  # 未指定则输出到管道
	-Method <m>      # http method: get/put/post...

脚本

脚本文件类型.ps1

在当前Shell环境执行脚本。

. 'c:\scripts\sample.ps1'
脚本文件编码

PowerShell 5.x默认使用的是本地化编码方案(如GBK/936),因此如果包含非ASCII码文字的脚本应该使用对应区域的本地化编码方案保存。(例外:VS Code中的集成终端代码页为936,但能正确读取UTF-8编码)

PowerShell 7.x默认使用UTF-8/65001编码。

PowerShell解释器会首先读入整个脚本文件内容,并检查语法错误。如果出现编码问题导致语法错误,则会直接抛出语法错误而不执行文件。

通过启动配置$Profile修改终端的默认编码(由于.NET缓存机制的原因,在启动后的PowerShell终端中调用chcp命令无效chcpcmd中是有效的):

$OutputEncoding = [console]::InputEncoding `
                = [console]::OutputEncoding `
                = New-Object System.Text.UTF8Encoding

作用域

全局作用域

位于脚本中的函数的作用域为脚本(Script scope),当脚本被加载或执行完后,脚本作用域中的函数不再可见。使用.命令加载脚本中的函数到全局作用域(Global scope)。

. .\My-Script.ps1

脚本模块

.psm1保存的脚本。如果脚本模块保存在PoweShell模块搜索路径下,则会被自动加载;反之,需要手动导入

Export-ModuleMember -Function Get-PublicFunction  # 在模块中导出定义公开函数

为模块创建元数据(元数据存储路径通常和模块位于同一路径下):

New-ModuleManifest -Path $ModulePath\MyScriptModule\MyScriptModule.psd1 
                   -RootModule MyScriptModule      # 必须指定
                   -Author 'Mike F Robbins' 
                   -Description 'MyScriptModule' 
                   -CompanyName 'mikefrobbins.com'
Update-ModuleManifest # 更新元数据,重复调用New-ModuleManifest会导致GUID变化                   

导出的公开函数也可在元数据文件中指定:

FunctionsToExport = 'Get-PublicFunction'

运行环境

环境变量

读取环境变量
Get-ChildItem env:  # 注意结尾的“:”
Get-ChildItem Env:JAVA_HOME # => Get-ChildItem Env:\JAVA_HOME
echo $Env:JAVA_HOME         # echo ${Env:JAVA_HOME} => echo ${Env:\JAVA_HOME}
Get-ChildItem Env: | Format-Table -Wrap -AutoSize
设置环境变量
$Env:JAVA_HOME='C:\tools\jdk'  # 仅当前会话有效
$Env:Path='C:\tools\jdk;'+$Env:Path

路径搜索优先级:从左至右搜索,将新路径置于左侧将覆盖已有配置。

脚本输入参数

脚本的输入参数格式与函数输入参数语法格式一致。

param (
    [string]$Target = '.\src',
    [string]$Source = '.\target'
)

定义的参数可在命令行中获得自动补全功能。

语句的退出状态码

$?             # return 'True'/'False' for success/error
$LastExistCode # Return 0 for success

执行策略

PowerShell 执行策略是一项安全功能,用于控制 PowerShell 加载配置文件和运行脚本的条件。 此功能有助于防止恶意脚本的执行。可以为本地计算机、当前用户或特定会话设置执行策略。 你还可以使用组策略设置为计算机和用户设置执行策略。本地计算机和当前用户的执行策略存储在注册表中。 不需要在 PowerShell 配置文件中设置执行策略。 特定会话的执行策略仅存储在内存中,并在会话关闭时丢失。

执行策略不是限制用户操作的安全系统。 例如,当用户无法运行脚本时,可以通过在命令行中键入脚本内容来轻松地绕过策略。 相反,执行策略可帮助用户设置基本规则并防止无意中违反它们。

策略详情
AllSigned脚本必须由信任发布方签名;在未确认的脚本运行前提示用户;==可能面临运行已签名的恶意脚本的风险==。
Bypass不会阻止且不会产生警告或信息;
Default默认策略:Restricted for Windows clients.RemoteSigned for Windows servers.
RemoteSigned来自互联网的脚本需要来自信任发布方的数字签名;本地脚本允许执行。
Restricted==允许单独执行命令,但不允许运行脚本==;
UndefinedIf the policy in all scopes is Undefined, use Default.
Unrestricted非Windows主机的默认策略;未签名脚本可以运行,非本地脚本运行前警告用户。
设置策略
Set-ExecutionPolicy <policy>

标准库

WMI和CIM

Windows Management Instrumentation (WMI)模块:弃用。

Common Information Model (CIM)模块:跨平台,通过CIM可访问WMI。

Get-Command -Module CimCmdlets
Get-CimInstance -Query 'Select * from Win32_BIOS'  # WMI Query Language (WQL)
Get-CimInstance -ClassName Win32_BIOS

远程管理(指定ComputerName参数):以域管理员启动PowerShell并执行

Get-CimInstance -ComputerName dc01 -ClassName Win32_BIOS

或者首先提供凭据(密码)并创建CIM会话:

$CimSession = New-CimSession -ComputerName dc01 -Credential (Get-Credential)
Get-CimInstance -CimSession $CimSession -ClassName Win32_BIOS

远程登录

从Windows可选功能中安装OpenSSH Server。

Installation of OpenSSH For Windows Server 2019 and Windows 10

远程登录后默认的Shell是cmd.exe,如果要启用PowerShell,可在命令窗口中运行powershell,或者直接在远程登录命令中运行该命令。

ssh user@host "chcp65001 && powershell"

直接进入Powershell会导致中文乱码,需要先切换编码到UTF-8

PowerShell Remoting Over SSH - PowerShell | Microsoft Docs

附录

cmd

注释语句:

REM comment something

使用^对单条命令进行换行,^后不可包含任何符号。

变量

变量名不区分大小写。查看变量:

set [ | more]
set var           # print: key=value
echo %variable%   # 使用`%%`读取变量的值,未定义的变量将打印变量名
定义console环境变量
set variable=value   # 必须设用set,不支持VAR=VALUE

PATH既是环境变量,也是一个命令(单独使用PATH可显示PATH变量的值,也可使用PATH=valuePATH变量赋值,普通变量必须使用set命令)。

参数变量:

%1 %2 %3 %4 %5 %6 %7 %8 %9
%~dp0

获取当前执行文件所在目录。

扩充变量语法

字符串

字符串不需要使用引号表示,单双引号均为普通字符。将两个字符串连接即视为字符串拼接。

环境变量
echo %USERPROFILE%   # 用户目录
echo %PROGRAMDATA%   # 应用程序配置文件路径
chcp 65001  # 支持PowerShell

Windows Code page 936, is Microsoft's character encoding for simplified Chinese, one of the four DBCSs for East Asian languages. Originally, Windows-936 covered GB 2312 (in its EUC-CN form), but it was expanded to cover most of GBK with the release of Windows 95.

https://en.wikipedia.org/wiki/UTF-8#Official_name_and_variants.

Windows Code Page: 936/1386 (GBK), 950/1370 (Big5), 1200 (UTF-16LE), 1201 (UTF-16BE), 54936 (GB18030), 65001 (UTF-8).

环境变量作用域
setlocal  # 执行之后所做的环境改动只限于批处理文件直到endlocal。
endlocal  # 脚本结束前隐式执行

语句

dir & echo foo
dir && echo foo   # execute only if the first exited successfully

batch file - How do I run two commands in one line in Windows CMD? - Stack Overflow

常用命令

help [cmd]  # cmd /?
call jobname.bat

调用脚本。

pause

暂停。

exit

退出。

cd [/D] path 

切换当前目录。

type file

输出文件内容。

流程控制

if [not] ERRORLEVEL <number> <command> [else <expression>]
if [not] <string1> == <string2> <command> [else <expression>]
if [not] exist <filename> <command> [else <expression>]

errorlevel:上一条命令的执行状态。

if | Microsoft Docs

for {%% | %}<variable> in (<set>) do <command> [<commandlineoptions>]
# for %f in (*.doc *.txt) do type %f

for | Microsoft Docs

控制台输入输出

echo on|off      # 控制是否回显命令行提示符
echo some string 

字符串不需要使用引号表示,单双引号均为普通字符。

参考资料