Избежать переменных Powershell Начиная с - (da sh) в командлете или вызове программы? - PullRequest
0 голосов
/ 13 июля 2020

Итак, я просто рвал волосы и кричал последние 30 минут, потому что я узнал, что (по-видимому?) PowerShell автоматически удалит первый - в любой переменной, независимо от того, экранированный, если эта переменная начинается с - и используется в пути. Вот мой сценарий, который представляет собой простую оболочку вокруг FFMPEG для объединения двух звуковых дорожек в одну и вырезания видео:

function IsNull($objectToCheck) {
    if ($objectToCheck -eq $null) {
        return $true
    }

    if ($objectToCheck -is [String] -and $objectToCheck -eq [String]::Empty) {
        return $true
    }

    if ($objectToCheck -is [DBNull] -or $objectToCheck -is [System.Management.Automation.Language.NullString]) {
        return $true
    }

    return $false
}

$_input = [Management.Automation.WildcardPattern]::Escape($args[0])
$time = $args[1]
$duration = $args[2]

if ($time -match ":") {
    $_matches = ($time | Select-String ":" -AllMatches).Matches
    $seconds = $time.Substring($_matches[$_matches.Count - 1].Index + 1, 2) -as [int]
    $finalSeconds = $seconds - 1
    $timeFlag2 = "-ss 1"
    $time = $time.Substring(0, $_matches[$_matches.Count - 1].Index) + (":{0:d2}" -f $finalSeconds)
    # there's totally a bug in here where we don't handle :00 seconds, plz ignore
}
else {
    $timeFlag2 = ""
}

if (-not (IsNull($duration))) {
    $d = "-t " + $duration
}
else {
    $d = ""
}

Write-Verbose "C:\ffmpeg.exe -ss $time -i $_input $timeFlag2 -filter_complex '[0:a:0]volume=0.6[l];[0:a:1]volume=1.2[k];[k][l]amerge=inputs=2[a]' -map '0:v:0' -map '[a]' -c:v copy -c:a libmp3lame -q:a 3 -ac 2 $d 'output-muxxed.mp4'" -Verbose

C:\ffmpeg.exe -ss $time -i $_input $timeFlag2 -filter_complex '[0:a:0]volume=0.6[l];[0:a:1]volume=1.2[k];[k][l]amerge=inputs=2[a]' -map '0:v:0' -map '[a]' -c:v copy -c:a libmp3lame -q:a 3 -ac 2 $d 'output-muxxed.mp4'

Все, что я хочу сделать, это: взять файл, взять временную метку для начала отсечение от, а затем продолжительность. Я делаю небольшую математику, поэтому могу передать -ss до и после вызова -i, чтобы исправить странную ошибку с зеленой рамкой при работе с ShadowPlay.

Этот в настоящее время не работает и выдаст ошибку : Unrecognized option ss 1. K, поэтому я решил, что переменная не экранируется должным образом. Поэтому я обновил последнюю команду, добавив кавычки вокруг нее, но все еще без кубиков. Тогда я решил сделать: $timeFlag2 = "`-ss 1", но все равно не повезло. Во время всего этого команда Write-Verbose производит:

VERBOSE: C:\ffmpeg.exe -ss 00:02:01 -i .\input.mp4 -ss 1 -filter_complex '[0:a:0]volume=0.6[l];[0:a:1]volume=1.2[k];[k][l]amerge=inputs=2[a]' -map '0:v:0' -map '[a]' -c:v copy -c:a libmp3lame -q:a 3 -ac 2  -t 40 'output-muxxed.mp4'

Что на 100% правильно и работает при копировании на терминал. После кучи дерьмовых поисков я решил попробовать еще кое-что и обновил вызовы присваивания $timeFlag2 и $d на:

$timeFlag2 = " -ss 1"
$d = " -t " + $duration

И мир заработал ... Все ошибки исчезли. Удаление пробелов приводит к их повторному введению.

