Powershell Запись на консоль и вывод

Я хотел бы писать в консоль [хост], а также отправлять разрешенное значение на выходные каналы при любых обстоятельствах.

Желаемый результат:

==== FOOBAR ====
Input: <input>

<output>
==== END OF FOOBAR ====

Самое близкое, что я получил к решению, следующее:

function Write-OutAndReturn {

    param(
        [Parameter(ValueFromPipeline = $true)]
        $Value
    )

    process {
        $ConsoleDevice = if ($IsWindows) { '\\.\CON' } else { '/dev/tty' }
        
        Write-Host "====== SECTION ===== = "

        Write-Host "Input: " -NoNewline
        Write-Host $MyInvocation.BoundParameters.Value

        $(if ($Value -is [scriptblock]) {
            $Value.Invoke()
        } else { $Value }) | Tee-Object -FilePath $ConsoleDevice

        Write-Host "====== END SECTION ===== = "
    }

}

Где выход $Result = Write-OutAndReturn { 1 + 1 } равен:

====== SECTION ======
Input:  1 + 1
2
====== END SECTION ======

Что желательно; и $Result теперь содержит 2

Однако, когда функция вызывается только (Write-OutAndReturn { 1 + 1 }), она генерирует следующее:

====== SECTION ======
Input:  1 + 1
2
2
====== END SECTION ======

Где второй 2 нежелателен

Есть ли более семантический способ справиться с этим поведением или обеспечить соблюдение краткости?

В качестве отступления: лучше избегать использования метода .Invoke() для выполнения блока скрипта в коде PowerShell, поскольку он меняет семантику вызова в нескольких отношениях. Вместо этого используйте & $scriptBlock [arg1 ...]. См. этот ответ для получения дополнительной информации.

mklement0 04.06.2023 19:13
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
123
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В вашем примере есть 3 возможных выхода:

все в конвейере можно передать другому командлету или захватить, назначив его переменной (как: $Result = Write-OutAndReturn { 1 + 1 })

Write-Host ────────> Console <═╕<─┐
                ┌──> File      │  │
Tee-Object ═════╡    (or CON) ─┘  │ not processed
                │                 │
(Write-Output) ─┴──> Pipeline ────┘

Путаница возникает из-за функции Write-Output (или неявного вывода PowerShell):

Если Write-Output является последней командой в конвейере, объекты отображаются в консоли.

Это означает, что все в конвейере, которое не захвачено в переменной или передано другому командлету, будет действовать так же, как если бы вы выполнили write-host. Чтобы захватить все, что в данный момент пишется в консоль, вам потребуется удалить командлет write-host (или заменить их на Write-Output).

В вашем примере, если $IsWindows равно true, Tee-Object дважды выведет на консоль:

  • явно для параметра -Filepath'\\.\CON'
  • и неявный по замыслу (как на чертеже)

Спасибо за быстрый ответ iRon. Возможно, я неправильно понимаю, основываясь на том, что я прочитал в документах, Tee-Object stores the output in a file or variable and also sends it down the pipeline; разве это не делает его явным и не отрицает триггер «последнего оператора»? Пример желаемого вывода чрезмерно упрощен, в конечном итоге будет другой поток управления для регистрации информации помимо этого. Замена только на выходе нежелательна. $Result не нуждается в этой информации. В идеале эхо будет происходить всегда, но использование разрешенного значения может не срабатывать.

soulshined 03.06.2023 11:36

@soulshined - любой незахваченный вывод будет возвращен в родительскую область в конвейере, и если он не захватит его, он будет передан родительской области этой области и т. д. Если он достигнет области верхнего уровня и не будет захвачен. будет передано в Out-Default, который обычно отображается на консоли. Если вы не хотите, чтобы выходной поток каскадировался вверх, вам нужно как-то его захватить. Некоторые варианты $null = … и … | Out-Null. Если вы хотите подавить вывод конвейера на консоль из вызова верхнего уровня Write-OutAndReturn, вам нужно как-то его захватить.

mclayton 03.06.2023 11:44

Хорошо понял! Это имеет смысл спасибо. Я полагаю, что нет способа условно проверить это внутри функции? По крайней мере, я ничего не могу быстро найти. Это не было бы односторонним Out-Null, просто если бы оно не было захвачено. И просто чтобы уточнить, я не хочу подавлять какие-либо консольные эхо внутри функции, я всегда хочу, чтобы они повторяли эти операторы, но я не всегда хочу использовать выходную переменную

soulshined 03.06.2023 11:49

@iron - в качестве теста я только что попробовал $null = 1..100 | write-output и ничего не записал в консоль, поэтому в документах говорится: «Если Write-Output является последней командой в конвейере, объекты отображаются в консоли». может быть, просто плохо сформулировано или я читаю это вне контекста, но это не так, как безоговорочное отдельное утверждение…

mclayton 03.06.2023 11:58

@soulshined - функция не может узнать, будет ли ее вывод захвачен вызывающим абонентом. Если известно, что функция отправляет выходные данные конвейера, а вызывающая сторона хочет предотвратить их всплытие, то это собственная работа вызывающей стороны, которая должна обрабатывать вывод путем захвата вывода…

mclayton 03.06.2023 12:02

@mclayton, ты прав. Я просто цитирую документ Write-Output здесь: Writes the specified objects to the pipeline. If Write-Output is the last command in the pipeline, the objects are displayed in the console. (когда я найду время, я верну это)

iRon 03.06.2023 12:02

Я полагаю, что я прошу эффективно эмулировать -PassThru, за исключением того, что всегда правильно регистрируйте операторы записи-хоста? Но на самом деле это невозможно в данном контексте, насколько я понимаю ваш отзыв? Просто нет способа Write-Output $Value (неявно или нет) только один раз в зависимости от того, захвачено оно или нет.

soulshined 03.06.2023 12:07

@soulshined — предположим, вы нашли способ определить, захвачены ли выходные данные или нет на месте вызова. Теперь проблема в том, как узнать, что намерение вызывающего абонента не состоит в том, чтобы намеренно оставить его незахваченным, потому что он хочет передать вывод родительскому конвейеру? Вы можете подавить вывод своей функции, когда вызывающая сторона действительно этого хотела...

mclayton 03.06.2023 12:21

справедливое замечание @mclayton, вероятно, не решение для будущего. Я думаю, что в конечном итоге я просто добавлю переключатель -PassThru и позволю вызывающему делегировать. Не конец света просто хотел, чтобы это был запрос «все в одном». Большое спасибо за ваше руководство, ребята!

soulshined 03.06.2023 12:24
Ответ принят как подходящий

Вместо

$(if ($Value -is [scriptblock]) {
            $Value.Invoke()
        } else { $Value }) | Tee-Object -FilePath $ConsoleDevice

Вы могли бы попробовать

$result = if ($Value -is [scriptblock]) { $Value.Invoke() } else { $Value }
Write-Host $result
$result

который в основном является бедняком Tee-Object, который отправляет значение хосту и выходному потоку.

Это означает, что вы можете увидеть $result на консоли во второй раз из выходного потока, если вы не захватите возвращаемое значение из Write-OutAndReturn, но это в значительной степени то, что вы ожидаете от незахваченных значений, которые достигают корневой области (сценария)…

Чтобы решить эту проблему, вы могли бы потенциально сделать строку $result необязательной (и «отключенной» по умолчанию) и добавить переключатель -PassThru, когда вызывающая сторона действительно хочет, чтобы значение было отправлено в выходной поток…

$result = if ($Value -is [scriptblock]) { $Value.Invoke() } else { $Value }
Write-Host $result
if ( $PassThru ) { $result }

Другие вопросы по теме