1

I have the following working call when I execute it directly in my PowerShell window:

$myexe = "C:\MyExe.exe"
"MyString" | & $myexe // works
Write-Output "MyString" | & $myexe // seems to work too

However, when I perform the same in a function of a PowerShell script it does no longer work. The program does not receive the string...any ideas?

4
  • "when I perform the same in a function of a PowerShell script it does no longer work." - can you show us such a script? Commented Feb 17, 2021 at 16:28
  • ditto, because as noted below, with just the posted lines in a pristine .ps1, it works as expected, as shown below. Commented Feb 17, 2021 at 16:35
  • It's very hard to describe: we realized yesterday that due to the complexity of the script, we overlooked that it executed a different version of the same executable + the maintainers of the executable explicitly removed the feature of piping in data to this executable between those two versions. So that's the actual reason why it doesn't work and why we couldn't provide a suitable example. Commented Feb 20, 2021 at 8:51
  • @D.R. It sounds like you figured out the answer to your question. It's probably worth writing that up quickly and accepting it so this question can be marked closed -- and, if you're able to provide more detail, help someone in the same situation down the line. Though even accepting a dupe of your comment is probably helpful. Commented Nov 19, 2021 at 22:50

2 Answers 2

2

Unlike POSIX-compatible shells such as bash, PowerShell does not automatically relay pipeline input received by a given script or function to commands you call from inside that script or function.

To do so, you must use the automatic $input variable[1]:

For instance:

function foo {
  # Relay the pipeline input received by this function 
  # to an external program, using `cat` as an example.
  $input | & /bin/cat -n # append $args to pass options through
}

'MyString' | foo

Note that & is optional in this case, because /bin/cat is an unquoted, literal path - see this answer for when & is required.

The output (on a Unix-like platform) is: 1 MyString, showing that the cat utility received the foo function's own pipeline input via its stdin, thanks to use of $input.

Without piping $input to cat, the latter would receive no stdin input at all (and in the case of cat would block, waiting for interactive input).

If you want to support passing filename arguments too - in lieu of pipeline input - more work is needed:

function foo {
  if ($MyInvocation.ExpectingInput) { # Pipeline input present
    # Relay the pipeline input received by this function 
    # to an external program, using `cat` as an example.
    $input | /bin/cat -n $args
  } else { # NO pipeline input.
    /bin/cat -n $args
  }
}

'MyString' | foo  # pipeline input
foo file.txt      # filename argument

Note:

  • Only non-advanced functions and scripts can make use of the $input variable, and its use implies that all input piped to the enclosing function/script is collected in full, up front, before sending it on begins.

  • To truly stream a script / function's own pipeline input to a single invocation of an external program - i.e. to relay the input as it is being received by the script / function - requires direct use of .NET APIs, namely System.Diagnostics.ProcessStartInfo and System.Diagnostics.Process.


[1] Similarly, $input is required to access data piped to a PowerShell command from outside PowerShell, via PowerShell's CLI; e.g., from bash:
echo hi | pwsh -c '$input | ForEach-Object { "[{0}]" -f $_ }'

Sign up to request clarification or add additional context in comments.

3 Comments

would you happen to have a good example of note number 2, "...requires direct use of .NET APIs"?
@Gregory, I haven't looked, but I assume that there are C# examples out there. If you have trouble adapting them to PowerShell, I encourage you to ask a new question (feel free to post a link to it here to notify me, once you have done so).
this was one of the avenues I was trying to chase down here stackoverflow.com/a/66270719/4496560 as a third solution to either have a [cmdlet]/[pscmdlet] object as a NoteProperty or to return one back to the pipeline via a ScriptMethod, but it just wasn't working out for me
1

With a simple script that only contains this...

$myexe = 'nslookup.exe'
'stackexchange.com' | & $myexe # works
Write-Output 'stackexchange.com' | & $myexe # seems to work too

..., it works as expected in the Consolehost/WindowsTerminal as expected.

 .\TestExePipe.ps1
# Results
<#
Default Server:  ...
Address:  ...

> Server:  ...
Address:  ...

Non-authoritative answer:
Name:    stackexchange.com
Addresses:  151.101.193.69
...

> Default Server:  ...
Address:  ...

> Server:  ...
Address:  ...

Non-authoritative answer:
Name:    stackexchange.com
Addresses:  151.101.129.69
          ...

>
#>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.