与许多其他语言一样,PowerShell有有条件地执行脚本中的代码的语句。其中一个陈述是 如果 陈述。今天,我们将深入潜入PowerShell中最根本的命令之一。
指数
条件执行
Your scripts will often need to make decisions and perform different logic based on those decisions. This is what I mean by conditional execution. You have one statement or value to evaluate, then execute a different sections of code based on that evaluation. This is exactly what the 如果
statement does.
IF语句
Here is a very basic example of the 如果
statement:
$condition = $true
如果 ( $condition )
{
Write-Output "The condition was true"
}
The first thing the 如果
statement does is evaluate the expression in parentheses. If it evaluates to $true
, then it will executes the scriptblock
in the braces. If the value was $false
, then it would skip over that scriptblock.
In the previous example, the 如果
statement was just evaluating the $condition
variable. It was $true
and would have executed the Write-Output
command inside the scriptblock.
In some languages, you can place a single line of code after the 如果
statement and it will get executed. That is not the case in PowerShell. You need to provide a full scriptblock
with braces for it to work correctly.
比较运算符
The most common thing you will use the 如果
statement for is comparing two items with each other. Powershell has special operators for different comparison scenarios. When you use a comparison operator, the value on the left hand side is compared to the value on the right hand side.
-eq平等
The -eq
does an equality check between two values to make sure they are equal to each other.
$value = Get-MysteryValue
如果 ( 5 -eq $value )
{
# do something
}
In this example, I am taking a known value of 5
and comparing it to my $value
to see if they match.
One possible usecase is to check the status of a value before you take an action on it. You could get a service and check that the status was running before you called Restart-Service
on it.
It is common in other languages like C# to use ==
for equality (ex: 5 == $value
) but that does not work with powershell. Another common mistake that people make is to use the equals sign (ex: 5 = $value
) that is reserved for assigning values to variables. By placing your known value on the left, it makes that mistaken more awkward to make.
这个运营商(和其他)有几种变体。
-eq
案不敏感平等-ieq
案不敏感平等-ceq
区分敏感的平等
- 不平等
Many operators have a related operator that is checking for the opposite result. -ne
will verify the values do not equal each other.
如果 ( 5 -ne $value )
{
# do something
}
Use this to make sure that the action only executes if the value is not 5
. A good use-cases where would be to check if a service was in the running state before you try and start it.
变化:
-ne
不区分大小写不等于-在e
不区分大小写不等于-cne
区分大小写不等于
These are just inverse variations of -eq
. I’ll group these types together when I list variations for other operators.
-gt -ge -lt -le大于或小于
These operators are used when checking to see if a value is larger or smaller than another value. The -gt -ge -lt -le
stand for GreaterThan, GreaterThanOrEqual, LessThan, and LessThanOrEqual.
如果 ( $value -gt 5 )
{
# do something
}
变化:
-gt
greater than-igt
大于,不区分大小写-cgt
大于,区分大小写-ge
大于或等于-ige
大于或平等,不区分大小写-cge
大于或等于,区分大小写-lt
less than-ilt
少于,不陈异亮的情况-clt
小于,区分大小写-le
少于 or equal-ile
小于或相等,不区分大小写-cle
小于或等于,区分大小写
我不知道为什么你会为这些运营商使用区分大小写和不敏感的选项。
- 麦克卡尔卡匹配
电源外壳 has its own wildcard based pattern matching syntax and you can use it with the -like
operator. These wildcard patterns are fairly basic.
?
将匹配任何单个字符*
将匹配任意数量的字符
$value = 'S-ATX-SQL01'
如果 ( $value -like 'S-*-SQL??')
{
# do something
}
It is important to point out that the pattern matches the whole string. If you need to match something in the middle of the string then you will need to place the *
on both ends of the string.
$value = 'S-ATX-SQL02'
如果 ( $value -like '*SQL*')
{
# do something
}
变化:
-like
不敏感通配符-ilike
不敏感通配符-clike
区分大小写的通配符-不是like
不敏感的通配符不匹配-在otlike
不敏感的通配符不匹配-cnotlike
区分敏感通配符不匹配
-match正则表达式
The -match
operator allows you to check a string for a regular expression based match. Use this when the wildcard patterns are not flexible enough for you.
$value = 'S-ATX-SQL01'
如果 ( $value -match 'S-\w\w\w-SQL\d\d')
{
# do something
}
默认情况下,正则表达式模式将匹配字符串中的任何位置。因此,您可以指定您希望如下所匹配的子字符串:
$value = 'S-ATX-SQL01'
如果 ( $value -match 'SQL')
{
# do something
}
Regex is a complex language of its own and worth looking into. I talk more about -match
and 使用正则表达式的许多方法 in another article.
变化:
-match
不陈异少的正则表达式-imatch
不陈异少的正则表达式-cmatch
case sensitive regex-不是match
不匹配的不敏感正则表达式-在otmatch
不匹配的不敏感正则表达式-cnotmatch
区分大小写的正则表达式不匹配
-is类型
You are able to check a value’s type with the -is
operator.
如果 ( $value -is [string] )
{
# do something
}
如果您正在使用类或在管道上接受各种对象,则可以使用此功能。您可以将服务或服务名称作为输入。然后检查您是否有服务并获取服务如果您只有名称。
如果 ( $Service -isnot [System.ServiceProcess.ServiceController] )
{
$Service = Get-Service -Name $Service
}
变化:
-is
of type-isnot
not of type
汇集运营商
When you use the previous operators with a single value, the result is $true
or $false
. This is handled slightly differently when working with a collection. Each item in the collection gets evaluated and the operator will instead return every value that evaluates to $true
.
PS> 1,2,3,4 -eq 3
3
This still works correctly in a 如果
statement. So a value is returned by your operator, then the whole statement is $true
.
$array = 1..6
如果 ( $array -gt 3 )
{
# do something
}
There is one small trap hiding in the details here that I need to point out. When using the -ne
operator this way, it is easy to mistakenly look at the logic backwards. Using -ne
with a collection will return $true
如果 any item in the collection does not match your value.
PS> 1,2,3 -ne 4
1
2
3
This may look like a clever trick, but we have operators - 索引
and -在
that handle this more efficiently and -不是contains
will correctly do what you expect.
- 索引
The - 索引
operator will check the collection for your value. As soon as as it finds a match, it will return $true
.
$array = 1..6
如果 ( $array - 索引 3 )
{
# do something
}
This is the preferred way to see if a collection contains your value. Using Where-Object
( or -eq
) will walk the entire list every time and be significantly slower.
变化:
- 索引
不敏感匹配-icontains
不敏感匹配-ccontains
case sensitive match-不是contains
不敏感不敏感的不敏感-在otcontains
不敏感不敏感的不敏感-cnotcontains
区分大小写不匹配
-在
The -在
operator is just like the - 索引
operator except the collection is on the right hand side.
$array = 1..6
如果 ( 3 -在 $array )
{
# do something
}
变化:
-在
不敏感匹配-iin
不敏感匹配-cin
case sensitive match-不是in
不敏感不敏感的不敏感-在otin
不敏感不敏感的不敏感-cnotin
区分大小写不匹配
逻辑运营商
逻辑运算符用于反转或组合其他表达式。
-不是
The -不是
operator flips an expression from $false
to $true
or from $true
to $false
. Here is an example where we want to perform an action when Test-Path
is $false
.
如果 ( -不是 ( Test-Path -Path $path ) )
Most of the operators we talked about do have a variation where you would not need to use the -不是
operator, but there are still times you would use it.
!
You can use !
as an alias for -不是
.
如果 ( -不是 $value ){}
如果 ( !$value ){}
You will see !
used more by people that come from another languages like C#. I prefer to type it out because I find it hard to see when quickly looking at my scripts.
-和
You can combine expressions with the -和
operator. When you do that, both sides need to be $true
for the whole expression to be $true
.
如果 ( ($age -gt 13) -和 ($age -lt 55) )
In that example, $age
must be 13 or older for the left side and less than 55 for the right side. I added extra parentheses to make it more clear in that example but they are optional as long as the expresion is simple. Here is the same example without them.
如果 ( $age -gt 13 -和 $age -lt 55 )
Evaluation happens from left to right. If the first item evaluates to $false
then it will exit early and not perform the right comparison. This is handy when you need to make sure a value exists before you use it. Test-Path
for example will throw an error if you give it a $null
path.
如果 ( $null -ne $path -和 (Test-Path -Path $path) )
-或者
The -或者
allows for you to specify two expressions and returns $true
如果 either one of them is $true
.
如果 ( $age -le 13 -或者 $age -ge 55 )
Just like with the -和
operator, the evaluation happens from left to right. Except that if the first part is $true
, then the whole statement is $true
and it will not process the rest of the expression.
Also make note of how the syntax works for these operators. You need two separate expressions. I have seen users try to do something like this $value -eq 5 -or 6
without realizing their mistake.
-Xor独家或者
This one is a little unusual. -xor
allows only one expression to evaluate to $true
. So if both items are $false
or both items are $true
, then the whole expression is $false
. Another way to look at this is the expression is only $true
when the results of the expression are different.
罕见的是,任何人都会使用这个逻辑运营商,我无法想出一个很好的例子,就是为什么我会使用它。
按位运算符
位运算符对值内的位进行计算,并产生新值。教学 按位运算符 超出了本文的范围,但这是他们的列表。
-band
binary AND-bor
binary OR-bxor
binary exclusive OR-bnot
binary NOT-shl
shift left-shr
shift right
电源外壳表达式
我们可以在条件语句内使用普通PowerShell。
如果 ( Test-Path -Path $Path )
Test-Path
returns $true
or $false
when it executes. This also applies to commands that return other values.
如果 ( Get-Process Notepad* )
It will evaluate to $true
如果 there is a returned process and $false
如果 there is nothing. It is perfectly valid to use pipeline expressions or other PowerShell statements like this:
如果 ( Get-Process | Where Name -eq Notepad )
These expressions can be combined with each other with the -和
and -或者
operators, but you may have to use parenthesis to break them into sub-expressions.
如果 ( (Get-Process) -和 (Get-Service) )
检查$ null
Having a no result or a $null
value evaluates to $false
in the 如果
statement. When checking specifically for $null
, it is a best practice to place the $null
on the left hand side.
如果 ( $null -eq $value )
There are quite a few nuances when dealing with $null
values in PowerShell. If you are interested in diving deeper, I have an article about 你想知道的一切关于$ null.
可变分配
我几乎忘了加上这一点 Prasoon Karunan V. reminded me of it.
您也可以添加以下案例:-)
—Prasoon Karunan V(@Prasoonkarunan) 2019年8月12日
如果 ($ process = get-process notepad -erroraction忽略){$ process} else {$ false}
通常,当您为变量分配值时,该值未传递到管道或控制台上。当您在子表达式中执行可变分配时,它会传递到管道。
PS> $first = 1
PS> ($second = 2)
2
See how the $first
assignment has no output and the $second
assignment does? When an assignment is done in an 如果
statement, it will execute just like the $second
assignment above. Here is a clean example on how you could use it:
如果 ( $process = Get-Process Notepad* )
{
$process | Stop-Process
}
If $process
gets assigned a value, then the statement will be $true
and then the $process
will get stopped.
Make sure you don’t confuse this with -eq
because this is not an equality check. This is a more obscure feature that most people don’t realize works this way.
备用执行路径
The 如果
statement allows you to specify an action for not only when the statement is $true
, but also for when it is $false
. This is where the 别的
statement comes into play.
别的
The 别的
statement is always the last part of the 如果
statement when used.
如果 ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
别的
{
Write-Warning "$path does not exist or is not a file."
}
In this example, we check the $path
to make sure it is a file. If we find the file, we move it. If not, we write a warning. This type of branching logic is very common.
嵌套if.
The 如果
and 别的
statements take a script block, so we can place any PowerShell command inside them, including another 如果
statement. This allows you to make use of much more complicated logic.
如果 ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
别的
{
如果 ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
别的
{
Write-Warning "$path could not be found."
}
}
在此示例中,我们首先测试快乐的路径,然后对其进行操作。如果失败,我们会拨打另一个检查并向用户提供更详细的信息。
eleesif
We are not limited to just a single conditional check. We can chain 如果
and 别的
statements together instead of nesting them by using the eleesif
statement.
如果 ( Test-Path -Path $Path -PathType Leaf )
{
Move-Item -Path $Path -Destination $archivePath
}
eleesif ( Test-Path -Path $Path )
{
Write-Warning "A file was required but a directory was found instead."
}
别的
{
Write-Warning "$path could not be found."
}
The execution happens from the top to the bottom. The top 如果
statement is evaluated first. If that is $false
, then it moves down to the next eleesif
or 别的
in the list. That last 别的
is the default action to take if none of the others return $true
.
转变
At tis point, I need to mention the 转变
statement. It provides an alternate syntax for doing multiple comparisons with a value. With the 转变
, you specify an expression and that result gets compared with several different values. If one of those values mach, then the matching code block is executed. Take a look at this example:
$itemType = 'Role'
转变 ( $itemType )
{
'Component'
{
'is a component'
}
'Role'
{
'is a role'
}
'Location'
{
'is a location'
}
}
There three possible values that can match the $itemType
. In this case, it will match with Role
and the is a role
would get executed. I used a very simple example just to give you some exposure to the 转变
operator. I talk more about 您想了解的一切关于交换机声明 in another article.
管道
The pipeline is a very unique and important feature of PowerShell. Any value that is not suppressed or assigned to a variable gets placed in the pipeline. The 如果
provides us a way to take advantage of the pipeline in a way that is not always obvious.
$discount = 如果 ( $age -ge 55 )
{
Get-SeniorDiscount
}
eleesif ( $age -le 13 )
{
Get-ChildDiscount
}
别的
{
0.00
}
Each script block is placing the results the commands or the value into the pipeline. Then we are assigning the result of the if statement to the $discount
variable. That example could have just as easily assigned those values to the $discount
variable directly in each scriptblock. I can’t say that I use this with the 如果
statement very often, but I do have an example where I have used this recently.
数组inline.
我有一个职能 invoke-snowsql. 它启动了一个有几个命令下的参数的可执行文件。这是我构建参数数组的函数的剪辑。
$snowSqlParam = @(
'--accountname', $Endpoint
'--username', $Credential.UserName
'--option', 'exit_on_error=true'
'--option', 'output_format=csv'
'--option', 'friendly=false'
'--option', 'timing=false'
如果 ($Debug)
{
'--option', 'log_level=DEBUG'
}
如果 ($Path)
{
'--filename', $Path
}
别的
{
'--query', $singleLineQuery
}
)
The $Debug
and $Path
variables are parameters on the function that are provided by the end user. I evaluate them inline inside the initialization of my array. If $Debug
is true, then those values fall into the $snowSqlParam
in the correct place. Same holds true for the $Path
variable.
简化复杂操作
这是不可避免的,您遇到了一个情况下有太多比较来检查和您的If语句滚动屏幕右侧。
$user = Get-ADUser -Identity $UserName
如果 ( $null -ne $user -和 $user.Department -eq 'Finance' -和 $user.Title -match 'Senior' -和 $user.首页Drive -不是like '\\server\*' )
{
# Do Something
}
他们很难读,这让你更容易犯错误。我们可以做一些事情。
线路延续
There some operators in PowerShell that let you wrap you command to the next line. The logical operators -和
and -或者
are good operators to use if you want to break your expression into multiple lines.
如果 ($null -ne $user -和
$user.Department -eq 'Finance' -和
$user.Title -match 'Senior' -和
$user.首页Drive -不是like '\\server\*'
)
{
# Do Something
}
那里还有很多事情,但将每件作品放在自己的线上有很大的不同。当我获得两个以上的比较或者我必须滚动到右侧来读取任何一个连接时,我通常只执行此操作。
预算效果
我们可以将该陈述从IF语句中拿出来,只需检查结果。
$needsSecureHomeDrive = $null -ne $user -和
$user.Department -eq 'Finance' -和
$user.Title -match 'Senior' -和
$user.首页Drive -不是like '\\server\*'
如果 ( $needsSecureHomeDrive )
{
# Do Something
}
这感觉比前面的例子更清洁。您还有机会使用解释您真正检查的变量名称。这也是自我记录代码的也是节省不必要的评论的。
多重IF语句
我们可以将其分解为多个陈述并一次检查它们。在这种情况下,我们将使用标志或跟踪变量来组合结果。
$skipUser = $false
如果 ( $null -eq $user )
{
$skipUser = $true
}
如果 ( $user.Department -ne 'Finance' )
{
Write-Verbose "Is not in Finance department"
$skipUser = $true
}
如果 ( $user.Title -match 'Senior' )
{
Write-Verbose "Does not have Senior title"
$skipUser = $true
}
如果 ( $user.首页Drive -like '\\server\*' )
{
Write-Verbose "首页 drive already configured"
$skipUser = $true
}
如果 ( -不是 $skipUser )
{
# do something
}
I did have to invert the logic to make the flag logic work correctly. Each evaluation is an individual 如果
statement. The advantage of this is that when you are debugging, you can tell exactly what the logic is doing. I was able to add much better verbosity at the same time.
明显的缺点是它是更多的代码。代码更复杂,看起来它需要一行逻辑,并将其爆炸成25行。
使用功能
我们还可以将所有验证逻辑移动到函数中。看看这看起来有多清洁。
如果 ( Test-SecureDriveConfiguration -ADUser $user )
{
# do something
}
You still have to create the function to do the validation, but it makes this code much easier to work with. It makes this code easier to test. In your tests, you can mock the call to Test-ADDriveConfiguration
and you only need two tests for this function. One where it returns $true
and one where it returns $false
. Testing the other function will be simpler because it is so small.
该函数的正文仍然可以是我们开始的单行或我们在最后一节中使用的爆炸逻辑。这对于这两个方案都很好,并且允许您在以后轻松更改该实现。
误差处理
One really important use of the 如果
statement is to check for error conditions before you run into errors. A good example is to check if a folder already exists before you try and create it.
如果 ( -不是 (Test-Path -Path $folder) )
{
New-Item -Type Directory -Path $folder
}
我喜欢说,如果你期望发生例外,那么它并不是一个例外。因此,请检查您的值并验证您的条件。
如果你想潜入实际的例外处理,我有一篇文章 你想知道的一切例外情况.
最后的话
The 如果
statement is such a simple statement but is a very fundamental piece of PowerShell. You will find yourself using this multiple times in almost every script you write. I hope you you have a better understanding than you had before.