当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 声明

作者:精品下载站 日期:2024-12-14 02:56:00 浏览:13 分类:玩电脑

声明


8.1 语句块和列表

语法:

有用的提示

语法定义中的 ~opt~ 表示法表示词汇实体在语法中是可选的。

statement-block:
    new-lines~opt~ { statement-list~opt~ new-lines~opt~ }

statement-list:
    statement
    statement-list statement

statement:
    if-statement
    label~opt~ labeled-statement
    function-statement
    flow-control-statement statement-terminator
    trap-statement
    try-statement
    data-statement
    inlinescript-statement
    parallel-statement
    sequence-statement
    pipeline statement-terminator

statement-terminator:
    ;
    new-line-character

描述:

语句指定要执行的某种操作。除非本条款中另有说明,否则语句按词汇顺序执行。

语句块允许将一组语句分组为单个语法单元。

8.1.1 带标签的语句

语法:

labeled-statement:
    switch-statement
    foreach-statement
    for-statement
    while-statement
    do-statement

描述:

迭代语句(第 8.4 节)或 switch 语句(第 8.6 节)可以选择紧接一个语句标签,标签。语句标签用作 break (§8.5.1) 或 continue (§8.5.2) 语句的可选目标。然而,标签不会改变控制流。

冒号 (:) 与其后面的标记之间不允许有空格。

示例:

:go_here while ($j -le 100) {
    # ...
}

:labelA
for ($i = 1; $i -le 5; ++$i) {
    :labelB
    for ($j = 1; $j -le 3; ++$j) {
        :labelC
        for ($k = 1; $k -le 2; ++$k) {
            # ...
        }
    }
}

8.1.2 声明值

语句的值是它写入管道的累积值集。如果该语句写入单个标量值,则该值就是该语句的值。如果该语句写入多个值,则该语句的值是按写入顺序存储在无约束一维数组的元素中的一组值。考虑以下示例:

$v = for ($i = 10; $i -le 5; ++$i) { }

没有循环迭代,也没有任何内容写入管道。该语句的值为$null

$v = for ($i = 1; $i -le 5; ++$i) { }

尽管循环迭代了五次,但没有任何内容写入管道。该语句的值为$null。

$v = for ($i = 1; $i -le 5; ++$i) { $i }

每次将 int$i 写入管道时,循环会迭代五次。该语句的值为长度为 5 的 object[]

$v = for ($i = 1; $i -le 5; ) { ++$i }

尽管循环迭代了五次,但没有任何内容写入管道。该语句的值为$null

$v = for ($i = 1; $i -le 5; ) { (++$i) }

该循环迭代五次,每个值都被写入管道。该语句的值为长度为 5 的 object[]

$i = 1; $v = while ($i++ -lt 2) { $i }

循环迭代一次。该语句的值为 int,值为 2。

以下是一些其他示例:

# if $count is not currently defined then define it with int value 10
$count = if ($count -eq $null) { 10 } else { $count }

$i = 1
$v = while ($i -le 5) {
    $i                   # $i is written to the pipeline
    if ($i -band 1) {

        "odd"            # conditionally written to the pipeline

    }

    ++$i                 # not written to the pipeline

}
# $v is object[], Length 8, value 1,"odd",2,3,"odd",4,5,"odd"

8.2 管道语句

语法:

pipeline:
    assignment-expression
    expression redirections~opt~ pipeline-tail~opt~
    command verbatim-command-argument~opt~ pipeline-tail~opt~

assignment-expression:
    expression assignment-operator statement

pipeline-tail:
    | new-lines~opt~ command
    | new-lines~opt~ command pipeline-tail

command:
    command-name command-elements~opt~
    command-invocation-operator command-module~opt~ command-name-expr command-elements~opt~

command-invocation-operator: one of
    &   .

command-module:
    primary-expression

command-name:
    generic-token
    generic-token-with-subexpr

generic-token-with-subexpr:
    No whitespace is allowed between ) and command-name.
    generic-token-with-subexpr-start statement-list~opt~ )

command-namecommand-name-expr:
    command-name

primary-expressioncommand-elements:
    command-element
    command-elements command-element

command-element:
    command-parameter
    command-argument
    redirection

command-argument:
    command-name-expr

verbatim-command-argument:
    --% verbatim-command-argument-chars

描述:

重定向在第 7.12 节中讨论; 赋值表达式在第 7.11 节中讨论;和命令调用操作符点(.)在§3.5.5中讨论。有关命令调用中参数到参数映射的讨论,请参阅第 8.14 节。

管道中的第一个命令是表达式或命令调用。通常,命令调用以命令名称开始,它通常是一个裸标识符。 command-elements 表示命令的参数列表。换行符或 n 个未转义的分号终止管道。

命令调用由命令名称后跟零个或多个参数组成。管辖争论的规则如下:

  • 不是表达式但包含不带未转义空格的任意文本的参数将被视为双引号。保留字母大小写。

  • 变量替换和子表达式扩展(第 2.3.5.2 节)发生在 expandable-string-literalexpandable-here-string-literal 内部。

  • 引号内的文本允许在参数值中包含前导、尾随和嵌入的空格。 [注意:带引号的参数中存在空格不会将单个参数变成多个参数。 尾注]

  • 在参数两边加上括号会导致对该表达式进行求值,并传递结果而不是原始表达式的文本。

  • 要传递看起来像开关参数(第 2.3.4 节)但并非如此的参数,请将该参数括在引号中。

  • 当指定与具有 [switch] 类型约束 (§8.10.5) 的参数相匹配的参数时,参数名称本身的存在会导致该参数被设置为 $true 。但是,可以通过向参数附加后缀来显式设置参数的值。例如,给定类型约束参数 p-p:$true 参数将 p 设置为 True,而 -p:$false将 p 设置为 False。

  • -- 参数表示其后面的所有参数均以其实际形式传递,就像在它们周围放置双引号一样。

  • --% 参数表示其后面的所有参数都将以最少的解析和处理进行传递。该参数称为逐字参数。逐字参数后面的参数不是 PowerShell 表达式,即使它们在语法上是有效的 PowerShell 表达式。

