Недавно (2-3 дня назад) я начал использовать Powershell. У меня есть приложение на C++, которое использует argparse
для обработки некоторых аргументов командной строки. Если что-то не так, как ожидалось, мой код завершается со строкой справки, предоставленной флагом -
/--help
любого приложения, использующего эту библиотеку.
Я использую (вывод $PSVersionTable
):
Name Value
---- -----
PSVersion 5.1.19041.2364
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
BuildVersion 10.0.19041.2364
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
У меня есть файл конфигурации CMake
Write-Host "${APP_NAME} (${BUILD_TYPE})"
$model = ""
$data_in = ""
Write-Host "Checking input arguments"
if ($args.count -ne 5)
{
Write-Host "Found {0} arguments but expecting 5" -f $args.count
Write-Host $(./pytorch_load_model.exe --help) -foregroundcolor Cyan
exit
}
for ( $i = 0; $i -lt $args.count; $i++ ) {
if (($args[ $i ] -eq "-m") -or ($args[ $i ] -eq "--model")){ $model=$args[ $i+1 ]}
if (($args[ $i ] -eq "-i") -or ($args[ $i ] -eq "--input")){ $data_in=$args[ $i+1 ]}
}
$fail = $false
if ($model -eq "")
{
Write-Host "No path for model provided"
}
if ($data_in -eq "")
{
Write-Host "No path for input provided"
}
if ($fail -eq $true)
{
Write-Host (./model.exe --help)
exit
}
# TODO Add check if $opencv and $libtorch valid dir paths
$opencv = "${OPENCV_BIN}"
$libtorch = "${LIBTORCH_BIN}"
#$model = "${PTH_MODEL_PATH}"
#$input = "${INPUT_IMG_PATH}"
Write-Host "Executing model"
$env:Path = "$($env:Path);$($opencv);$($libtorch)"; ./pytorch_load_model.exe -m "$($model)" -i "$($data_in)"
который обрабатывается с помощью configure_file()
для вывода скрипта (*.ps1
файла), который добавляется в двоичный каталог моего проекта (EXE
, dll
s и другие данные, относящиеся к времени выполнения). Он настраивает PATH
так, что приложение работает как положено, а также передает тот же аргумент командной строки, что и мое приложение.
Я заметил, что Write-Host
«съедает» все новые строки, которые появляются в виде многострочной строки из моего приложения.
Вместо
PyTorch Load Model (release)
Checking input arguments
Usage: model [--help] [--version] [--model-path VAR] [--png-path VAR]
Optional arguments:
-h, --help shows help message and exits
-v, --version prints version information and exits
-m, --model-path Path to PTH TorchScript model
-i, --png-path Path to PNG image to be processed by the model
я получаю
Usage: model [--help] [--version] [--model-path VAR] [--png-path VAR] Optional arguments: -h, --help shows help message and exits -v, --version prints version information and exits -m, --model-path Path to PTH TorchScript model -i, --png-path Path to PNG image to be processed by the model
Как сохранить новые строки?
@sirtao Я добавил информацию в ОП.
Направьте вывод на хост записи: (./model.exe --help) | хост записи
Как предполагает @nimizen, лучшим вариантом будет трубопровод: (./model.exe --help) | write-host
В качестве альтернативного решения вы можете использовать ((./model.exe --help) -join "`r`n")
Вместо Write-Host вы можете использовать Write-Output, который просто передает значения в выходной поток. В то время как Write-Host превращает данные только в вывод на экран, и если вы не меняете цвета шрифта, это никогда не понадобится.
Тоже хороший вариант, хотя хотелось бы использовать раскраску. :D
Полезный ответ Сиртао показывает эффективные решения.
Что касается того, что вы пробовали:
Когда PowerShell вызывает внешнюю программу, она построчно передает выходные данные стандартного вывода в конвейер PowerShell.
При заключении такого вызова в (...)
— например, для передачи вывода в качестве аргумента команды, как в этом случае — строки обязательно собираются в массив (типа [object[]]
), при условии, что существует две или более строк.
Write-Host, если в качестве аргумента передан массив, печатает (строчные) элементы массива в одной строке, каждый из которых разделен одним пробелом — вот что вы видели:
Простой пример:
# -> 'foo bar'
Write-Host @('foo', 'bar')
Чтобы добиться многострочного вывода (оба метода показаны в ответе Сиртао):
Либо: предоставьте выходные данные, полученные в результате вызова внешней программы (например, ./model.exe --help
) через конвейер , который заставляет Write-Host
печатать каждый входной объект конвейера в отдельной строке.
Или: Явно соедините строки, выводимые внешними программами, с символами новой строки, чтобы сформировать одну многострочную строку, которая Write-Host
печатается как есть.
В качестве альтернативы решению -join
в ответе Сиртао вы можете использовать командлет Out-String; например.:
Write-Host -NoNewLine (./model.exe --help | Out-String)
-NoNewLine
, которое компенсирует тот досадный факт, что Out-String
добавляет завершающую новую строку к создаваемой многострочной выходной строке; это проблемное поведение обсуждается в выпуске GitHub № 14444.Write-Host
для создания цветного вывода stderr:Write-Host обычно является неправильным инструментом для использования , если только целью не является запись только на дисплей, минуя поток вывода успеха и вместе с ним возможность отправлять вывод другим командам, фиксировать его в переменной, перенаправлять его в файл. Чтобы вывести значение, используйте его отдельно; например, $value
вместо Write-Host $value
(или используйте Write-Output $value
, хотя это требуется редко). См. также: нижнюю часть этого ответа.
Write-Host
вывод, а именно через информационный поток вывода , номер которого равен 6
; например, чтобы перенаправить этот поток в выходной поток успеха, используйте перенаправление 6>&1
Write-Host
— подходящий инструмент для создания интерактивного пользовательского интерфейса, особенно если вы пытаетесь создать цветной вывод с параметрами -ForegroundColor
и -BackgroundColor
, как в вашем случае.
Тем не менее, теперь у вас также есть возможность встраивать escape-последовательности ANSI/VT в строки, что позволяет отправлять их в поток вывода успеха и, следовательно, захватывать или перенаправлять напрямую; например, в PowerShell (Core) 7 , используя привилегированную переменную $PSStyle для облегчения внедрения escape-последовательностей:
# Produces the same *display* output as:
# Write-Host -Foreground Cyan 'This prints in cyan.'
# Unlike the latter, it outputs to the success output stream.
"$($PSStyle.Foreground.Cyan)This prints in cyan.$($PSSTyle.Reset)"
# Windows PowerShell equivalent, where $PSStyle isn't available.
# See the "ANSI/VT escape sequences" link above for details.
"$([char] 27 + '[36m')This prints in cyan.$([char] 27 + '[m')"
[Console]::IsOutputRedirected
(используйте [Console]::IsErrorRedirected
для stderr).Однако в вашем случае, учитывая, что вы указываете состояние ошибки (неожиданное количество аргументов), было бы более целесообразно напечатать сообщение в потоке stderr процесса PowerShell.
Внутренне PowerShell использует свой во многом аналогичный поток, поток вывода ошибок, через Write-Error не является опцией в Windows PowerShell, поскольку любое сообщение, передаваемое в него, всегда будет печататься красным цветом и, по умолчанию, шумно, как многострочное. нить; в PowerShell (Core) 7 вы можете переопределить красный цвет с помощью встроенных escape-последовательностей ANSI/VT, но префикс Write-Error:
все равно будет печататься красным цветом.
Однако, если использование сеанса потока ошибок внутри PowerShell не важно, вы можете использовать [Console]::Error.WriteLine() для печати непосредственно в поток stderr процесса PowerShell (тем самым отказываясь от возможности захвата выходные данные внутри сеанса):
# PowerShell 7:
# Print directly to stderr, with ANSI/VT-sequence coloring:
[Console]::Error.WriteLine(
"$($PSStyle.Foreground.Cyan)This prints in cyan.$($PSSTyle.Reset)"
)
Чтобы продемонстрировать технику условной раскраски, при которой раскраска используется только тогда, когда stderr подключен к терминалу:
# PowerShell 7:
# Print directly to stderr, with ANSI/VT-sequence coloring
# applied only when connected to a terminal.
[Console]::Error.WriteLine(
"$([Console]::IsErrorRedirected ? '' : $PSStyle.Foreground.Cyan)This prints in cyan when connected to a terminal.$([Console]::IsErrorRedirected ? '' : $PSSTyle.Reset)"
)
Отличное объяснение, очень полезно
Рад это слышать, @sirtao; Я ценю приятный отзыв.
вы используете PowerShell 5.1 или 7.x? Скорее всего это кодирование махинаций из приложения cmd.exe в Powershell