Как мне создать функцию, выполняющую команду?

Итак, я пытаюсь воссоздать аналогичную функцию, которую я бы использовал в Bash, но в Powershell:

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "FAILED: $*"; }

На данный момент меня больше всего интересует функция try (). По сути, он позволяет мне обернуть команду этой функцией и позволить ей управлять кодом выхода. Эффект примерно такой:

try doSomething -args

Если doSomething завершается с ненулевым значением, он выводит команду на stderr и останавливает выполнение сценария.

Я понимаю, что в Powershell есть действие при ошибке, которое можно использовать для прерывания скриптов, но похоже, что это применимо только к командлетам. Мне нужно что-то, что я могу использовать во всем на протяжении всего сценария. Я также стараюсь избегать тонны подробной логики try / catch, искажающей сценарий, отсюда и желание чего-то элегантного, например, try / yell / die. Таким образом, я могу написать обработку только в этой функции и просто использовать ее для вызова всего, что я хочу обработать.

Я нашел $MyInvocation и подумал, что это может быть выход, но я не могу найти способ на самом деле выполнить его из функции. Например:

function run() {
    $MyInvocation # ?? what do??
}

run doSomething -args

Я думаю, что смогу разобраться в остальном самостоятельно, я просто не совсем знаю, как написать эту функцию-оболочку. Любые идеи?

Обновлять

Итак, я сделал что-то глупое, выделил команду и сделал Invoke-Expression на том, что осталось, и, похоже, это сработало. Это кажется супер хакерским, поэтому я все еще открыт для идей:

function attempt() {
    $thisCommand = $MyInvocation.Line.Trim()
    Write-Output $thisCommand
    Invoke-Expression $thisCommand.Substring(8)
    if ($LASTEXITCODE -ne 0) { 
        throw "Command failed $thisCommand" 
        exit 111
    }
}

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

Ответы 1

Ответ принят как подходящий

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

function Test-Command {
    try {
        $cmd, $params = $args
        $params = @($params)
        $global:LastExitCode = 0
        $output = & $cmd @params 2>&1
        if ($global:LastExitCode -ne 0) {
            throw $output
        }
        $output
    } catch {
        throw $_
    }
}

Авария:

$cmd, $params = $args принимает автоматическая переменная$args (массив аргументов, переданных функции) и присваивает свой первый элемент переменной $cmd, а остальные - переменной $params.

$params = @($params) гарантирует, что $params содержит массив (необходимый для следующего шага), даже если переменная была пустой или содержала только одно значение.

& $cmd @params вызывает команду, используя оператор вызова&, а брызги - параметры. Использует ли НЕТInvoke-Expression.

оператор перенаправления2>&1 объединяет вывод ошибок с нормальным выводом, так что оба выходных потока фиксируются в переменной $output.

Если $cmd является ошибкой Командлет PowerShell, возникает исключение, которое перехватывается оператором try. Остальной код в блоке try будет пропущен. Однако обратите внимание, что не все ошибки, выдаваемые командлетами PowerShell, автоматически завершают ошибки (см., Например, «Введение в обработку ошибок в PowerShell» в блоге Scripting Guy). Чтобы превратить непрекращающиеся ошибки в прерывающиеся, вам необходимо установить $ErrorActionPreference = 'Stop' (и сбросить его до исходного значения после того, как вы закончите).

Если $cmd является ошибкой внешняя команда, исключения не будут генерироваться, но автоматическая переменная $LastExitCode обновляется кодом выхода команды. Команда, возвращающая ненулевой код выхода, вызовет условие if и вызовет создание настраиваемого исключения с использованием выходных данных команды в качестве сообщения об исключении. Это исключение также перехватывается оператором try. Остальной код в блоке try снова пропускается.

$global:LastExitCode = 0 сбрасывает переменную $LastExitCode перед каждым запуском. Это необходимо, поскольку только внешние команды возвращают код выхода, а командлеты PowerShell - нет. Поскольку $LastExitCode сохраняет код выхода внешней команды, выполненной последним в текущем сеансе, отказ от сброса переменной может помешать обнаружению статуса командлета PowerShell, запущенного после внешней команды.

Последняя строка в блоке try, которая повторяет захваченный вывод команды, достигается только в том случае, если команда не генерирует исключение и не возвращает ненулевой код выхода.

Любое обнаруженное исключение обрабатывается в блоке catch, который просто передает исключение вызывающей стороне функции. Вместо того, чтобы бросать, вы также можете вывести ошибку и, конечно же, выйти.

Когда я тестирую это с Test-Command echo foo, кажется, что команда запускается один раз для каждого символа в "foo", поэтому я получаю 'f' 'o' 'o' каждый в отдельной строке. Кажется, это верно для большинства команд. Например, Test-Command dotnet --info тоже не работает, потому что считает каждое тире аргументом.

Sinaesthetic 19.12.2018 21:55

Полагаю, я ошибаюсь. Требует ли от меня отправки параметров в виде хеш-таблицы?

Sinaesthetic 19.12.2018 22:05

О, я вижу, что это сработает, если я вызову через него функцию PowerShell. Но есть ли способ заставить это работать с другими командами?

Sinaesthetic 19.12.2018 22:26

FWIW ни $global:LastExitCode, ни $LASTEXITCODE, похоже, не работают в PWSH в реальной среде Linux. Вместо этого мне пришлось использовать $?, который возвращает истину / ложь в зависимости от того, был ли последний код выхода успешным.

Sinaesthetic 20.12.2018 19:01

@Sinaesthetic В вашем вопросе не упоминается, что вы собираетесь запускать это в Linux, поэтому я тестировал код только на своем тестовом окне Windows. Пожалуйста, не упускайте подобные подробности из своих вопросов.

Ansgar Wiechers 20.12.2018 19:14

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

Sinaesthetic 20.12.2018 19:21

Итак, я заметил одну вещь: если я отправлю команду cli в функцию, определенную здесь, вывод этой команды не будет выведен на консоль: /

Sinaesthetic 22.01.2019 23:30

У меня отлично сработало (например, Test-Command ping 127.0.0.1).

Ansgar Wiechers 22.01.2019 23:42

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