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

[玩转系统] 关于 if 语句你想知道的一切

作者:精品下载站 日期:2024-12-14 03:04:20 浏览:14 分类:玩电脑

关于 if 语句你想知道的一切


与许多其他语言一样,PowerShell 具有用于在脚本中条件执行代码的语句。这些语句之一是 If 语句。今天我们将深入探讨 PowerShell 中最基本的命令之一。

笔记

本文的原始版本出现在@KevinMarquette 撰写的博客上。 PowerShell 团队感谢 Kevin 与我们分享这些内容。请查看他的博客:PowerShellExplained.com。

有条件执行

您的脚本通常需要做出决策并根据这些决策执行不同的逻辑。这就是我所说的条件执行的意思。您需要评估一个语句或值,然后根据该评估执行不同的代码部分。这正是 if 语句的作用。

if 语句

以下是 if 语句的基本示例:

$condition = $true
if ( $condition )
{
    Write-Output "The condition was true"
}

if 语句所做的第一件事是计算括号中的表达式。如果其计算结果为 $true,则执行大括号中的 scriptblock。如果值为 $false,那么它将跳过该脚本块。

在前面的示例中,if 语句只是评估 $condition 变量。它是 $true 并且会在脚本块内执行 Write-Output 命令。

在某些语言中,您可以在 if 语句后面放置一行代码,然后它就会被执行。 PowerShell 中的情况并非如此。您必须提供完整的带有大括号的 scriptblock 才能正常工作。

比较运算符

if 语句最常见的用途是将两个项目相互比较。 PowerShell 有针对不同比较场景的特殊运算符。使用比较运算符时,会将左侧的值与右侧的值进行比较。

-eq 表示平等

-eq 在两个值之间进行相等性检查,以确保它们彼此相等。

$value = Get-MysteryValue
if ( 5 -eq $value )
{
    # do something
}

在此示例中,我采用已知值 5 并将其与我的 $value 进行比较,看看它们是否匹配。

一种可能的用例是在对某个值采取操作之前检查该值的状态。您可以获取一个服务并在调用 Restart-Service 之前检查其状态是否正在运行。

在 C# 等其他语言中,通常使用 == 来表示相等(例如:5 == $value),但这不适用于 PowerShell。人们犯的另一个常见错误是使用等号(例如:5=$value),该等号是为变量赋值而保留的。通过将你的已知值放在左边,会让这个错误更难犯。

该运算符(和其他运算符)有一些变化。

  • -eq 不区分大小写的相等
  • -ieq 不区分大小写的相等
  • -ceq 区分大小写的相等

-ne 不等于

许多运算符都有一个相关的运算符来检查相反的结果。 -ne 验证这些值是否彼此相等。

if ( 5 -ne $value )
{
    # do something
}

使用此选项可确保仅当值不是 5 时才执行该操作。一个很好的用例是在尝试启动服务之前检查服务是否处于运行状态。

变化:

  • -ne 不区分大小写不等于
  • -ine 不区分大小写不等于
  • -cne 区分大小写不等于

这些是 -eq 的逆变体。当我列出其他运算符的变体时,我会将这些类型分组在一起。

-gt -ge -lt -le 大于或小于

在检查一个值是否大于或小于另一个值时使用这些运算符。 -gt -ge -lt -le 代表 GreaterThan、GreaterThanOrEqual、LessThan 和 LessThanOrEqual。

if ( $value -gt 5 )
{
    # do something
}

变化:

  • -gt 大于
  • -igt 大于,不区分大小写
  • -cgt 大于,区分大小写
  • -ge 大于或等于
  • -ige 大于等于,不区分大小写
  • -cge 大于等于,区分大小写
  • -lt 小于
  • -ilt 小于,不区分大小写
  • -clt 小于,区分大小写
  • -le 小于或等于
  • -ile 小于等于,不区分大小写
  • -cle 小于等于,区分大小写

我不知道为什么您要对这些运算符使用区分大小写和不敏感的选项。

-类似通配符匹配

PowerShell 有自己的基于通配符的模式匹配语法,您可以将其与 -like 运算符一起使用。这些通配符模式相当基本。

  • ? 匹配任何单个字符
  • * 匹配任意数量的字符
