3

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
user310291
  • 36,946
  • 82
  • 271
  • 487

2 Answers2

5

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.

Tomalak
  • 332,285
  • 67
  • 532
  • 628
  • 2
    While in an ideal world passing the arguments individually is undoubtedly preferable, a [longstanding bug](https://github.com/PowerShell/PowerShell/issues/5576) unfortunately makes it better to encode all arguments in a _single string_ - see [this answer](https://stackoverflow.com/a/62784608/45375). All that is needed to solve the OP's immediate problem is to switch from a _single_-quoted here-string to a _double_-quoted one. But ultimately there is no reason to use `Start-Process` at all: direct invocation will do and is preferable (which inherently involves passing arguments individually). – mklement0 Aug 14 '21 at 18:39
  • 1
    I realized that I initially missed the fact that you're _compensating for the bug_ by enclosing arguments that need it in _embedded_ `"..."` - that is certainly an option, but my personal sense is that simply using a single string with such embedded quoting is ultimately easier to understand and maintain. And let's keep our fingers crossed for a proper solution that allows individual argument-passing without workarounds, via the [proposed `-ArgumentArray` parameter](https://github.com/PowerShell/PowerShell/issues/5576#issuecomment-666566501). – mklement0 Aug 14 '21 at 20:55
  • @mklement0 Surely, people will understand the ramifications of `-ArgumentList` vs. `-ArgumentArray` instinctively... :) – Tomalak Aug 15 '21 at 09:48
  • To minimize confusion, the proposal suggests renaming `-ArgumentList` to `-ArgumentString`, keeping the former name only as an _alias_, for backward compatibility, to be documented as such. `Start-Process -?` would then show only `-ArgumentString`, and tab-completion would complete `-ArgumentString` _first_. That's the best that can be done without breaking backward compatiblility. – mklement0 Aug 15 '21 at 10:56
  • @mklement0 Yep, that's always the problem with releasing broken stuff into the wild - you constantly have to bend over backwards and can never really improve on things, making broken behaviors the default and better replacements a thing for a hand-full of advanced users. That's hardly a fix. – Tomalak Aug 15 '21 at 11:00
  • 1
    I hear you - have a look at [GitHub issue #6745](https://github.com/PowerShell/PowerShell/issues/6745). – mklement0 Aug 15 '21 at 11:03
4

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Will giving a bare `$args[0]` without double quotes produce the wanted result for paths with spaces? – Tomalak Aug 14 '21 at 18:51
  • 1
    @Tomalak, yes: for external-executable calls, PowerShell _rebuilds the command line_ behind the scenes using double-quoting (only) as needed: it automatically double-quotes (expanded) argument values that contain spaces. (It botches how empty-string arguments and arguments with embedded `"` chars. are handled, but that's another story - see [this answer](https://stackoverflow.com/a/66837948/45375)). – mklement0 Aug 14 '21 at 18:54
  • 2
    Sigh. There is no end to the quirks of PowerShell it seems. One can only remember so many coping strategies - the one in my answer is what I chose to settle on, it's always amazing to see how many levels down the rabbit hole goes. – Tomalak Aug 14 '21 at 20:18
  • Related [question](https://stackoverflow.com/questions/75724090/passing-multi-line-kql-query-to-az-powershell-cmdlet-error-parsing-query). Please answer if you can. – Rajesh Swarnkar Mar 13 '23 at 15:47