如果命令类型为应用程序,则不会将参数--%传递给命令。 --% 之后的参数扩展了任何环境变量(由 % 包围的字符串)。例如:

echoargs.exe --% "%path%" # %path% is replaced with the value $env:path

参数的评估顺序未指定。

有关参数绑定的信息,请参阅第 8.14 节。有关名称查找的信息,请参阅§3.8。

一旦参数处理完成,就会调用该命令。如果调用的命令正常终止(第 8.5.4 节),则控制权将恢复到紧随命令调用之后的脚本或函数中的点。有关异常终止行为的描述,请参阅 break (§8.5.1)、continue (§8.5.2)、throw (§ 8.5.3)、exit (§8.5.5)、try (§8.7) 和 trap (§8.8)。

通常,通过使用命令名称后跟任何参数来调用命令。但是,可以使用命令调用运算符 &。如果命令名称包含未转义的空格,则必须使用此运算符将其加引号并调用。由于脚本块没有名称,因此也必须使用此运算符来调用它。例如,命令调用 Get-Factorial 的以下调用是等效的:

Get-Factorial 5
& Get-Factorial 5
& "Get-Factorial" 5

允许直接和间接递归函数调用。例如,

function Get-Power([int]$x, [int]$y) {
    if ($y -gt 0) { return $x * (Get-Power $x (--$y)) }
    else { return 1 }
}

示例:

New-Object 'int[,]' 3,2
New-Object -ArgumentList 3,2 -TypeName 'int[,]'

dir e:\PowerShell\Scripts\*statement*.ps1 | Foreach-Object {$_.Length}

dir e:\PowerShell\Scripts\*.ps1 | Select-String -List "catch" | Format-Table path,linenumber -AutoSize

8.3 if语句

语法:

if-statement:
    if new-lines~opt~ ( new-lines~opt~ pipeline new-lines~opt~ ) statement-block
        elseif-clauses~opt~ else-clause~opt~

elseif-clauses:
    elseif-clause
    elseif-clauses elseif-clause

elseif-clause:
    new-lines~opt~ elseif new-lines~opt~ ( new-lines~opt~ pipeline new-lines~opt~ ) statement-block

else-clause:
    new-lines~opt~ else statement-block

描述:

管道控制表达式必须具有 bool 类型或者可以隐式转换为该类型。 else 子句是可选的。可能有零个或多个elseif-clause

如果顶级管道测试为True,则执行其语句块并终止该语句的执行。否则,如果存在elseif-clause,并且其管道测试为True,则执行其语句块并终止语句的执行。否则,如果存在else-clause,则执行其语句块

示例:

$grade = 92
if ($grade -ge 90) { "Grade A" }
elseif ($grade -ge 80) { "Grade B" }
elseif ($grade -ge 70) { "Grade C" }
elseif ($grade -ge 60) { "Grade D" }
else { "Grade F" }

8.4 迭代语句

8.4.1 while 语句

语法:

while-statement:
    while new-lines~opt~ ( new-lines~opt~ while-condition new-lines~opt~ ) statement-block

while-condition:
    new-lines~opt~ pipeline

描述:

控制表达式 while-condition 必须具有 bool 类型或可隐式转换为该类型。循环体由语句块组成,重复执行直到控制表达式测试为False。每次执行循环体之前都会计算控制表达式。

示例:

$i = 1
while ($i -le 5) {                     # loop 5 times
    "{0,1}`t{1,2}" -f $i, ($i*$i)
    ++$i
}

8.4.2 do 语句

语法:

do-statement:
    do statement-block new-lines~opt~ while new-lines~opt~ ( while-condition new-lines~opt~ )
    do statement-block new-lines~opt~ until new-lines~opt~ ( while-condition new-lines~opt~ )

while-condition:
    new-lines~opt~ pipeline

描述:

控制表达式 while-condition 必须具有 bool 类型或可隐式转换为该类型。在 while 形式中,循环体由语句块组成,在控制表达式测试 True 时重复执行。在until 形式中,循环体会重复执行,直到控制表达式测试为True。每次执行循环体后都会计算控制表达式。

示例:

$i = 1
do {
    "{0,1}`t{1,2}" -f $i, ($i * $i)
}
while (++$i -le 5)                 # loop 5 times

$i = 1
do {
    "{0,1}`t{1,2}" -f $i, ($i * $i)
}
until (++$i -gt 5)                 # loop 5 times

8.4.3 for 语句

语法:

for-statement:
    for new-lines~opt~ (
        new-lines~opt~ for-initializer~opt~ statement-terminator
        new-lines~opt~ for-condition~opt~ statement-terminator
        new-lines~opt~ for-iterator~opt~
        new-lines~opt~ ) statement-block

    for new-lines~opt~ (
        new-lines~opt~ for-initializer~opt~ statement-terminator
        new-lines~opt~ for-condition~opt~
        new-lines~opt~ ) statement-block

    for new-lines~opt~ (
        new-lines~opt~ for-initializer~opt~
        new-lines~opt~ ) statement-block

for-initializer:
    pipeline

for-condition:
    pipeline

for-iterator:
    pipeline

描述:

控制表达式for-condition必须具有bool类型或者可以隐式转换为该类型。循环体由语句块组成,在控制表达式测试True时重复执行。每次执行循环体之前都会计算控制表达式。

表达式for-initializer在第一次计算控制表达式之前计算。表达式for-initializer仅评估其副作用;它产生的任何值都会被丢弃并且不会写入管道。

表达式for-iterator在每次执行循环体后进行计算。表达式for-iterator仅评估其副作用;它产生的任何值都会被丢弃并且不会写入管道。

如果省略表达式 for-condition,则控制表达式测试 True。

示例:

for ($i = 5; $i -ge 1; --$i) { # loop 5 times
    "{0,1}`t{1,2}" -f $i, ($i * $i)
}

$i = 5
for (; $i -ge 1; ) { # equivalent behavior
    "{0,1}`t{1,2}" -f $i, ($i * $i)
    --$i
}

8.4.4 foreach 语句

语法:

foreach-statement:
    foreach new-lines~opt~ foreach-parameter~opt~ new-lines~opt~
        ( new-lines~opt~ variable new-lines~opt~ *in* new-lines~opt~ pipeline
        new-lines~opt~ ) statement-block