Итак, мой вопрос: , что, черт возьми, здесь происходит? Почему `, оператор escape, не экранирует -? Почему космос ускользает от него? Почему это выглядит правильно экранированным при передаче в строку для Write-Verbose, но не при передаче в командлет Path или как там, черт возьми, это называется?

Я - loooonnnnnggg-time bash пользователь , но я использую PS около 6 недель, поэтому я уверен, что здесь много неправильных и что мне не хватает, поэтому все советы / помощь / критические замечания приветствуются!

Заранее спасибо!

1 Ответ

0 голосов
/ 15 июля 2020

Хорошо, понял это, особенно из PoV экс-БАШЕРА. Итак, BASH ДЕЙСТВИТЕЛЬНО свободен в отношении экранирования аргументов и того, как работают пробелы, общая идиома в bash состоит в том, чтобы создавать команды как: mv "$src" "$dest", на случай, если $src или $dest содержат пробелы. zsh и подобные им справляются с этим, но BASH нет. Вот как я писал сценарии оболочки.

Это не то, как Windows PowerShell работает (очевидно). PS делает действительно хорошую работу, полностью скрывая свои переменные и параметры команд. Со мной здесь происходило следующее: когда я устанавливал var: $timeFlag2 = "-ss 1" и передавал это, PS буквально передавал: "-ss 1" базовой команде, независимо от того, как я от нее ускользнул или что я сделал. Это не сразу очевидно с ошибками, которые вы получаете обратно, если вы не понимаете, какие ошибки команды экранирования могут привести к (Invalid flag with name: "$THIS_IS_CLEARLY_A_VALID_FLAG"), а какие нет.

Правильный способ сделать это - разделить флаги объединяются в две переменные: $timePiece1 = "-ss" и $timePiece2 = "1", а затем вызывают команду, используя обе. Еще лучше splatting , который похож на расширение пространства BASH, но НАМНОГО умнее. Вот чем закончилось основное исправление:

$FFMPEG_PARAMS = @(
  "-ss",
  $time,
  "-i",
  $_input,
  $time2Flag,
  $time2Val,
  "-filter_complex",
  "[0:a:0]volume=0.6[l];[0:a:1]volume=1.2[k];[k][l]amerge=inputs=2[a]",
  "-map",
  "0:v:0",
  "-map",
  "[a]",
  "-c:v",
  "copy",
  "-c:a",
  "libmp3lame",
  "-q:a",
  "3",
  "-ac",
  "2",
  $d,
  $duration,
  "output-muxxed.mp4"
)

C:\ffmpeg.exe @FFMPEG_PARAMS

Разрушение параметров и использование splatting решило эту проблему! Вот полный сценарий для справки:

function IsNull($objectToCheck) {
    if ($objectToCheck -eq $null) {
        return $true
    }

    if ($objectToCheck -is [String] -and $objectToCheck -eq [String]::Empty) {
        return $true
    }

    if ($objectToCheck -is [DBNull] -or $objectToCheck -is [System.Management.Automation.Language.NullString]) {
        return $true
    }

    return $false
}

$_input = [Management.Automation.WildcardPattern]::Escape($args[0])
$time = $args[1]
$duration = $args[2]
if ($time -match ":") {
    $_matches = ($time | Select-String ":" -AllMatches).Matches
    $seconds = $time.Substring($_matches[$_matches.Count - 1].Index + 1, 2) -as [int]
    $finalSeconds = $seconds - 1
    $time2Flag = "-ss"
    $time2Val = "1"
    $time = $time.Substring(0, $_matches[$_matches.Count - 1].Index) + (":{0:d2}" -f $finalSeconds)
}
else {
    $time2Flag = ""
    $time2Val = ""
}

if (-not (IsNull($duration))) {
    $d = "-t"
}
else {
    $d = ""
}

$FFMPEG_PARAMS = @(
  "-ss",
  $time,
  "-i",
  $_input,
  $time2Flag,
  $time2Val,
  "-filter_complex",
  "[0:a:0]volume=0.6[l];[0:a:1]volume=1.2[k];[k][l]amerge=inputs=2[a]",
  "-map",
  "0:v:0",
  "-map",
  "[a]",
  "-c:v",
  "copy",
  "-c:a",
  "libmp3lame",
  "-q:a",
  "3",
  "-ac",
  "2",
  $d,
  $duration,
  "output-muxxed.mp4"
)
Write-Verbose "C:\ffmpeg.exe -ss $time -i $_input $time2Flag $time2Val -filter_complex '[0:a:0]volume=0.6[l];[0:a:1]volume=1.2[k];[k][l]amerge=inputs=2[a]' -map '0:v:0' -map '[a]' -c:v copy -c:a libmp3lame -q:a 3 -ac 2 $d $duration 'output-muxxed.mp4'" -Verbose

C:\ffmpeg.exe @FFMPEG_PARAMS

Сегодня я выучил ценный урок PS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...