1

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
Fenomatik
  • 457
  • 2
  • 8
  • 22
  • The function returns all output from all commands run in the function. – js2010 May 06 '21 at 20:12
  • @js2010 I have updated my Do- While as well , Cant I just return a single variable value ? – Fenomatik May 06 '21 at 20:19
  • As an aside, `while($letter_acquired = $false)` is no different from `while($false)` - you'll want `while($letter_acquired -eq $false)` (I would prefer `while(-not $letter_acquired)`) for it to work the way you intend – Mathias R. Jessen May 06 '21 at 20:19
  • You would have to hide the output of all other commands, for example `new-item file5 > $null`. Return doesn't really do what you think it does. It's only to branch out of code. – js2010 May 06 '21 at 21:07
  • 1
    In short: any output - be it from a PowerShell command or a .NET method call - that is neither captured in a variable nor redirected (sent through the pipeline or to a file) is _implicitly output_. To simply _discard_ such output, use `$null = ...`. If you don't discard such output, it becomes part of a script or function's "return value" (stream of output objects). See [this answer](https://stackoverflow.com/a/55665963/45375) to the linked duplicate. – mklement0 May 06 '21 at 22:26

1 Answers1

7

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
}
Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206