Случайные разрывы строк в стандартном выводе ошибок PowerShell

Я хочу конвертировать многие файлы.iso в.mp4 с помощью HandBrake, поэтому я пытаюсь использовать интерфейс командной строки. Я бы предпочел написать свои сценарии для этого в powershell вместо командных файлов. Тем не менее, стандартная ошибка содержит разрывы строк в случайном месте, если я использую PowerShell.

Для устранения неполадок я создал упрощенный скрипт как в powershell, так и в пакетном режиме.

Powershell:

& "$Env:ProgramFiles\HandBrake\HandBrakeCLI.exe" @(
    '--input', 'V:\',
    '--title', '1', '--chapter', '1',
    '--start-at', 'duration:110', '--stop-at', 'duration:15',
    '--output', 'pmovie.mp4',
    '--format', 'av_mp4'
    ) > ".\pstd.txt" 2> ".\perr.txt"

Пакетный файл:

"%ProgramFiles%\HandBrake\HandBrakeCLI.exe" --input V:\ --title 1 --chapter 1 --start-at duration:110 --stop-at duration:15 --output ".\cmovie.mp4" --format av_mp4 > ".\cstd.txt" 2> ".\cerr.txt"

Оба сценария создают один и тот же файл.mp4, разница только в том, что они создают стандартный вывод ошибок:

Powershell:

