Powershell 中的行延续中间字符串

Nin*_*ura 5 syntax powershell readability line-continuation

我正在尝试在具有两个相同捕获卡的系统上编写 FFmpeg 命令,因此我必须使用它们的“备用名称”,否则 FFmpeg 无法理解我正在调用哪一个。

唯一的问题是备用名称非常长,尤其是通过 dshow 调用音频和视频时:

-i video="@device_pnp_\\?\usb#vid_07ca&pid_0570&mi_00#7&3886ab1a&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global":audio="@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{0E6F0DEF-2B29-4117-8D30-13F01160AC5B}"
Run Code Online (Sandbox Code Playgroud)

我想在调用音频之前打破这个“字符串”中线,以使其更具可读性,如下所示:

-i video="@device_pnp_\\?\usb#vid_07ca&pid_0570&mi_00#7&3886ab1a&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"`
:audio="@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{0E6F0DEF-2B29-4117-8D30-13F01160AC5B}"
Run Code Online (Sandbox Code Playgroud)

然而,反引号在这种情况下似乎不起作用,我猜是因为它与角色接壤?如果 I 和反引号前有一个空格,则会破坏字符串并且 FFmpeg 会出错:

    -i video="@device_pnp_\\?\usb#vid_07ca&pid_0570&mi_00#7&3886ab1a&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global" `
:audio="@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{0E6F0DEF-2B29-4117-8D30-13F01160AC5B}"
Run Code Online (Sandbox Code Playgroud)

有没有办法在 Powershell 中将其拆分为多行?

编辑 - 全功能命令:

ffmpeg -y -hide_banner -thread_queue_size 9999 -indexmem 9999 -guess_layout_max 0 -f dshow -rtbufsize 2147.48M `
-video_size 1920x1080 -framerate 60 `
-i video="@device_pnp_\\?\usb#vid_07ca&pid_0570&mi_00#7&3886ab1a&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global":audio="@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{0E6F0DEF-2B29-4117-8D30-13F01160AC5B}" `
-thread_queue_size 9999 -indexmem 9999 -guess_layout_max 0 -f dshow -rtbufsize 2147.48M `
-video_size 1920x1080 -framerate 60 `
-i video="@device_pnp_\\?\usb#vid_07ca&pid_0570&mi_00#7&24df76f&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global":audio="@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{0A494693-5F33-4304-88D7-394757E09648}" `
-map 0:0,0:1 -map 0:1 -map 1:0,1:1 -map 1:1 -c:v h264_nvenc -r 60 -rc-lookahead 120 -forced-idr 1 -strict_gop 1 `
-sc_threshold 0 -flags +cgop -force_key_frames "expr:gte(t,n_forced*2)" -preset: llhp -pix_fmt yuv420p -b:v 250M `
-minrate 250M -maxrate 250M -bufsize 250M -c:a aac -ar 44100 -b:a 384k -ac 2 -af "aresample=async=250" -vsync 1 `
-max_muxing_queue_size 9999 -f segment -segment_time 600 -segment_wrap 9 -reset_timestamps 1 `
-segment_format_options max_delay=0 C:\Users\Jordan\Videos\FFmpeg\FFmpeg%02d.ts
Run Code Online (Sandbox Code Playgroud)

我的目标是打破我对视频/音频设备的要求,以更好地适应街区的其余部分。

mkl*_*nt0 8

在 PowerShell 中的字符串`内部或字符串之间的行尾使用与在 Bash 中用于行继续的工作方式不同例如:"..."\

如果将While放置在行的最后,则在 PowerShell 中`启用行继续:

  • 跨行"..."字符串中使用时,` 会转义换行符,而不删除它,这实际上是一个无操作,因为 PowerShell 字符串文字中的换行符不需要转义。

  • 当,正如您的尝试一样,在应该形成单个参数的字符串之间使用时(在一行上直接相邻放置的字符串,例如,video="...":audio="..."),` 有效地充当参数分隔符,因此您不能分割应该的内容这样就变成单个字符串了。[1]

简而言之:在 PowerShell 中

  • 只能在参数之间使用续行。

  • 为了将单个参数分布在多行中,您需要使用表达式( (...));表达式可以跨越多行而无需续行。


在您的情况下,一种选择是将字符串部分指定为数组的元素,稍后将这些元素连接起来形成单个字符串。

使用一个简化的例子;请注意该-join技术如何与使用行结束共存`以将命令分布在参数之间的多行上:

ffmpeg -framerate 60 `
  -i (@(
    'video=@device_pnp_\\?\1'
    'audio=@device_cm_2'
  ) -join ':') `
  -f dshow
Run Code Online (Sandbox Code Playgroud)

这在执行时转换为以下单行:

ffmpeg -framerate 60 -i video=@device_pnp_\\?\1:audio=@device_cm_2 -f dshow
Run Code Online (Sandbox Code Playgroud)

请注意,您最初的参数内部引用 ( video="...":audio="...") 未被使用,因为它通常是不必要的(尽管某些程序,例如msiexec,遗憾地需要它)。

或者,如果您不介意:在每个子字符串中包括最后一个子字符串,则可以使用+字符串连接,正如Ted Shaneyfelt 的回答指出的那样:

ffmpeg -framerate 60 `
  -i (
    'video=@device_pnp_\\?\1:' +
    'audio=@device_cm_2'
  ) `
  -f dshow
Run Code Online (Sandbox Code Playgroud)

PowerShell 根据需要自动为外部程序的参数添加双引号,即如果它们包含 PowerShell 认为不在嵌入双引号内的空格。有关更多信息,请参阅此答案,包括何时可能出现问题。


另一种选择预先构造(仅)长参数并通过变量引用它们:

$inputSpec = @(
  'video=@device_pnp_\\?\1'
  'audio=@device_cm_2'
) -join ':'

ffmpeg -framerate 60 -i $inputSpec -f dshow
Run Code Online (Sandbox Code Playgroud)

通常,要将命令的参数分布在多行中而不必在参数之间的`行末尾使用,请预先在数组中构造参数,并使用splatting来传递它:

# Construct an array containing all arguments.
# Note: Each parameter (+ operand, if any) is placed on its own line
#       here for clarity, but you're free to choose as many or few 
#       lines desired.
$ffmpegArgs = @(
  '-y'
  'hide_banner'
  '-thread_queue_size', '9999'
  '-indexmem', '9999'
  '-guess_layout_max', '0'
  '-f', 'dshow'
  '-rtbufsize', '2147.48M'
  '-video_size', '1920x1080'
  '-framerate', '60'
  '-i', (@(
    'video=@device_pnp_\\?\usb#vid_07ca&pid_0570&mi_00#7&3886ab1a&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global'
    'audio=@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{0E6F0DEF-2B29-4117-8D30-13F01160AC5B}'
  ) -join ':')
)

# ...
    
# Pass the array via splatting - note the '@'
# (Since ffmpeg is an *external program*, passing the array
#  directly, as $ffmpegArgs would work too.)
ffmpeg @ffmpegArgs
Run Code Online (Sandbox Code Playgroud)

笔记:

  • Splatting 的主要用例是启用命令参数的编程构造,并且需要不同的语法,如图所示。

  • 这个最终被拒绝的RFC 提案提出了新的语法,该语法将使常规参数语法的多行延续变得更容易,而不需要`在每个内部行的末尾;拒绝的决定就在这里


[1] 有关 PowerShell 如何解析复合令牌的更多信息,请参阅GitHub 问题 #6467