$value = 'S-ATX-SQL01'
if ( $value -like 'S-*-SQL??')
{
    # do something
}

需要指出的是,该模式与整个字符串相匹配。如果需要匹配字符串中间的某些内容,则需要将 * 放在字符串的两端。

$value = 'S-ATX-SQL02'
if ( $value -like '*SQL*')
{
    # do something
}

变化:

  • -like 不区分大小写的通配符
  • -ilike 不区分大小写的通配符
  • -clike 区分大小写的通配符
  • -notlike 不区分大小写的通配符不匹配
  • -inotlike 不区分大小写的通配符不匹配
  • -cnotlike 区分大小写的通配符不匹配

- 匹配正则表达式

-match 运算符允许您检查字符串是否基于正则表达式的匹配。当通配符模式对您来说不够灵活时使用此选项。

$value = 'S-ATX-SQL01'
if ( $value -match 'S-\w\w\w-SQL\d\d')
{
    # do something
}

默认情况下,正则表达式模式匹配字符串中的任何位置。因此,您可以指定要匹配的子字符串,如下所示:

$value = 'S-ATX-SQL01'
if ( $value -match 'SQL')
{
    # do something
}

正则表达式本身就是一门复杂的语言,值得研究。我在另一篇文章中详细讨论了 -match 以及使用正则表达式的多种方法。

变化:

  • -match 不区分大小写的正则表达式
  • -imatch 不区分大小写的正则表达式
  • -cmatch 区分大小写的正则表达式
  • -notmatch 不区分大小写的正则表达式不匹配
  • -inotmatch 不区分大小写的正则表达式不匹配
  • -cnotmatch 区分大小写的正则表达式不匹配

- 属于类型

您可以使用 -is 运算符检查值的类型。

if ( $value -is [string] )
{
    # do something
}

如果您正在使用类或通过管道接受各种对象,则可以使用它。您可以将服务或服务名称作为输入。然后检查您是否有服务,如果只有名称,则获取该服务。

if ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
    $Service = Get-Service -Name $Service
}

变化:

  • -is 类型
  • -isnot 不是类型

收集运营商

当您将前面的运算符与单个值一起使用时,结果为 $true$false。使用集合时,处理方式略有不同。集合中的每个项目都会被求值,并且运算符会返回每个求值为 $true 的值。

PS> 1,2,3,4 -eq 3
3

这在 if 语句中仍然可以正常工作。所以你的操作符返回一个值,那么整个语句就是$true

$array = 1..6
if ( $array -gt 3 )
{
    # do something
}

这里的细节中隐藏着一个小陷阱,我需要指出。当以这种方式使用 -ne 运算符时,很容易错误地向后查看逻辑。如果集合中的任何项目与您的值不匹配,则对集合使用 -ne 会返回 $true

PS> 1,2,3 -ne 4
1
2
3

这可能看起来是一个聪明的技巧,但我们有运算符 -contains-in 可以更有效地处理这个问题。 -notcontains 可以满足您的期望。

-contains

-contains 运算符检查集合中的值。一旦找到匹配,它就会返回$true

$array = 1..6
if ( $array -contains 3 )
{
    # do something
}

这是查看集合是否包含您的价值的首选方法。使用 Where-Object (或 -eq)每次都会遍历整个列表,并且速度明显慢一些。

变化:

  • -contains 不区分大小写的匹配
  • -icontains 不区分大小写的匹配
  • -ccontains 区分大小写的匹配
  • -notcontains 不区分大小写不匹配
  • -inotcontains 不区分大小写不匹配
  • -cnotcontains 区分大小写不匹配

-in

-in 运算符与 -contains 运算符类似,只是集合位于右侧。

$array = 1..6
if ( 3 -in $array )
{
    # do something
}

变化:

  • -in 不区分大小写的匹配
  • -iin 不区分大小写的匹配
  • -cin 区分大小写的匹配
  • -notin 不区分大小写不匹配
  • -inotin 不区分大小写,不匹配
  • -cnotin 区分大小写不匹配

逻辑运算符

逻辑运算符用于反转或组合其他表达式。

-not

