Powershell pitfalls

后端 未结 21 1195
梦谈多话
梦谈多话 2020-12-23 01:44

What Powershell pitfalls you have fall into? :-)

Mine are:

# -----------------------------------
function foo()
{
    @(\"text\")
}

# Expected 1, a         


        
相关标签:
21条回答
  • 2020-12-23 02:31

    The logical and bitwise operators don't follow standard precedence rules. The operator -and should have a higher priority than -or yet they're evaluated strictly left-to-right.

    For example, compare logical operators between PowerShell and Python (or virtually any other modern language):

    # PowerShell
    PS> $true -or $false -and $false
    False
    
    # Python
    >>> True or False and False
    True
    

    ...and bitwise operators:

    # PowerShell
    PS> 1 -bor 0 -band 0
    0
    
    # Python
    >>> 1 | 0 & 0
    1
    
    0 讨论(0)
  • 2020-12-23 02:32

    Here is something Ive stumble upon lately (PowerShell 2.0 CTP):

    $items = "item0", "item1", "item2"
    
    $part = ($items | select-string "item0")
    
    $items = ($items | where {$part -notcontains $_})
    

    what do you think that $items be at the end of the script?

    I was expecting "item1", "item2" but instead the value of $items is: "item0", "item1", "item2".

    0 讨论(0)
  • 2020-12-23 02:34

    Say you've got the following XML file:

    <Root>
        <Child />
        <Child />
    </Root>
    

    Run this:

    PS > $myDoc = [xml](Get-Content $pathToMyDoc)
    PS > @($myDoc.SelectNodes("/Root/Child")).Count
    2
    PS > @($myDoc.Root.Child).Count
    2
    

    Now edit the XML file so it has no Child nodes, just the Root node, and run those statements again:

    PS > $myDoc = [xml](Get-Content $pathToMyDoc)
    PS > @($myDoc.SelectNodes("/Root/Child")).Count
    0
    PS > @($myDoc.Root.Child).Count
    1
    

    That 1 is annoying when you want to iterate over a collection of nodes using foreach if and only if there actually are any. This is how I learned that you cannot use the XML handler's property (dot) notation as a simple shortcut. I believe what's happening is that SelectNodes returns a collection of 0. When @'ed, it is transformed from an XPathNodeList to an Object[] (check GetType()), but the length is preserved. The dynamically generated $myDoc.Root.Child property (which essentially does not exist) returns $null. When $null is @'ed, it becomes an array of length 1.

    0 讨论(0)
  • 2020-12-23 02:35
    $files = Get-ChildItem . -inc *.extdoesntexist
    foreach ($file in $files) {
        "$($file.Fullname.substring(2))"
    }
    

    Fails with:

    You cannot call a method on a null-valued expression.
    At line:3 char:25
    + $file.Fullname.substring <<<< (2)
    

    Fix it like so:

    $files = @(Get-ChildItem . -inc *.extdoesntexist)
    foreach ($file in $files) {
        "$($file.Fullname.substring(2))"
    }
    

    Bottom line is that the foreach statement will loop on a scalar value even if that scalar value is $null. When Get-ChildItem in the first example returns nothing, $files gets assinged $null. If you are expecting an array of items to be returned by a command but there is a chance it will only return 1 item or zero items, put @() around the command. Then you will always get an array - be it of 0, 1 or N items. Note: If the item is already an array putting @() has no effect - it will still be the very same array (i.e. there is no extra array wrapper).

    0 讨论(0)
  • 2020-12-23 02:35

    alex2k8, I think this example of yours is good to talk about:

    # -----------------------------------
    function foo($a){
        # I thought this is right.
        #if($a -eq $null)
        #{
        #    throw "You can't pass $null as argument."
        #}
        # But actually it should be:
        if($null -eq $a)
        {
            throw "You can't pass $null as argument." 
        }
    }
    foo @($null, $null)
    

    PowerShell can use some of the comparators against arrays like this:

    $array -eq $value
    ## Returns all values in $array that equal $value
    

    With that in mind, the original example returns two items (the two $null values in the array), which evalutates to $true because you end up with a collection of more than one item. Reversing the order of the arguments stops the array comparison.

    This functionality is very handy in certain situations, but it is something you need to be aware of (just like array handling in PowerShell).

    0 讨论(0)
  • 2020-12-23 02:36

    My personal favorite is

    function foo() {
      param ( $param1, $param2 = $(throw "Need a second parameter"))
      ...
    }
    
    foo (1,2)
    

    For those unfamiliar with powershell that line throws because instead of passing 2 parameters it actually creates an array and passes one parameter. You have to call it as follows

    foo 1 2
    
    0 讨论(0)
提交回复
热议问题