Итак, спустя четыре года у меня наконец есть набросок ответа на этот вопрос. Ответ заключается в использовании инструментов перехвата , которые используют libevdev для захвата ввода с клавиатуры и обработки его перед повторной -отправкой. Что касается одновременного сопоставления клавиш,Я сделал примерный плагин для Interception Tools под названием Chorded Keymap , который позволяет h , j , k , l . ] действуют как клавиши со стрелками, когда s и d нажаты одновременно.
Вы можете сделать что-то вроде этого:
$ 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
Это предполагает, что у команд нет опций. Если они есть, вам нужно заключить каждую команду/опцию в кавычки.
Думаю, это работает только для bash, но вы можете попробовать:
set -o xtrace
set -o errexit
Или, если хотите быть кратким,
set -ex
Это сделает две вещи::errexit
(-e )прервет сценарий при возникновении ошибки, аxtrace
(-x )выведет каждую команду непосредственно перед ее выполнением bash, так что в случае сбоя вы будете знать именно то, что он выполнял.
Недостатком является то, что вывод будет загроможден, но если вас это устраивает, это довольно хорошее решение с минимальными затратами.
set -o pipefail
:, иначе, если вы запустите foo | bar
, сбой foo
будет молча проигнорирован. (Предупреждение :Он слегка изменяет "код выхода" оператора канала, поэтому используйте его с осторожностью, если вы изменяете чужой скрипт. Кроме того, иногда вы не заботитесь о сбое foo
, поэтому очевидно, что вы не можете использовать pipefail
в таком случае.)Хотите необычное решение? Если у вас установлено 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."
Примечание. :Убедитесь, что (хе )делает отступ для ваших команд, как указано выше, но с табуляцией.
Здесь я не согласен почти со всем. Начиная с «удобно» и «длинного списка команд».Это все равно, что просить «хороший способ программирования».
ОП, кажется, объясняет, что ему нужен поток управления . Не дает представления, что за (спец? сложный? )команды это может быть.
Это:
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 можно назвать «длинным списком команд», которые нуждаются в автоматизации.
Используйте это решение, если хотите сделать это в интерактивной оболочке:
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
).
Возможно, просто функция того, что вы уже делаете:
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
Один из вариантов — поместить команды в скрипт 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
Вывод идентичен приведенному выше, хотя статус выхода сценария отличается.
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