Какой удобный способ запустить длинный список команд и показать сообщение, если что-то пойдет не так?

Итак, спустя четыре года у меня наконец есть набросок ответа на этот вопрос. Ответ заключается в использовании инструментов перехвата , которые используют libevdev для захвата ввода с клавиатуры и обработки его перед повторной -отправкой. Что касается одновременного сопоставления клавиш,Я сделал примерный плагин для Interception Tools под названием Chorded Keymap , который позволяет h , j , k , l . ] действуют как клавиши со стрелками, когда s и d нажаты одновременно.

34
02.01.2020, 14:40
8 ответов

Вы можете сделать что-то вроде этого:

$ for f in command_1 command_2 command_3 command_4
do
  $f
  rc=$?
  if [[ 0 != $rc ]]; then echo Failed command: ${f}; break; fi
done

Это предполагает, что у команд нет опций. Если они есть, вам нужно заключить каждую команду/опцию в кавычки.

3
27.01.2020, 19:36

Думаю, это работает только для bash, но вы можете попробовать:

set -o xtrace
set -o errexit

Или, если хотите быть кратким,

set -ex

Это сделает две вещи::errexit(-e )прервет сценарий при возникновении ошибки, аxtrace(-x )выведет каждую команду непосредственно перед ее выполнением bash, так что в случае сбоя вы будете знать именно то, что он выполнял.

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

  • Пока мы на этом, обычно рекомендуется также включить set -o pipefail:, иначе, если вы запустите foo | bar, сбой fooбудет молча проигнорирован. (Предупреждение :Он слегка изменяет "код выхода" оператора канала, поэтому используйте его с осторожностью, если вы изменяете чужой скрипт. Кроме того, иногда вы не заботитесь о сбое foo, поэтому очевидно, что вы не можете использовать pipefailв таком случае.)
16
27.01.2020, 19:36

Хотите необычное решение? Если у вас установлено make, вы можете поместить список команд в Makefileдля выполнения make. Дополнительным преимуществом :является то, что вам не нужно проверять наличие ошибок. makeавтоматически проверяет возвращаемое значение каждой команды. Если он не равен -нулю, рецепт прерывается с ошибкой. Если вы хотите игнорировать ошибки с определенными командами, свяжите их с помощью || true.

Образец Makefile:

.PHONY: all
.SILENT:

all:
    echo "Started list of commands."
    true
    echo "Executing a command which will fail, but I want to ignore failure."
    false || true
    echo "Executing a command which will definitely fail."
    false
    echo "This code will not be reached."

Примечание. :Убедитесь, что (хе )делает отступ для ваших команд, как указано выше, но с табуляцией.

20
27.01.2020, 19:36

Здесь я не согласен почти со всем. Начиная с «удобно» и «длинного списка команд».Это все равно, что просить «хороший способ программирования».

ОП, кажется, объясняет, что ему нужен поток управления . Не дает представления, что за (спец? сложный? )команды это может быть.

Это:

command_1;
command_2;
command_3;

либо

command_1; command_2; command_3;

или

 command_1
 command_2
 command_3

Здесь вы видите «удобство I» :самый простой -до -чтение (длинный )список команд.


Я не понимаю:

...some commands will fail, and I will have no idea, which commands have failed.

Этот скрипт:

ls
ls NOSUCH >/dev/null
ls -l >/dev/null && echo "long ls OK"

дает эти строки:

cl.sh
ls: cannot access 'NOSUCH': No such file or directory
long ls OK

Несмотря на то, что вторая команда "тихая", я вижу ошибку, и причина почему. Перенаправление stderrне требуется, оно уже хорошо направлено.

Третья строка показывает, что команда и сообщение не совпадают.


set -e— хороший ответ, но это жесткое специальное решение.


Я хочу сказать, что :если кто-то хочет заново изобрести колесо (или изобрести растворимую воду ):, вперед. Но так и скажите, и не говорите о каком-то загадочном «длинном списке команд»

.

I'd rather create a script...and run it once.

...для экономии времени! Я не думаю, что команды руководства по Linux можно назвать «длинным списком команд», которые нуждаются в автоматизации.

-1
27.01.2020, 19:36