foreach-parameter:
    -parallel

描述:

循环体由语句块组成,针对pipeline指定的集合中的变量variable指定的每个元素执行。 变量的范围不限于foreach语句。因此,在循环体执行完毕后,它仍保留其最终值。如果 pipeline 指定一个标量(不包括值 $null)而不是集合,则该标量将被视为一个元素的集合。如果pipeline指定值$null,则pipeline被视为零元素的集合。

如果指定了foreach-parameter -parallel,则行为由实现定义。

foreach-parameter -parallel 仅在工作流程中允许使用 (§8.10.2)。

每个 foreach 语句都有自己的枚举器 $foreach (第 2.3.2.2 节、第 4.5.16 节),该枚举器仅在循环执行时存在。

管道生成的对象在语句块开始执行之前收集。但是,使用 ForEach-Object cmdlet,statement-block 在每个对象生成时执行。

示例:

$a = 10, 53, 16, -43
foreach ($e in $a) {
    ...
}
$e # the int value -43

foreach ($e in -5..5) {
    ...
}

foreach ($t in [byte], [int], [long]) {
    $t::MaxValue # get static property
}

foreach ($f in Get-ChildItem *.txt) {
    ...
}

$h1 = @{ FirstName = "James"; LastName = "Anderson"; IDNum = 123 }
foreach ($e in $h1.Keys) {
    "Key is " + $e + ", Value is " + $h1[$e]
}

8.5 流程控制语句

语法:

flow-control-statement:
    break label-expression~opt~
    continue label-expression~opt~
    throw pipeline~opt~
    return pipeline~opt~
    exit pipeline~opt~

label-expression:
    simple-name
    unary-expression

描述:

流控制语句导致控制无条件转移到某个其他位置。

8.5.1break语句

描述:

带有标签表达式的break语句被称为带标签的break语句。没有标签表达式的break语句被称为无标签break语句

在 trap 语句之外,直接在迭代语句 (§8.4) 内的未标记的 break 语句会终止最小封闭迭代语句的执行。直接在 switch 语句(第 8.6 节)中的未标记的 break 语句终止当前 switch 的 switch-condition 的模式匹配。有关在 trap 语句中使用 break 的详细信息,请参阅 (§8.8)。

迭代语句或 switch 语句可以选择紧接一个语句标签(第 8.1.1 节)。这样的语句标签可以用作带标签的break语句的目标,在这种情况下,该语句终止目标的执行附上迭代语句。

标记的中断不需要在任何本地范围内解决;即使跨越脚本和函数调用边界,对匹配标签的搜索也可以继续沿调用堆栈向上进行。如果没有找到匹配的标签,则终止当前命令调用。

label-expression指定的标签名称不需要有常量值。

如果label-expression一元表达式,则会将其转换为字符串。

示例:

$i = 1
while ($true) { # infinite loop
    if ($i * $i -gt 100) {
        break # break out of current while loop
    }
    ++$i
}

$lab = "go_here"
:go_here
for ($i = 1; ; ++$i) {
    if ($i * $i -gt 50) {
        break $lab # use a string value as target
    }
}

:labelA
for ($i = 1; $i -le 2; $i++) {

    :labelB
    for ($j = 1; $j -le 2; $j++) {

        :labelC
        for ($k = 1; $k -le 3; $k++) {
            if (...) { break labelA }
        }
    }
}

8.5.2 continue 语句

描述:

带有标签表达式Continue语句被称为带标签的Continue语句。不带标签表达式的 continue 语句称为无标签 continue 语句。

第 8.8 节讨论了 trap 语句中 continue 的使用。

循环中未标记的 continue 语句会终止当前循环的执行,并将控制权转移到最小封闭迭代语句的右大括号(第 8.4 节)。 switch 中未标记的 continue 语句会终止当前 switch 迭代的执行,并将控制权转移到最小的封闭 switchswitch-条件(§8.6)。

迭代语句或 switch 语句(第 8.6 节)可以选择紧接一个语句标签(第 8.1.1 节)。这样的语句标签可以用作封闭的标记 continue 语句的目标,在这种情况下,该语句终止当前循环或 switch 迭代的执行,并转移控制权到目标封闭迭代或 switch 语句标签。

标记的继续不需要在任何本地范围内解析;即使跨越脚本和函数调用边界,对匹配标签的搜索也可能继续到调用堆栈上。如果没有找到匹配的标签,则终止当前命令调用。

label-expression指定的标签名称不需要有常量值。

如果label-expression一元表达式,则会将其转换为字符串。

示例:

$i = 1
while (...) {
    ...
    if (...) {
        continue # start next iteration of current loop
    }
    ...
}

$lab = "go_here"
:go_here
for (...; ...; ...) {
    if (...) {
        continue $lab # start next iteration of labeled loop
    }
}

:labelA
for ($i = 1; $i -le 2; $i++) {

    :labelB
    for ($j = 1; $j -le 2; $j++) {

        :labelC
        for ($k = 1; $k -le 3; $k++) {
            if (...) { continue labelB }
        }
    }
}

8.5.3 throw 语句

描述:

异常是处理系统或应用程序级错误情况的一种方法。 throw 语句引发异常。 (有关异常处理的讨论,请参见第 8.7 节。)

如果省略了管道并且 throw 语句不在 catch 子句中,则行为是实现定义的。如果存在管道并且 throw 语句位于 catch 子句中,则重新抛出由该 catch 子句捕获的异常在执行与catch-clause关联的任何finally-clause之后。

如果存在管道,则抛出的异常类型由实现定义。

当引发异常时,控制权将转移到可以处理异常的封闭 try 语句中的第一个 catch 子句。最初抛出异常的位置称为抛出点。一旦抛出异常,就会重复执行第 8.7 节中描述的步骤,直到找到与异常匹配的 catch 子句或找不到。

示例:

throw
throw 100
throw "No such record in file"

如果省略pipeline并且抛出语句不是来自catch-clause,则文本“ScriptHalted”将写入管道,并且引发的异常类型为System.Management.Automation.RuntimeException

如果存在管道,则引发的异常将包装在 System.Management.Automation.RuntimeException 类型的对象中,其中包含有关异常的信息,作为 System. Management.Automation.ErrorRecord 对象(可通过 $_ 访问)。