HandBrakeCLI.exe : [10:41:44] hb_init: starting libhb thread
At C:\Test\phandbrake.ps1:1 char:2
+ & <<<<  "$Env:ProgramFiles\HandBrake\HandBrakeCLI.exe" @(
    + CategoryInfo          : NotSpecified: ([10:41:44] hb_i...ng libhb thread 
   :String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

[10:41:44] thread 541fc20 started ("libhb")
HandBrake 1.1.2 (2018090500) - MinGW x86_64 - https://handbrake.fr
8 CPUs detected

O
pening V:\...

[10:41:44] CPU: Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz

[10:41:44]  - Intel microarchitecture Sandy Bridge
[10:41:44]  - logical processor count: 8

[10:41:44] Intel Quick Sync Video support: no

[10:41:44] hb_scan: path=V:\, title_index=1

src/libbluray/disc/disc.c:424: error opening file BDMV\index.bdmv

src/libbluray/disc/disc.c:424: error opening file BDMV\BACKUP\index.bdmv

[10:41:44] bd: not a bd - trying as a stream/file instead

libdvdnav: Using dvdnav version 6.0.0

l
ibdvdnav: Unable to open device file V:\.
libdvdnav: vm: dvd_read_name failed
libdvdnav: DVD disk re
ports i
tself wi
th Region mask 0x
0000000
0. Reg
ions:
 1 2 3 4 5 
6 7 8

Пакетный файл:

[10:41:35] hb_init: starting libhb thread
[10:41:35] thread 5a2cc30 started ("libhb")
HandBrake 1.1.2 (2018090500) - MinGW x86_64 - https://handbrake.fr
8 CPUs detected
Opening V:\...
[10:41:35] CPU: Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz
[10:41:35]  - Intel microarchitecture Sandy Bridge
[10:41:35]  - logical processor count: 8
[10:41:35] Intel Quick Sync Video support: no
[10:41:35] hb_scan: path=V:\, title_index=1
src/libbluray/disc/disc.c:424: error opening file BDMV\index.bdmv
src/libbluray/disc/disc.c:424: error opening file BDMV\BACKUP\index.bdmv
[10:41:35] bd: not a bd - trying as a stream/file instead
libdvdnav: Using dvdnav version 6.0.0
libdvdnav: Unable to open device file V:\.
libdvdnav: vm: dvd_read_name failed
libdvdnav: DVD disk reports itself with Region mask 0x00000000. Regions: 1 2 3 4 5 6 7 8

libdvdread: Attempting to retrieve all CSS keys
libdvdread: This can take a _long_ time, please be patient

libdvdread: Get key for /VIDEO_TS/VIDEO_TS.VOB at 0x00000130
libdvdread: Elapsed time 0

Это беспокоит меня, потому что я хотел бы проверить эти текстовые файлы, чтобы убедиться, что во время кодирования не было ошибок.

Я полагаю, это может быть связано с отсутствием синхронизации между потоками, которые записывают в один поток, но я не уверен в этом.

Вопрос: что я могу сделать, чтобы получить стандартный вывод ошибок от PowerShell без этих случайных разрывов строк?

3 ответа

Решение

Вы можете попробовать Start-Process команда, с -RedirectStandardError,
-RedirectStandardInput, а также -Wait опции.

Эти -Redirect... варианты на Start-Process Выполняйте перенаправление ввода-вывода на уровне операционной системы непосредственно в целевой файл, как это делает большинство оболочек. Насколько я понимаю, переадресация угловых скобок PowerShell работает не так, вместо этого они направляют выходные данные через другой конвейер PowerShell, используя Write-File (или что-то), который вставляет разрывы строк между строками, которые он получает.

Я не уверен в точных деталях этого, но я рад слышать, что это, кажется, решает проблему для вас так же, как и для меня.

Я думаю, что проблема заключается в том, что консоль имеет определенную ширину, а сама консоль по существу перенаправляется в файл.

Мое решение для этого состоит в том, чтобы перенаправить вывод непосредственно в конвейер, используя:

2>&1 #Interpreted by the console
2>&1 | x #Output directly to x

А потом с помощью Out-File с доступным -Width параметр:

$(throw thisisnotsometthingyoucanthrowbutisinfactaverylongmessagethatdemonstratesmypoint) 2>&1 |
 Out-File "test.txt" -Width 10000

В этом случае powershell напишет 10000 символов перед переносом текста.

Тем не менее, у вас также есть некоторые странные разрывы строк, которые я не могу сейчас воспроизвести. Тем не менее, теперь, когда вы знаете, как отправлять вывод через конвейер, вы можете использовать другие методы для удаления разрывов строк.

Например, вы можете использовать эту функцию, которая печатает точные управляющие символы, которые вызывают разрывы строк.

$(throw error) 2>&1 | Out-String | Debug-String

Затем вы можете просмотреть вывод и заменить проблемные символы, например, так:

$(throw error) 2>&1 | Out-String | % {$_ -replace "`r"} | Out-File "test.txt" -Width 10000

Полезный ответ Берта Харриса показывает вам один из способов избежать проблемы, через Start-Process, что требует, чтобы вы структурировали команду принципиально иначе, однако.

Если вывод, который производит эквивалентный пакетный файл, достаточен, есть более простой способ: просто вызовите cmd /c и разреши cmd обработайте перенаправления вывода, как в вашем пакетном файле:

cmd /c "`"`"$Env:ProgramFiles\HandBrake\HandBrakeCLI.exe`"`"" @(
    '--input', 'V:\',
    '--title', '1', '--chapter', '1',
    '--start-at', 'duration:110', '--stop-at', 'duration:15',
    '--output', 'pmovie.mp4',
    '--format', 'av_mp4'
    ) '> .\pstd.txt 2> .\perr.txt'

Обратите внимание, как два выходных перенаправления передаются как одна строка в кавычках, чтобы гарантировать, что они интерпретируются cmd.exe а не PowerShell.

Также обратите внимание на встроенные двойные кавычки (`") вокруг исполняемого пути, чтобы убедиться, что cmd.exe видит весь путь как одну строку в двойных кавычках.


Что касается дополнительных разрывов строк, которые вы видите: у меня нет конкретных объяснений, но я могу рассказать вам, как > а также 2> работают по-разному в PowerShell - оба по сравнению с cmd.exe (пакетные файлы) и Start-Process с -RedirectStandard*:

  • cmd.exe перенаправление оператора (>) записывает необработанные байты в указанный целевой файл, оба при перенаправлении stdout (просто > или, явно, 1>) и stderr (2>); как таковой, вывод текста внешними программами, такими как HandBrakeCLI.exe передается как есть.

  • Start-Process, который использует.NET API под капотом, делает по существу то же самое, когда -RedirectStandardOutput и / или -RedirectStandardError параметры указаны.

В отличие от собственного Powershell > Оператор функционирует по-разному:

  • PowerShell - внутренне (при вызове собственных команд PowerShell) он преобразует входные объекты (которые еще не являются строками) в строки, используя систему форматирования выходных данных PowerShell, перед отправкой их в выходные файлы, используя кодировку символов, подробно описанную ниже.

  • Предполагается, что выходные данные, полученные из внешних программ, представляют собой текст, кодировка которого по умолчанию считается кодировкой OEM-символов системы, как это отражено в [console]::OutputEncoding а также chcp, Декодированный текст загружается в строки.NET (которые по сути основаны на UTF-16) построчно.

    • Для перенаправленного вывода stdout эти строки перекодируются при выводе в целевой файл, используя следующую кодировку по умолчанию:

      • Windows PowerShell: UTF-16LE ("Юникод")
      • PowerShell Core: UTF-8 без спецификации
      • Примечание. Эти значения по умолчанию можно изменить только в Windows PowerShell v5.1 или более поздней версии и PowerShell Core. Дополнительные сведения см. В этом ответе.
    • Напротив, при перенаправлении вывода stderr через поток 2 (Поток ошибок PowerShell), строки заключены в объекты ошибок (экземпляры типа [System.Management.Automation.ErrorRecord]) перед выводом, и полученные объекты преобразуются в строки на основе системы форматирования вывода PowerShell, и при выводе в целевой файл применяется та же кодировка символов, что и выше.

      • Вы можете увидеть доказательства этого в своем выводе, содержащем дополнительную информацию и такие строки, как HandBrakeCLI.exe : [10:41:44] hb_init: starting libhb thread а также At C:\Test\phandbrake.ps1:1 char:2...
      • Это также означает, что можно вводить дополнительные разрывы строк, потому что текст, создаваемый системой форматирования вывода, предполагает фиксированную ширину строки, основанную на ширине окна консоли.
      • Тем не менее, это не объясняет странно расположенные разрывы строк в вашем случае.
Другие вопросы по тегам