Используйте это решение, если хотите сделать это в интерактивной оболочке:

set -x; command_1 && command_2 && command_3 && echo "Everything OK" || echo "Error while executing last command"; set +x

Вывод при успешном выполнении всех команд:

+ command_1
output of command 1
+ command_2
output of command 2
+ command_3
output of command 3
+ echo "Everything OK"
Everything OK
+ set +x

Вывод при отказе command_2:

+ command_1
output of command 1
+ command_2
output of command 2
+ echo "Error while executing last command"
+ set +x

Команда set -xвключает протоколирование всех выполненных команд. Записанные команды имеют префикс с одним или несколькими плюсами (. Стиль можно изменить с помощью переменной$PS4). set +xотключает этот режим.

Вы должны поместить set -xи set +xв одну строку, потому что set -xпозволяет протоколировать все выполненные команды, включая $COMMAND_PROMPTи команды, выполняемые ею.


Вы также можете использовать его для сценариев оболочки:

#!/bin/bash -ex

command_1
command_2
command_3

Параметры оболочки могут быть встроены в строку shebang. -eзавершает работу оболочки после первой неудачной команды (, например, когда все строки заканчиваются на&&). -xвключает протоколирование команд. Обратите внимание, что интерпретатору можно передать только один аргумент. Используйте /usr/bin/envдля передачи нескольких аргументов (#!/usr/bin/env -S bash -ex arg1 arg2 argN).

3
27.01.2020, 19:36

Возможно, просто функция того, что вы уже делаете:

function run_cmd
    ( $* && echo OK $* || ( echo FAILED $*; false ) )

run_cmd echo 1 &&
run_cmd false 2 &&
run_cmd echo 3 &&
echo DONE || echo FAILED

Результатом этого является:

1
OK echo 1
FAILED false 2
FAILED
1
27.01.2020, 19:36

Один из вариантов — поместить команды в скрипт bash и запустить его с помощью set -e.

Это приведет к досрочному завершению сценария, если какая-либо команда завершается с ненулевым -статусом выхода.

См. также этот вопрос о переполнении стека:https://stackoverflow.com/q/19622198/828193

Чтобы распечатать ошибку, вы можете использовать

trap 'do_something' ERR

Где do_something— это команда, которую вы должны создать для отображения ошибки.

Вот пример скрипта, чтобы увидеть, как он работает:

#!/bin/bash

set -e
trap 'echo "******* FAILED *******" 1>&2' ERR

echo 'Command that succeeds'   # this command works
ls non_existent_file           # this should fail
echo 'Unreachable command'     # and this is never called
                               # due to set -e

А это результат:

$./test.sh 
Command that succeeds
ls: cannot access 'non_existent_file': No such file or directory
******* FAILED *******

Кроме того, как упоминал @jick , имейте в виду, что статус выхода конвейера по умолчанию является статусом выхода final команды в нем. Это означает, что если не -final команда в конвейере завершится ошибкой, она не будет перехвачена set -e. Чтобы решить эту проблему, если она вас беспокоит, вы можете использоватьset -o pipefail


Как предложили мои @glenn jackman и @Monty Harder , использование функции в качестве обработчика может сделать сценарий более читабельным, поскольку он позволяет избежать вложенных кавычек. Поскольку сейчас мы все равно используем функцию, я полностью удалил set -eи использовал exit 1в обработчике, что также может сделать его более читаемым для некоторых:

#!/bin/bash

error_handler() {
  echo "******* FAILED *******" 1>&2
  exit 1
}

trap error_handler ERR

echo 'Command that succeeds'   # this command works
ls non_existent_file           # this should fail
echo 'Unreachable command'     # and this is never called
                               # due to the exit in the handler

Вывод идентичен приведенному выше, хотя статус выхода сценария отличается.

62
27.01.2020, 19:36
failed=0
try(){
(( failed )) && return
"$@" 
ec=$?
if (( ec ))
then
  failed=$ec
  echo "failed($failed): $*" >&2
fi
}

try something args
try other-thing more args

if (( failed )) 
then
  echo "something went wrong: see above." >&2
  exit 1
fi
3
27.01.2020, 19:36

Теги

Похожие вопросы