Как создать функцию, которая запускает команду? - PullRequest
0 голосов
/ 18 декабря 2018

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

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

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

try doSomething -args

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

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

Я нашел $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

1 Ответ

0 голосов
/ 18 декабря 2018

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

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 вызывает команду, используя оператор вызова & при разбрызгивании параметров. Do NOT use Invoke-Expression.

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

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

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

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

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

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

...