New-Item messing up my variable PowerShell

692 Views Asked by At

I wrote a very simple script to acquire a random free drive letter. The function finds a random free letter , creates a new empty text file of that drive letter name eg. Q.txt I then return the value as $new_letter but when it comes out of the function somehow newly file created path is a part of the variable C:\AppPack\Logs\Q.txt Q

Is it something New-Item messing up with my $new_letter variable ?


function get_drive_letter()
{
$letter_acquired = $false
Do
{
$new_letter = Get-ChildItem function:[h-z]: -Name | ForEach-Object { if (!(Test-Path $_)){$_} } | random -Count 1 | ForEach-Object {$_ -replace ':$', ''}
    write-host ("RIGHT AFTER " + $new_letter)
    if (!(test-path "C:\AppPack\Logs\$new_letter.txt"))
    {    
    New-Item -Path C:\AppPack\Logs\ -Name "$new_letter.txt" -ItemType "file" 
    write-host ("FROM FUNCTION " + $new_letter)
    $letter_acquired = $true
    return $new_letter
     }
    
    
    else
    {
    write-host ("LETTER USED ALREADY")
    write-host ($new_letter)
    }
}
while($letter_acquired = $false)
}

$drive_letter = $null

$drive_letter = get_drive_letter
write-host ("RIGHT AFTER FUNCTION " + $drive_letter)

OUTPUT :

RIGHT AFTER Q
FROM FUNCTION Q
RIGHT AFTER FUNCTION C:\AppPack\Logs\Q.txt Q
1

There are 1 best solutions below

0
On BEST ANSWER

A PowerShell function outputs everything, not just the result of the expression right after return!

The additional file path you see is the output from New-Item ... - it returns a FileInfo object for the file you just created.

You can suppress output by assigning it to the special $null variable:

# Output from New-Item will no longer "bubble up" to the caller
$null = New-Item -Path C:\AppPack\Logs\ -Name "$new_letter.txt" -ItemType "file"
return $new_letter

Or by piping to Out-Null:

New-Item ... |Out-Null

Or by casting the entire pipeline to [void]:

[void](New-Item ...)

Although I recommend explicitly handling unwanted output at the call site, you can also work around this behavior with a hoisting trick.

To demonstrate, consider this dummy function - let's say we "inherit" it from a colleague who didn't always write the most intuitive code:

function Get-RandomSquare {
  "unwanted noise"

  $randomValue = 1..100 |Get-Random

  "more noise"

  $square = $randomValue * $randomValue

  return $square
}

The function above will output 3 objects - the two garbage strings one-by-one, followed by the result that we're actually interested in:

PS ~> $result = Get-RandomSquare
PS ~> $result
unwanted noise
more noise
6400

Let's say we've been told to make as few modifications as possible, but we really need to suppress the garbage output.

To do so, nest the entire function body in a new scriptblock literal, and then invoke the whole block using the dot-source operator (.) - this forces PowerShell to execute it in the function's local scope, meaning any variable assignments persist:

function Get-RandomSquare {
  # suppress all pipeline output
  $null = . {
    "unwanted noise"
    $randomValue = 1..100 |Get-Random 
    "more noise"
    $square = $randomValue

    return $square
  }

  # variables assigned in the block are still available
  return $square
}