2

The code explains it best:

# Trying to store in variable and also output on the same line
$SqlFileDest = "C:\Temp\SomeFile.bak" | Write-Host "##[debug]Output file is $($_)"

I'm using Azure DevOps with PS and to log debug commands, they need to be in the format Write-Host "##[debug]...".

I'm able to do | Write-Host, but when I try and modify the string, I receive an error.

I'm trying to avoid rewriting many scripts in favor of a simple approach.

mklement0
  • 382,024
  • 64
  • 607
  • 775
William YK
  • 1,025
  • 12
  • 26

1 Answers1

1
# Note the (...) around the assignment.
Write-Host "##[debug]Output file is $( ($SqlFileDest="C:\Temp\SomeFile.bak") )"
  • That is, embed the variable assignment in an expandable string ("...") via $(...), the subexpression operator, and **enclose the assignment in (...) so as to pass its value through**.

  • Note that, unlike $(...) in Bash, for instance, PowerShell's $(...) runs in-process, directly in the caller's scope, so that variable $SqlFileDest is visible to subsequent code.


A pipeline-based alternative requires use of ForEach-Object with a script block ({ ... }), so that the automatic $_ variable is meaningfully defined:

# Note the (...) around the assignment.
($SqlFileDest = "C:\Temp\SomeFile.bak") |
  ForEach-Object { Write-Host "##[debug]Output file is $_" }

An alternative is to use the (rarely necessary) Write-Output cmdlet with the common -OutVariable parameter parameter:

# Note the use of -OutVariable, and that the target var. is NOT $-prefixed
Write-Output "C:\Temp\SomeFile.bak" -OutVariable SqlFileDest |
  ForEach-Object { Write-Host "##[debug]Output file is $_" }

A small caveat is that the target variable, $SqlFileDest will be System.Collections.ArrayList-typed, i.e., be an array-like data structure with a single element containing the output string in this case, because -OutVariable always returns the captured output this way - see GitHub issue #3154 for a discussion.


As for what you tried:

$SqlFileDest = "C:\Temp\SomeFile.bak" | Write-Host "##[debug]Output file is $($_)"

Leaving the problem with use of $_ outside a script block aside, this assigns the entire pipeline's output to $SQlFileDest.

Since Write-Host produces no pipeline input, $a will be $null. See this answer for more information.


A note re Azure and diagnostic/debug output:

  • Write-Host is used here to produce specially formatted stdout output for Azure in this case, which calls via PowerShell's CLI, where Write-Host output becomes stdout output too.

    • While Write-Output - or simply implicit output of strings - is the better choice, using Write-Host is very commonly found online, including in the documentation.
  • If you generally want to produce diagnostic / debugging output in PowerShell, you can pipe to Write-Verbose or Write-Debug, which target the verbose and debug output streams respectively, which obviates the need for ForEach-Object if you supply the message via a delay-bind script block,[1] as zett42 points out; e.g.:

    # Note: 
    #  * -Verbose produces verbose output *unconditionally*
    #    If you omit it, the $VerbosePreference preference var. controls the behavior.
    #    By default, there is *no* output.
    #    The same applies analogously to -Debug / $DebugPreference. 
    #  * Note the use of { ... } - a delay-bind script block, which
    #    in turn requires targeting the -Message parameter explicitly.
    ($SqlFileDest = "C:\Temp\SomeFile.bak") |
      Write-Verbose -Verbose -Message { "Output file is $_" }
    

[1] The reason this technique can not be used with Write-Host is that its -Object parameter (receiving the object(s) to write) is [object]-typed, which precludes use of delay-bind script blocks. See this answer for details.

mklement0
  • 382,024
  • 64
  • 607
  • 775