示例 1:throw 123 导致 RuntimeException 类型的异常。在 catch 块中,$_.TargetObject 包含包装在其中的对象,在本例中为值为 123 的 System.Int32

示例 2:throw "xxx" 导致 RuntimeException 类型的异常。在 catch 块中,$_.TargetObject 包含包装在其中的对象,在本例中为值为“xxx”的 System.String

示例 3:throw 10,20 导致 RuntimeException 类型的异常。在 catch 块中,$_.TargetObject 包含包装在内部的对象,在本例中为 System.Object[],这是一个由两个元素组成的无约束数组,其 System.Int32` 值 10 和 20。

8.5.4 返回语句

描述:

return 语句将pipeline 指定的值(如果有)写入管道,并将控制权返回给函数或脚本的调用者。函数或脚本可能有零个或多个 return 语句。

如果执行到达函数的右大括号,则假定不带管道的隐含返回

return 语句是一点“语法糖”,允许程序员像使用其他语言一样表达自己的想法;但是,从函数或脚本返回的值实际上是该函数或脚本写入管道的所有值加上管道指定的任何值。如果仅将标量值写入管道,则其类型为返回值的类型;否则,返回类型是一个不受约束的一维数组,其中包含写入管道的所有值。

示例:

function Get-Factorial ($v) {
    if ($v -eq 1) {
        return 1 # return is not optional
    }

    return $v * (Get-Factorial ($v - 1)) # return is optional
}

Get-Factorial 的调用者返回一个 int

function Test {
    "text1" # "text1" is written to the pipeline
    # ...
    "text2" # "text2" is written to the pipeline
    # ...
    return 123 # 123 is written to the pipeline
}

Test 的调用者返回一个由三个元素组成的无约束一维数组。

8.5.5 退出语句

描述:

exit 语句终止当前脚本并将控制权和退出代码返回到主机环境或调用脚本。如果提供了 pipeline,则如有必要,它指定的值将转换为 int。如果不存在这样的转换,或者省略了 pipeline,则返回 int 值零。

示例:

exit $count # terminate the script with some accumulated count

8.6 switch 语句

语法:

switch-statement:
    switch new-lines~opt~ switch-parameters~opt~ switch-condition switch-body

switch-parameters:
    switch-parameter
    switch-parameters switch-parameter

switch-parameter:
    -regex
    -wildcard
    -exact
    -casesensitive
    -parallel

switch-condition:
    ( new-lines~opt~ pipeline new-lines~opt~ )
    -file new-lines~opt~ switch-filename

switch-filename:
    command-argument
    primary-expression

switch-body:
    new-lines~opt~ { new-lines~opt~ switch-clauses }

switch-clauses:
    switch-clause
    switch-clauses switch-clause

switch-clause:
    switch-clause-condition statement-block statement-terimators~opt~

switch-clause-condition:
    command-argument
    primary-expression

描述:

如果switch-condition指定单个值,则控制权将传递到一个或多个匹配的模式语句块。如果没有模式匹配,则可以采取一些默认操作。

开关必须包含一个或多个开关子句,每个开关子句都以一种模式(非默认开关子句)或关键字default默认 switch 子句)。开关必须包含零个或一个默认开关子句,以及零个或多个非默认开关子句。 Switch 子句可以按任何顺序编写。

多个模式可以具有相同的值。模式不必是文字,并且开关可以具有不同类型的模式。

如果 switch-condition 的值与模式值匹配,则执行该模式的语句块。如果多个模式值与 switch-condition 的值匹配,则按词法顺序执行每个匹配模式的 statement-block,除非其中任何一个 statement-blocks 包含一个 break 语句(第 8.5.1 节)。

如果switch-condition的值不匹配任何模式值,如果存在default switch子句,则执行其statement-block;否则,该开关条件的模式匹配将终止。

开关可以嵌套,每个开关都有自己的一组开关子句。在这种情况下,switch 子句属于当前作用域中最里面的 switch。

在进入每个语句块时,$_ 会被自动分配开关条件的值,该值导致控制转到该语句块 >语句块。 $_ 也可以在该语句块switch-clause-condition中使用。

非字符串的匹配是通过测试相等性来完成的(第 7.8.1 节)。

如果匹配涉及字符串,默认情况下比较不区分大小写。 开关参数 -casesensitive 的存在使得比较区分大小写。

模式可以包含通配符(第 3.15 节),在这种情况下,将执行通配符字符串比较,但前提是存在 switch-parameter -通配符。默认情况下,比较不区分大小写。

模式可以包含正则表达式(第 3.16 节),在这种情况下,将执行正则表达式字符串比较,但前提是存在 switch-parameter -regex 。默认情况下,比较不区分大小写。如果 -regex 存在并且模式匹配,则在 switch-clause statement-block 中定义 $matches > 对于该模式。

开关参数可以缩写;可以使用参数的任何不同的前导部分。例如,-regex-rege-reg-re-r 是等效的。

如果指定了冲突的开关参数,则以词法上的最后一个为准。 -exact 的存在会禁用 -regex-wildcard;但是,它对 -case 没有影响。

如果指定了switch-parameter -parallel,则行为由实现定义。

switch-parameter -parallel 仅在工作流程中允许 (§8.10.2)。

如果模式是脚本块表达式,则计算该块并将结果转换为布尔值(如有必要)。如果结果值为$true,则执行相应的statement-block;否则,就不是。

如果 switch-condition 指定多个值,则使用上述指定单个值的 switch-condition 规则,将开关按词法顺序应用于每个值。每个 switch 语句都有自己的枚举器 $switch (第 2.3.2.2 节,第 4.5.16 节),该枚举器仅在该 switch 执行时存在。

switch 语句可以有标签,并且它可以包含带标签和无标签的break (§8.5.1) 和 continue (§8.5.2) 语句。

如果 switch-condition-file switch-filename,则 switch 不会迭代表达式中的值,而是迭代表达式中的值文件由 switch-filename 指定。文件一次读取一行,每行包含一个值。值中不包含行终止符。

示例:

$s = "ABC def`nghi`tjkl`fmno @#$"
$charCount = 0; $pageCount = 0; $lineCount = 0; $otherCount = 0
for ($i = 0; $i -lt $s.Length; ++$i) {
    ++$charCount
    switch ($s[$i]) {
        "`n" { ++$lineCount }
        "`f" { ++$pageCount }
        "`t" { }
        " " { }
        default { ++$otherCount }
    }
}

