powershellcmdpipelinebinary-data

How to convert cmd pipe to powershell in context of binary data (ffmpeg and streamlink)


I'd like to convert a pipe of video stream between streamlink and ffmpeg from cmd to Powershell. But from what I searched powershell only supports piping objects.

Is there any way to reproduce this in Powershell without using Start-Process -FilePath "cmd" -ArgumentList [...]? I'd prefer not to use it because I need access to the exit code.

streamlink $stream_url best -O | ffmpeg -i pipe: "output_file.mp4"

note: the line above had some args removed for better clarity


Solution

  • In PowerShell (Core) 7 - specifically in v7.4+ - your command would work as-is, because in these versions piping raw bytes (binary data) is supported and automatically employed when piping between two external programs (see this answer for more information).

    In earlier versions, notably including Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), the simplest workaround is to directly invoke your command line via cmd /c to take advantage of cmd.exe's binary pipeline:

    cmd /c "streamlink `"$stream_url`" best -O | ffmpeg -i pipe: `"output_file.mp4`""
    

    As shown above, direct invocation of cmd.exe is possible (no need for Start-Process)[1], which relays the exit code reported by the given command line to PowerShell, allowing you to query it via the automatic $LASTEXITCODE variable.

    Note that, for robustness, embedded (escaped) "..." quoting was also applied to the $stream_url argument, as its expanded value could contain cmd.exe metacharacters such as & that would otherwise break the command.


    [1] The general advice applies:
    To synchronously execute console applications or batch files in the current console (terminal), call them directly (some.exe or c:\path\to\some.exe ... or & 'c:\path with spaces\to\some.exe' ... or & $exePathStoredInVariable ...). This also allows you to capture their output directly and to later query their exit code via $LASTEXITCODE. Do not use Start-Process (or the System.Diagnostics.Process API it is based on). See this answer and this guidance.
    That said, in scenarios where you do need Start-Process, you can obtain the exit code of the launched process as follows: use the -PassThru switch, which makes Start-Process emit a process-information object; once the process has exited (which you can await with the -Wait switch or by calling .WaitForExit() on the process-information object), you can obtain the exit code via its .ExitCode property.