-not 运算符将表达式从 $false 翻转为 $true 或从 $true 翻转为 $假。下面是一个示例,我们希望在 Test-Path$false 时执行操作。

if ( -not ( Test-Path -Path $path ) )

我们讨论的大多数运算符都有一个变体,您不需要使用 -not 运算符。但有时它还是有用的。

!操作员

您可以使用 ! 作为 -not 的别名。

if ( -not $value ){}
if ( !$value ){}

您可能会发现来自其他语言(例如 C#)的人更多地使用 !。我更喜欢把它打印出来,因为我发现快速查看脚本时很难看清。

-and

您可以使用 -and 运算符组合表达式。当您这样做时,两边都需要为 $true 才能使整个表达式为 $true

if ( ($age -gt 13) -and ($age -lt 55) )

在此示例中,左侧的 $age 必须为 13 岁或以上,右侧的$age 必须小于 55 岁。我添加了额外的括号以使其在该示例中更清晰,但只要表达式简单,它们就是可选的。这是没有它们的相同示例。

if ( $age -gt 13 -and $age -lt 55 )

评估从左到右进行。如果第一项的计算结果为 $false,则它会提前退出并且不会执行正确的比较。当您需要在使用某个值之前确保该值存在时,这非常方便。例如,如果您为其提供 $null 路径,则 Test-Path 会引发错误。

if ( $null -ne $path -and (Test-Path -Path $path) )

-or

-or 允许您指定两个表达式,如果其中一个为 $true,则返回 $true

if ( $age -le 13 -or $age -ge 55 )

就像 -and 运算符一样,计算从左到右进行。但如果第一部分为 $true,则整个语句为 $true 并且不会处理表达式的其余部分。

另请注意这些运算符的语法如何工作。您需要两个单独的表达式。我见过用户尝试执行类似 $value -eq 5 -or 6 的操作,但没有意识到自己的错误。

-xor 异或

这个有点不寻常。 -xor 只允许一个表达式计算结果为 $true。因此,如果两项均为 $false 或两项均为 $true,则整个表达式为 $false。另一种看待这个问题的方法是,只有当表达式的结果不同时,表达式才为 $true

很少有人会使用这个逻辑运算符,我也想不出一个很好的例子来说明为什么我会使用它。

按位运算符

位运算符对值中的位执行计算并生成新值作为结果。按位运算符的教学超出了本文的范围,但这里是它们的列表。

  • -band 二进制 AND
  • -bor 二进制或
  • -bxor 二进制异或
  • -bnot 二进制 NOT
  • -shl 左移
  • -shr 右移

PowerShell 表达式

我们可以在条件语句中使用普通的 PowerShell。

if ( Test-Path -Path $Path )

Test-Path 执行时返回 $true$false。这也适用于返回其他值的命令。

if ( Get-Process Notepad* )

如果有返回的进程,则计算结果为 $true;如果没有,则计算结果为 $false。使用管道表达式或其他 PowerShell 语句是完全有效的,如下所示:

if ( Get-Process | Where Name -eq Notepad )

这些表达式可以使用 -and-or 运算符相互组合,但您可能必须使用括号将它们分解为子表达式。

if ( (Get-Process) -and (Get-Service) )

检查 $null

如果 if 语句中没有结果或 $null 值,则计算结果为 $false。专门检查 $null 时,最佳做法是将 $null 放在左侧。

if ( $null -eq $value )

在 PowerShell 中处理 $null 值时存在很多细微差别。如果您有兴趣深入了解,我有一篇文章介绍了您想了解的有关 $null 的所有信息。

条件内的变量赋值

我差点忘了添加这个,直到 Prasoon Karunan V 提醒我。

if ($process=Get-Process notepad -ErrorAction ignore) {$process} else {$false}

通常,当您为变量分配值时,该值不会传递到管道或控制台。当您在子表达式中进行变量赋值时,它确实会传递到管道。

PS> $first = 1
PS> ($second = 2)
2

看看 $first 赋值没有输出而 $second 赋值有输出吗?当在 if 语句中完成赋值时,它的执行方式与上面的 $second 赋值类似。这是一个关于如何使用它的干净示例:

if ( $process = Get-Process Notepad* )
{
    $process | Stop-Process
}

如果为 $process 分配了一个值,则该语句为 $true 并且 $process 将停止。

确保不要将其与 -eq 混淆,因为这不是相等检查。这是一个比较晦涩的功能,大多数人没有意识到它是这样工作的。

来自脚本块的变量赋值

您还可以使用 if 语句脚本块为变量赋值。

$discount = if ( $age -ge 55 )
{
    Get-SeniorDiscount
}
elseif ( $age -le 13 )
{
    Get-ChildDiscount
}
else
{
    0.00
}

每个脚本块都将命令的结果或值写入作为输出。我们可以将 if 语句的结果分配给 $discount 变量。该示例可以轻松地将这些值直接分配给每个脚本块中的 $discount 变量。我不能说我经常将其与 if 语句一起使用,但我确实有一个最近使用它的示例。

备用执行路径

if 语句不仅允许您在语句为 $true 时指定操作,还可以在语句为 $false 时指定操作。这就是 else 语句发挥作用的地方。

别的

使用时,else 语句始终是 if 语句的最后一部分。

if ( Test-Path -Path $Path -PathType Leaf )
{
    Move-Item -Path $Path -Destination $archivePath
}
else
{
    Write-Warning "$path doesn't exist or isn't a file."
}

在此示例中,我们检查 $path 以确保它是一个文件。如果我们找到该文件,我们就会移动它。如果没有,我们会写一个警告。这种类型的分支逻辑很常见。

嵌套 if

ifelse 语句采用脚本块,因此我们可以在其中放置任何 PowerShell 命令,包括另一个 if 语句。这允许您使用更复杂的逻辑。

if ( Test-Path -Path $Path -PathType Leaf )
{
    Move-Item -Path $Path -Destination $archivePath
}
else
{
    if ( Test-Path -Path $Path )
    {
        Write-Warning "A file was required but a directory was found instead."
    }
    else
    {
        Write-Warning "$path could not be found."
    }
}

在此示例中,我们首先测试快乐路径,然后对其采取行动。如果失败,我们会进行另一次检查并向用户提供更详细的信息。

否则

我们不仅限于一次条件检查。我们可以将 ifelse 语句链接在一起,而不是使用 elseif 语句嵌套它们。

if ( Test-Path -Path $Path -PathType Leaf )
{
    Move-Item -Path $Path -Destination $archivePath
}
elseif ( Test-Path -Path $Path )
{
    Write-Warning "A file was required but a directory was found instead."
}
else
{
    Write-Warning "$path could not be found."
}

执行是从上到下进行的。首先评估顶部的 if 语句。如果是 $false,那么它将向下移动到列表中的下一个 elseifelse。最后一个 else 是在其他都没有返回 $true 时执行的默认操作。

转变

说到这里,我需要提一下switch语句。它提供了一种用于对值进行多次比较的替代语法。使用开关,您可以指定一个表达式,然后将该结果与几个不同的值进行比较。如果这些值之一匹配,则执行匹配的代码块。看一下这个例子:

$itemType = 'Role'
switch ( $itemType )
{
    'Component'
    {
        'is a component'
    }
    'Role'
    {
        'is a role'
    }
    'Location'
    {
        'is a location'
    }
}

可以匹配 $itemType 的三个可能值。在本例中,它与Role 匹配。我使用一个简单的示例只是为了让您了解 switch 运算符。我在另一篇文章中详细讨论了您想了解的有关 switch 语句的所有内容。

数组内联

我有一个名为 Invoke-SnowSql 的函数,它启动带有多个命令行参数的可执行文件。这是该函数的一个片段,我在其中构建了参数数组。

$snowSqlParam = @(
    '--accountname', $Endpoint
    '--username', $Credential.UserName
    '--option', 'exit_on_error=true'
    '--option', 'output_format=csv'
    '--option', 'friendly=false'
    '--option', 'timing=false'
    if ($Debug)
    {
        '--option', 'log_level=DEBUG'
    }
    if ($Path)
    {
        '--filename', $Path
    }
    else
    {
        '--query', $singleLineQuery
    }
)

$Debug$Path 变量是最终用户提供的函数参数。我在数组的初始化过程中内联评估它们。如果 $Debug 为 true,则这些值将落入 $snowSqlParam 的正确位置。对于 $Path 变量也是如此。

简化复杂操作

您不可避免地会遇到需要检查太多比较的情况,并且您的 If 语句会滚动到屏幕右侧之外。

$user = Get-ADUser -Identity $UserName
if ( $null -ne $user -and $user.Department -eq 'Finance' -and $user.Title -match 'Senior' -and $user.HomeDrive -notlike '\server\*' )
{
    # Do Something
}

它们可能难以阅读,并且使您更容易犯错误。对此我们可以做一些事情。

线路延续

PowerShell 中有一些运算符可让您将命令包装到下一行。如果您想将表达式分成多行,则逻辑运算符 -and-or 是很好的运算符。

if ($null -ne $user -and
    $user.Department -eq 'Finance' -and
    $user.Title -match 'Senior' -and
    $user.HomeDrive -notlike '\server\*'
)
{
    # Do Something
}

那里还有很多事情要做,但是将每个部分放在自己的线上会产生很大的不同。当我得到两个以上的比较或者我必须滚动到右侧才能阅读任何逻辑时,我通常会使用它。

预计算结果

我们可以将该语句从 if 语句中取出,只检查结果。

$needsSecureHomeDrive = $null -ne $user -and
    $user.Department -eq 'Finance' -and
    $user.Title -match 'Senior' -and
    $user.HomeDrive -notlike '\server\*'

if ( $needsSecureHomeDrive )
{
    # Do Something
}

这感觉比前面的例子干净多了。您还有机会使用变量名称来解释您真正要检查的内容。这也是自记录代码的示例,可以节省不必要的注释。

多个 if 语句

我们可以将其分解为多个语句并一次检查一个。在这种情况下,我们使用标志或跟踪变量来组合结果。

$skipUser = $false

if( $null -eq $user )
{
    $skipUser = $true
}

if( $user.Department -ne 'Finance' )
{
    Write-Verbose "isn't in Finance department"
    $skipUser = $true
}

if( $user.Title -match 'Senior' )
{
    Write-Verbose "Doesn't have Senior title"
    $skipUser = $true
}

if( $user.HomeDrive -like '\server\*' )
{
    Write-Verbose "Home drive already configured"
    $skipUser = $true
}

if ( -not $skipUser )
{
    # do something
}

我确实必须反转逻辑才能使标志逻辑正常工作。每个评估都是一个单独的 if 语句。这样做的好处是,当你调试时,你可以准确地知道逻辑在做什么。我能够同时添加更详细的内容。

明显的缺点是需要编写更多代码。该代码看起来更复杂,因为它需要一行逻辑并将其分解为 25 行或更多行。

使用函数

我们还可以将所有验证逻辑移至一个函数中。看看这一眼看上去多干净。

if ( Test-SecureDriveConfiguration -ADUser $user )
{
    # do something
}

您仍然需要创建函数来进行验证,但这使得此代码更易于使用。它使这段代码更容易测试。在测试中,您可以模拟对 Test-ADDriveConfiguration 的调用,并且只需对此函数进行两次测试。一种返回 $true,另一种返回 $false。测试另一个函数更简单,因为它很小。

该函数的主体仍然可以是我们开始使用的单行代码或我们在上一节中使用的分解逻辑。这对于这两种情况都很有效,并且允许您稍后轻松更改该实现。

错误处理

if 语句的一个重要用途是在遇到错误之前检查错误条件。一个很好的例子是在尝试创建文件夹之前检查该文件夹是否已存在。

if ( -not (Test-Path -Path $folder) )
{
    New-Item -Type Directory -Path $folder
}

我想说的是,如果你期望异常发生,那么它就不是真正的异常。因此,请检查您的价值观并尽可能验证您的条件。

如果您想更深入地了解实际的异常处理,我有一篇文章介绍了您想了解的有关异常的所有内容。

最后的话

if 语句是一个非常简单的语句,但却是 PowerShell 的基本部分。您会发现自己在编写的几乎每个脚本中都会多次使用它。我希望你比以前有更好的理解。

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

取消回复欢迎 发表评论:

关灯