switch -wildcard ("abc") {
    a* { "a*, $_" }
    ?B? { "?B? , $_" }
    default { "default, $_" }
}

switch -regex -casesensitive ("abc") {
    ^a* { "a*" }
    ^A* { "A*" }
}

switch (0, 1, 19, 20, 21) {
    { $_ -lt 20 } { "-lt 20" }
    { $_ -band 1 } { "Odd" }
    { $_ -eq 19 } { "-eq 19" }
    default { "default" }
}

8.7 try/finally 语句

语法:

try-statement:
    try statement-block catch-clauses
    try statement-block finally-clause
    try statement-block catch-clauses finally-clause

catch-clauses:
    catch-clause
    catch-clauses catch-clause

catch-clause:
    new-lines~opt~ catch catch-type-list~opt~
    statement-block

catch-type-list:
    new-lines~opt~ type-literal
    catch-type-list new-lines~opt~ , new-lines~opt~

type-literalfinally-clause:
    new-lines~opt~ finally statement-block

描述:

try 语句提供了一种捕获块执行期间发生的异常的机制。 try 语句还提供了指定当控制离开 try 语句时始终执行的代码块的能力。通过 throw 语句引发异常的过程在第 8.5.3 节中描述。

try 块 是与 try 语句关联的语句块catch 块 是与catch 子句 关联的语句块finally 块 是与finally-clause 关联的语句块

没有catch-type-listcatch-clause称为通用catch子句

每个catch-clause都是一个异常处理程序,以及一个catch-clause,其catch-type-list包含引发的异常的类型是匹配的catch子句。通用 catch 子句匹配所有异常类型。

尽管catch-clausesfinally-clauses是可选的,但至少其中之一必须存在。

