Convert parameter string to Here String

Viewed 150

I want to call ffmpeg with Start-Process and -Wait, I have problem with $ArgumentList

function convertgif {
  
  $ArgumentList = @'
 -i $($args[0]) -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" "$([IO.Path]::ChangeExtension($args[0], 'mp4'))"
'@
  Start-Process -FilePath "C:\ffmpeg\bin\ffmpeg.exe" -ArgumentList $ArgumentList -Wait -NoNewWindow;
}

I can't find how to fix error

$args[0]: No such file or directory
2 Answers

You don't want a "here-string", you want an array that can be joined with spaces

function convertgif {
  $ArgumentList = @(
    "-i"
    '"' + $args[0] + '"'
    "-movflags"
    "faststart"
    "-pix_fmt"
    "yuv420p"
    "-vf"
    '"scale=trunc(iw/2)*2:trunc(ih/2)*2"'
    '"' + [IO.Path]::ChangeExtension($args[0], 'mp4') + '"'
  )
  
  Start-Process -FilePath "C:\ffmpeg\bin\ffmpeg.exe" -ArgumentList $ArgumentList -Wait -NoNewWindow;
}

In that context, "-movflags" and "faststart" are two separate arguments, even though from ffmpeg's point of view, they belong together. But that's purely a semantic detail of how ffmpeg parses its arguments, PowerShell (or Windows, for that matter) does not care about that.

Of course, whether you decide to form the end result by giving "-movflags" and "faststart" separately or as "-movflags faststart" is up to you.

Just like regular PowerShell strings come in two flavors - '...' (single-quoted, verbatim) vs. "..." (double-quoted, expandable aka interpolating) - so do here-strings.

Since you need interpolation - your string contains subexpressions ($(...)) whose output you want to incorporate into the string - you must use the double-quoted here-string variant (@"<newline>...<newline>"@):

$ArgumentList = @"
 -i "$($args[0])" -movflags faststart -pix_fmt yuv420p -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" "$([IO.Path]::ChangeExtension($args[0], 'mp4'))"
"@

Note: So as to also handle paths with spaces correctly, $($args[0]) is also enclosed in "...".

Passing all pass-through arguments to Start-Process's -ArgumentList parameter as a single string in which all arguments are included, using embedded "..." quoting as necessary, is preferable to passing the arguments individually, unfortunately, due to a longstanding bug - see this answer.


Taking a step back: You don't need to use Start-Process in order to synchronously invoke a console application in the current console window; direct invocation does just that, while additionally allowing you to capture the output from the application.

It even simplifies the syntax, as you then pass the arguments individually and needn't worry about explicit quoting of variable references or expressions (PowerShell will double-quote values with spaces for you, behind the scenes):

C:\ffmpeg\bin\ffmpeg.exe -i $args[0] -movflags faststart -pix_fmt yuv420p -vf 'scale=trunc(iw/2)*2:trunc(ih/2)*2' ([IO.Path]::ChangeExtension($args[0], 'mp4'))

See this answer for more information.

Related