Search code examples
arrayspowershellobjectvariablesscope

Variable scope within foreach-object in Powershell


I am trying to understand the output & error message for:

# declare empty array
$thisDataArray = @()
# assign array values
$thisDataArray = '123','cadabra','456','789','trouble'
# declare empty array to receive specific values
$letters = @()
# use this pattern to filter array members that contain letters
$regex = "[a-z]"

# confirm the array values
Write-Host $thisDataArray
# pipe array & filter to find the values
$thisDataArray | Select-String -AllMatches -Pattern $regex -CaseSensitive | ForEach-Object { $letters = $_.Matches.Value} 
# output the result
Write-Host $letters

The error is: The variable $letters is assigned but never used. And the output is:

123 cadabra 456 789 trouble
t r o u b l e

My questions are:

  1. How is Write-Host $letters not using the assigned variable $letters?
  2. Why do I only get the one array member 'trouble' from my $regex?
  3. And finally. Why are there spaces between the chars of trouble e.g 't r o u b l e'

Any suggestions appreciated.


Solution

    1. How is Write-Host $letters not using the assigned variable $letters?

    Because it is outside the ForEach-Object loop, hence, you're only reading the last result from that variable assignment. You probably wanted:

    $thisDataArray | Select-String -AllMatches -Pattern $regex -CaseSensitive | ForEach-Object {
        $letters = $_.Matches.Value
        Write-Host $letters
    }
    

    Though, is worth noting that output to console is implicit unless captured or redirected, so, Write-Host might not be needed nor is a variable assignment:

    $thisDataArray | Select-String -AllMatches -Pattern $regex -CaseSensitive | ForEach-Object {
        $_.Matches.Value
    }
    
    1. Why do I only get the one array member 'trouble' from my $regex?

    You don't, you would get cadabra and trouble assuming Write-Host is inside the loop.

    1. And finally. Why are there spaces between the chars of trouble e.g t r o u b l e

    Because your regex [a-z] matches a single character and since -AllMatches is being used, all appeareances of a character match are being outputted. The reason why they then appear concatenated by a space is because Write-Host is stringifying the $letters array and joining it by $OFS (default $OFS is a space). i.e.:

    PS ..\pwsh> $letters      
    t
    r
    o
    u
    b
    l
    e
    
    PS ..\pwsh> Write-Host $letters
    t r o u b l e
    
    PS ..\pwsh> [string] $letters
    t r o u b l e
    
    PS ..\pwsh> $ofs = ''
    PS ..\pwsh> [string] $letters
    trouble
    
    • If you want to match all letters from a to z you could use:
    $regex = "[a-z]+"
    
    • If you want to match those elements $thisDataArray which have only letters from a to z then you could use:
    $regex = "^[a-z]+$"
    

    As for the "error" The variable $letters is assigned but never used, I assume you're referring to a PSScriptAnalyzer Rule, namely UseDeclaredVarsMoreThanAssignments, which simply means, that the variable being assigned inside a script block (ForEach-Object -Process { ... } in this case) is never used inside that script block:

    enter image description here