抛出的异常的处理包括重复评估以下步骤,直到找到与异常匹配的 catch 子句。

  • 在当前作用域中,将检查包含抛出点的每个 try 语句。对于每个 try 语句S,从最里面的 try 语句开始到最外面的 try 语句结束,将评估以下步骤:

    • 如果 Stry 块包含抛出点,并且如果 S 有一个或多个 catch 子句,则按词法顺序检查 catch 子句以为异常找到合适的处理程序。指定异常类型或异常类型的基类型的第一个 catch 子句被视为匹配。通用 catch 子句被视为与任何异常类型匹配。如果找到匹配的catch 子句,则通过将控制转移到该catch 子句的块来完成异常处理。在匹配的 catch 子句中,变量 $_ 包含当前异常的描述。

  • 否则,如果 Stry 块或 catch 块包含抛出点,并且如果 S 具有 finally 块,控制权转移到finally 块。如果finally块抛出另一个异常,则终止当前异常的处理。否则,当控制到达finally块的末尾时,继续处理当前异常。

  • 如果异常处理程序不在当前作用域中,则对封闭作用域重复上述步骤,并使用与调用当前作用域的语句相对应的抛出点。

  • 如果异常处理最终终止所有作用域,表明不存在该异常的处理程序,则该行为是未指定的。

  • 为了防止 try 块中出现无法访问的 catch 子句,catch 子句指定的异常类型不得等于或派生自同一 try 块中较早的 catch 子句中指定的类型。

    当控制离开 try 语句时,finally 块中的语句始终会执行。无论控制权转移是由于正常执行、执行 breakcontinuereturn 语句而发生,情况都是如此。或者是 try 语句抛出异常的结果。

    如果在执行finally 块期间引发异常,则该异常将被抛出到下一个封闭的try 语句。如果正在处理另一个异常,则该异常将丢失。生成异常的过程将在 throw 语句的描述中进一步讨论。

    try 语句可以与 trap 语句共存;详细信息请参见第 8.8 节。

    示例:

    $a = new-object 'int[]' 10
    $i = 20 # out-of-bounds subscript
    
    while ($true) {
        try {
            $a[$i] = 10
            "Assignment completed without error"
            break
        }
    
        catch [IndexOutOfRangeException] {
            "Handling out-of-bounds index, >$_<`n"
            $i = 5
        }
    
        catch {
            "Caught unexpected exception"
        }
    
        finally {
            # ...
        }
    }
    

    每个抛出的异常都会作为 System.Management.Automation.RuntimeException 引发。如果 try 块中存在特定于类型的 catch-clause,则会检查异常的 InnerException 属性以尝试查找匹配项,例如上面的 System.IndexOutOfRangeException 类型。

    8.8 陷阱语句

    语法:

    trap-statement:
        *trap* new-lines~opt~ type-literal~opt~ new-lines~opt~ statement-block
    

    描述:

    带有和不带有 type-literal 的 trap 语句类似于带有和不带有 catch-type- 的 catch 块 (§8.7)分别列出,除了 trap 语句一次只能捕获一种类型。

    多个trap语句可以在同一个语句块中定义,并且它们的定义顺序无关。如果在同一作用域中定义了具有相同type-literal的两个trap语句,则词法上的第一个语句将用于处理匹配类型的异常。

    catch 块不同,trap 语句与异常类型完全匹配;不执行派生类型匹配。

    当异常发生时,如果当前作用域中不存在匹配的 trap 语句,则会在封闭的作用域中搜索匹配的 trap 语句,这可能涉及在调用脚本、函数或过滤器中查找,然后在它的调用者中,依此类推。如果查找最终终止所有范围,表明不存在异常的处理程序,则行为未指定。

    trap 语句的 statement-body 仅执行以处理相应的异常;否则,执行将忽略它。

    如果trapstatement-body正常退出,默认情况下,错误对象会写入错误流,异常被视为已处理,并继续执行该语句紧跟在包含使异常可见的 trap 语句的作用域中的语句之后。异常的原因可能是在包含 trap 语句的命令所调用的命令中。

    如果在 trapstatement-body 中执行的最终语句是 continue (§8.5.2),则禁止将错误对象写入错误流,并且继续执行紧跟在包含使异常可见的陷阱语句的作用域中的语句之后的语句。如果trapstatement-body中执行的最终语句是break(第8.5.1节),则禁止将错误对象写入错误流,并且异常被重新抛出。

    trap 语句中,变量 $_ 包含当前错误的描述。

    考虑这样一种情况:从 try 块内引发的异常没有匹配的 catch 块,但在某个位置存在匹配的 trap 语句。更高的块级别。在执行 try 块的 finally 子句后,即使任何父作用域具有匹配的 catch 块,trap 语句也会获得控制权。如果 trap 语句是在 try 块本身内定义的,并且该 try 块具有匹配的 catch 块, trap 语句获得控制权。

    示例:

    在下面的示例中,写入了错误对象,并继续执行紧随导致陷阱的语句之后的语句;也就是说,“Done”被写入管道。

    $j = 0; $v = 10/$j; "Done"
    trap { $j = 2 }
    

    在下面的示例中,错误对象的写入被抑制,并继续执行紧随导致陷阱的语句之后的语句;也就是说,“Done”被写入管道。

    $j = 0; $v = 10/$j; "Done"
    trap { $j = 2; continue }
    

    在下面的示例中,错误对象的写入被抑制并重新引发异常。

    $j = 0; $v = 10/$j; "Done"
    trap { $j = 2; break }
    

    在以下示例中,陷阱和异常生成语句位于同一范围内。捕获并处理异常后,将继续执行并向管道写入 1。

    &{trap{}; throw '\...'; 1}
    

    在以下示例中,陷阱和异常生成语句位于不同的范围内。捕获并处理异常后,将恢复执行并向管道写入 2(而不是 1)。

    trap{} &{throw '\...'; 1}; 2
    

    8.9 数据声明

    语法:

    data-statement:
        data new-lines~opt~ data-name data-commands-allowed~opt~ statement-block
    
    data-name:
        simple-name
    
    data-commands-allowed:
        new-lines~opt~ -supportedcommand data-commands-list
    
    data-commands-list:
        new-lines~opt~ data-command
        data-commands-list , new-lines~opt~ data-command
    
    data-command:
        command-name-expr
    

    描述:

    数据语句创建一个数据部分,将该部分的数据与代码分开。这种分离支持诸如单独的文本字符串资源文件之类的功能,例如错误消息和帮助字符串。它还可以更轻松地隔离、定位和处理将翻译成不同语言的字符串,从而帮助支持国际化。

    脚本或函数可以有零个或多个数据部分。

    数据部分的语句块仅限于包含以下 PowerShell 功能:

    • -match 之外的所有运算符
    • if 语句
    • 以下自动变量:$PsCulture$PsUICulture$true$false$空
    • 评论
    • 管道
    • 语句之间用分号分隔 (;)
    • 文字
    • 调用 ConvertFrom-StringData cmdlet
    • 通过 supportedcommand 参数标识的任何其他 cmdlet

    如果使用 ConvertFrom-StringData cmdlet,则可以使用任何形式的字符串文字来表示键/值对。但是,expandable-string-literal 和expandable-here-string-literal 不得包含任何变量替换或子表达式扩展。

    示例:

    SupportedCommand 参数指示给定的 cmdlet 或函数仅生成数据。例如,以下数据部分包含用户编写的 cmdlet ConvertTo-XML,它格式化 XML 文件中的数据:

    data -supportedCommand ConvertTo-XML {
        Format-XML -strings string1, string2, string3
    }
    

    考虑以下示例,其中数据部分包含一个 ConvertFrom-StringData 命令,该命令将字符串转换为哈希表,其值分配给 $messages

    $messages = data {
        ConvertFrom-StringData -stringdata @'
        Greeting = Hello
        Yes = yes
        No = no
    '@
    }
    

    分别使用 $messages.Greeting$messages.Yes$messages.No 访问哈希表的键和值。

    现在,可以将其另存为英语资源。德语和西班牙语资源可以在单独的文件中创建,并具有以下数据部分:

    $messages = data {
        ConvertFrom-StringData -stringdata @"
        Greeting = Guten Tag
        Yes = ja
        No = nein
    "@
    }
    
    $messagesS = data {
        ConvertFrom-StringData -stringdata @"
        Greeting = Buenos días
        Yes = sí
        No = no
    "@
    }
    

    如果存在dataname,它会命名要存储数据语句值的变量(不使用前导$)。具体来说,$name=data { ... } 相当于 data name { ... }

    8.10 函数定义

    语法:

    function-statement:
        function new-lines~opt~ function-name function-parameter-declaration~opt~ { script-block }
        filter new-lines~opt~ function-name function-parameter-declaration~opt~ { script-block }
        workflow new-lines~opt~ function-name function-parameter-declaration~opt~ { script-block }
    
    function-name:
        command-argument
    
    command-argument:
        command-name-expr
    
    function-parameter-declaration:
        new-lines~opt~ ( parameter-list new-lines~opt~ )
    
    parameter-list:
        script-parameter
        parameter-list new-lines~opt~ , script-parameter
    
    script-parameter:
        new-lines~opt~ attribute-list~opt~ new-lines~opt~ variable script-parameter-default~opt~
    
    script-block:
        param-block~opt~ statement-terminators~opt~ script-block-body~opt~
    
    param-block:
        new-lines~opt~ attribute-list~opt~ new-lines~opt~ param new-lines~opt~
            ( parameter-list~opt~ new-lines~opt~ )
    
    parameter-list:
        script-parameter
        parameter-list new-lines~opt~ , script-parameter
    
    script-parameter-default:
        new-lines~opt~ = new-lines~opt~ expression
    
    script-block-body:
        named-block-list
        statement-list
    
    named-block-list:
        named-block
        named-block-list named-block
    
    named-block:
        block-name statement-block statement-terminators~opt~
    
    block-name: one of
        dynamicparam   begin   process   end
    

    描述:

    函数定义指定所定义的函数、过滤器或工作流的名称及其参数的名称(如果有)。它还包含零个或多个为实现该函数目的而执行的语句。

    每个函数都是 System.Management.Automation.FunctionInfo 类的一个实例。

    8.10.1 过滤功能

    普通函数在管道中运行一次并通过 $input 访问输入集合,而 filter 是一种特殊类型的函数,它对输入集合中的每个对象执行一次。当前正在处理的对象可通过变量 $_ 获得。

    没有命名块(§8.10.7)的过滤器相当于带有进程块的函数,但没有任何开始块或结束块。

    考虑以下过滤器函数定义和调用:

    filter Get-Square2 { # make the function a filter
        $_ * $_ # access current object from the collection
    }
    
    -3..3 | Get-Square2 # collection has 7 elements
    6, 10, -3 | Get-Square2 # collection has 3 elements
    

    每个过滤器都是 System.Management.Automation.FilterInfo 类的一个实例(第 4.5.11 节)。

    8.10.2 工作流程功能

    工作流函数就像具有实现定义语义的普通函数。工作流函数被转换为一系列 Windows Workflow Foundation 活动并在 Windows Workflow Foundation 引擎中执行。

    8.10.3 参数处理

    考虑名为 Get-Power 的函数的以下定义:

    function Get-Power ([long]$base, [int]$exponent) {
        $result = 1
        for ($i = 1; $i -le $exponent; ++$i) {
            $result *= $base
        }
        return $result
    }
    

    该函数有两个参数:$base$exponent。它还包含一组语句,对于非负指数值,计算 $base^$exponent^ 并将结果返回给 Get-Power 的调用者。

    当脚本、函数或过滤器开始执行时,每个参数都会初始化为其相应参数的值。如果没有相应的参数并且提供了默认值(第 8.10.4 节),则使用该值;否则,使用值$null。因此,每个参数都是一个新变量,就像它是在脚本块开头通过赋值来初始化的一样。

    如果脚本参数包含类型约束(例如上面的[long][int]),则相应参数的值为如有必要,转换为该类型;否则,不会发生转换。

    当脚本、函数或过滤器开始执行时,变量 $args 在其中定义为不受约束的一维数组,其中包含按词法顺序不受名称或位置约束的所有参数。

    考虑以下函数定义和调用:

    function F ($a, $b, $c, $d) { ... }
    
    F -b 3 -d 5 2 4       # $a is 2, $b is 3, $c is 4, $d is 5, $args Length 0
    F -a 2 -d 3 4 5       # $a is 2, $b is 4, $c is 5, $d is 3, $args Length 0
    F 2 3 4 5 -c 7 -a 1   # $a is 1, $b is 2, $c is 7, $d is 3, $args Length 2
    

    有关参数绑定的更多信息,请参阅第 8.14 节。

    8.10.4 参数初始值设定项

    参数 p 的声明可能包含一个初始值设定项,在这种情况下,该初始值设定项的值用于初始化 p,前提是 p 未绑定到通话中的任何参数。

    考虑以下函数定义和调用:

    function Find-Str ([string]$str, [int]$start_pos = 0) { ... }
    
    Find-Str "abcabc" # 2nd argument omitted, 0 used for $start_pos
    Find-Str "abcabc" 2 # 2nd argument present, so it is used for $start_pos
    

    8.10.5 [switch]类型约束

    当传递开关参数时,命令中对应的参数必须受到类型开关的约束。类型开关有两个值:True 和 False。

    考虑以下函数定义和调用:

    function Process ([switch]$trace, $p1, $p2) { ... }
    
    Process 10 20                # $trace is False, $p1 is 10, $p2 is 20
    Process 10 -trace 20         # $trace is True, $p1 is 10, $p2 is 20
    Process 10 20 -trace         # $trace is True, $p1 is 10, $p2 is 20
    Process 10 20 -trace:$false  # $trace is False, $p1 is 10, $p2 is 20
    Process 10 20 -trace:$true   # $trace is True, $p1 is 10, $p2 is 20
    

    8.10.6 管道和函数

    当在管道中使用脚本、函数或过滤器时,值的集合将传递到该脚本或函数。脚本、函数或过滤器通过枚举器 $input(第 2.3.2.2 节、第 4.5.16 节)访问该集合,该枚举器是在该脚本、函数或过滤器的入口处定义的。

    考虑以下函数定义和调用:

    function Get-Square1 {
        foreach ($i in $input) {   # iterate over the collection
            $i * $i
        }
    }
    
    -3..3 | Get-Square1            # collection has 7 elements
    6, 10, -3 | Get-Square1        # collection has 3 elements
    

    8.10.7 命名块

    脚本块中的语句可以属于一个大的未命名块,也可以分布到一个或多个命名块中。命名块允许对来自管道的集合进行自定义处理;命名块可以按任何顺序定义。

    begin 块中的语句(即用关键字 begin 标记的语句)在传递第一个管道对象之前执行一次。

    对于交付的每个管道对象,都会执行进程块中的语句(即标有关键字 process 的语句)。 ($_ 提供对来自管道的输入集合中正在处理的当前对象的访问。)这意味着,如果通过管道发送零个元素的集合,则不会执行处理块全部。但是,如果在管道上下文之外调用脚本或函数,则该块仅执行一次,并且 $_ 设置为 $null,因为没有当前集合目的。

    end 块 中的语句(即用关键字 end 标记的语句)在最后一个管道对象交付后执行一次。

    8.10.8 动态参数块

    到目前为止,§8.10 的小节涉及静态参数,它们被定义为源代码的一部分。还可以通过dynamicparam块定义动态参数,这是另一种形式的命名块(§8.10.7),用关键字dynamicparam。这种机制的大部分是由实现定义的。

    动态参数是 cmdlet、函数、筛选器或脚本的参数,仅在特定条件下可用。其中一种情况是 Set-Item cmdlet 的 Encoding 参数。

    在语句块中,使用 if 语句指定参数在函数中可用的条件。使用 New-Object cmdlet 创建实现定义类型的对象来表示参数,并指定其名称。另外,使用 New-Object 创建不同实现定义类型的对象来表示参数的实现定义属性。

    以下示例显示了一个具有名为“名称”和“路径”的标准参数以及一个名为 DP1 的可选动态参数的函数。 DP1 参数位于 PSet1 参数集中,类型为 Int32。仅当 Path 参数的值包含“HKLM:”时,Sample 函数中的 DP1 参数才可用,表明该参数正在 HKEY_LOCAL_MACHINE 注册表驱动器中使用。

    function Sample {
        Param ([String]$Name, [String]$Path)
        dynamicparam {
            if ($path -match "*HKLM*:") {
                $dynParam1 = New-Object System.Management.Automation.RuntimeDefinedParameter("dp1", [Int32], $attributeCollection)
    
                $attributes = New-Object System.Management.Automation.ParameterAttribute
                $attributes.ParameterSetName = 'pset1'
                $attributes.Mandatory = $false
    
                $attributeCollection = New-Object -Type System.Collections.ObjectModel.Collection``1[System.Attribute]
                $attributeCollection.Add($attributes)
    
                $paramDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
                $paramDictionary.Add("dp1", $dynParam1)
                return $paramDictionary
            }
        }
    }
    

    用于创建表示动态参数的对象的类型是 System.Management.Automation.RuntimeDefinedParameter。

    用于创建对象来表示参数属性的类型是 System.Management.Automation.ParameterAttribute。

    参数的实现定义属性包括强制位置ValueFromPipeline

    8.10.9 参数块

    param-block提供了另一种声明参数的方式。例如,以下几组参数声明是等效的:

    function FindStr1 ([string]$str, [int]$start_pos = 0) { ... }
    function FindStr2 {
        param ([string]$str, [int]$start_pos = 0) ...
    }
    

    param-block 允许在param-block 上使用属性列表,而函数参数声明 则不允许不是。

    脚本可以有参数块,但没有函数参数声明。函数或过滤器定义可以具有函数参数声明参数块,但不能同时具有两者。

    考虑以下示例:

    param ( [Parameter(Mandatory = $true, ValueFromPipeline=$true)]
            [string[]] $ComputerName )
    

    第一个参数 $ComputerName 的类型为 string[],它是必需的,它从管道获取输入。

    有关 Parameter 属性的讨论和更多示例,请参阅第 12.3.7 节。

    8.11 并行语句

    语法:

    parallel-statement:
        *parallel* statement-block
    

    并行语句包含零个或多个以实现定义的方式执行的语句。

    并行语句仅允许在工作流程中使用(第 8.10.2 节)。

    8.12 顺序语句

    语法:

    sequence-statement:
        *sequence* statement-block
    

    序列语句包含零个或多个以实现定义的方式执行的语句。

    序列语句仅允许在工作流程中使用(第 8.10.2 节)。

    8.13 内联脚本语句

    语法:

    inlinescript-statement:
        inlinescript statement-block
    

    inlinescript 语句包含零个或多个以实现定义的方式执行的语句。

    仅允许在工作流程中使用内联脚本语句(第 8.10.2 节)。

    8.14 参数绑定

    当调用脚本、函数、过滤器或 cmdlet 时,每个参数都可以按位置绑定到相应的参数,第一个参数的位置为零。

    考虑以下名为 Get-Power 的函数的定义片段及其调用:

    function Get-Power ([long]$base, [int]$exponent) { ... }
    
    Get-Power 5 3       # argument 5 is bound to parameter $base in position 0
                        # argument 3 is bound to parameter $exponent in position 1
                        # no conversion is needed, and the result is 5 to the power 3
    
    Get-Power 4.7 3.2   # double argument 4.7 is rounded to int 5, double argument
                        # 3.2 is rounded to int 3, and result is 5 to the power 3
    
    Get-Power 5         # $exponent has value $null, which is converted to int 0
    
    Get-Power           # both parameters have value $null, which is converted to int 0
    

    当调用脚本、函数、过滤器或 cmdlet 时,可以按名称将参数绑定到相应的形参。这是通过使用带有参数的参数来完成的,参数是参数的名称,带有前导破折号 (-),后跟该参数的关联值。使用的参数名称可以具有任何不区分大小写的拼写,并且可以使用唯一指定相应参数的任何前缀。选择参数名称时,避免使用公共参数的名称。

    考虑以下对函数 Get-Power 的调用:

    Get-Power -base 5 -exponent 3   # -base designates $base, so 5 is
                                    # bound to that, exponent designates
                                    # $exponent, so 3 is bound to that
    
    Get-Power -Exp 3 -BAs 5         # $base takes on 5 and $exponent takes on 3
    
    Get-Power -e 3 -b 5             # $base takes on 5 and $exponent takes on 3
    

    另一方面,调用以下函数

    function Get-Hypot ([double]$side1, [double]$side2) {
        return [Math]::Sqrt($side1 * $side1 + $side2 * $side2)
    }
    

    必须使用参数 -side1-side2,因为没有唯一指定参数的前缀。

    同一参数名称无论有或没有不同的关联参数值都不能多次使用。

    参数可以具有属性(§12)。有关各个属性的信息,请参阅第 12.3 节中的部分。有关参数集的信息,请参阅§12.3.7。

    脚本、函数、过滤器或 cmdlet 可以通过调用命令行、管道或两者接收参数。以下是按顺序解决参数绑定的步骤:

    1. 绑定所有命名参数,然后
    2. 绑定位置参数,然后
    3. 通过精确匹配的值(§12.3.7)从管道绑定,然后
    4. 通过转换按值(§12.3.7)从管道绑定,然后
    5. 通过完全匹配的名称(§12.3.7)从管道绑定,然后
    6. 通过名称(§12.3.7)通过转换从管道绑定

    其中几个步骤涉及转换,如第 6 节中所述。但是,绑定中使用的转换集与语言转换中使用的转换集并不完全相同。具体来说,

    • 虽然值 $null 可以转换为 bool,但 $null 不能绑定到 bool
    • 当值 $null 传递给 cmdlet 的 switch 参数时,它被视为传递了 $true。但是,当传递给函数的 switch 参数时,它被视为传递了 $false
    • bool 或 switch 类型的参数只能绑定到数字或 bool 参数。
    • 如果参数类型不是集合,但参数是某种集合,则不会尝试转换,除非参数类型是 object 或 PsObject。 (此限制的要点是不允许将集合转换为字符串参数。)否则,将尝试通常的转换。

    如果参数类型为 IListICollection

    您需要 登录账户 后才能发表评论

    取消回复欢迎 发表评论:

    关灯