BASH: повторение последнего запуска команды
Я пытаюсь повторить последнюю команду, выполненную внутри сценария bash. Я нашел способ сделать это с некоторым history,tail,head,sed который отлично работает, когда команды представляют определенную строку в моем скрипте с точки зрения парсера. Однако при некоторых обстоятельствах я не получаю ожидаемый результат, например, когда команда вставлена в case о себе:
сценарий:
#!/bin/bash
set -o history
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"
case "1" in
"1")
date
last=$(echo `history |tail -n2 |head -n1` | sed 's/[0-9]* //')
echo "last command is [$last]"
;;
esac
вывод:
Tue May 24 12:36:04 CEST 2011
last command is [date]
Tue May 24 12:36:04 CEST 2011
last command is [echo "last command is [$last]"]
[Q] может кто-нибудь помочь мне найти способ Эхо последней команды запуска независимо от того, как/где эта команда вызывается в сценарии bash?
мой ответ:
несмотря на очень ценный вклад моих коллег SO'ERS, я решил написать run функция-которая запускает все свои параметры как одну команду и отображает команду и ее код ошибки при сбое - со следующими преимуществами:
- Мне нужно только добавить команды, которые я хочу проверить с run что держит их на одной строке и не влияет на лаконичность моего скрипта
- Всякий раз, когда сценарий терпит неудачу на одной из этих команд, последняя выходная строка моего сценария-это сообщение, которое четко отображает, какая команда терпит неудачу вместе с ее кодом выхода, что упрощает отладку
пример:
#!/bin/bash
die() { echo >&2 -e "nERROR: $@n"; exit 1; }
run() { "$@"; code=$?; [ $code -ne 0 ] && die "command [$*] failed with error code $code"; }
case "1" in
"1")
run ls /opt
run ls /wrong-dir
;;
esac
вывод:
$ ./test.sh
apacheds google iptables
ls: cannot access /wrong-dir: No such file or directory
ERROR: command [ls /wrong-dir] failed with error code 2
Я тестировал различные команды с несколькими аргументами, переменные bash в качестве аргументов, цитируемые аргументы... а то run функция их не сломала. Единственная проблема, которую я нашел до сих пор, - это запустить эхо, которое ломается, но я все равно не планирую проверять свои Эхо.
5 ответов:
история команд-это интерактивная функция. В историю вводятся только полные команды. Например,
caseконструкция вводится в целом, когда оболочка закончила ее разбор. Ни один из них не просматривает историю сhistoryвстроенный (ни печать его через расширение оболочки (!:p)) делает то, что вы, кажется, хотите, что печатать вызовы простых команд.The
DEBUGловушка позволяет выполнить команду прямо перед любым простое выполнение команд. Строковая версия команды для выполнения (со словами, разделенными пробелами) доступна вBASH_COMMANDпеременной.trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG … echo "last command is $previous_command"отметим, что
previous_commandбудет меняться каждый раз, когда вы запускаете команду, поэтому сохраните ее в переменную, чтобы использовать ее. Если вы также хотите узнать статус возврата предыдущей команды, сохраните их в одной команде.cmd=$previous_command ret=$? if [ $ret -ne 0 ]; then echo "$cmd failed with error code $ret"; fiкроме того, если вы хотите прервать только неудачные команды, используйте
set -eчтобы завершить работу скрипта с первой неудачной командой. Вы можете отобразить последнюю команду изEXITловушка.set -e trap 'echo "exit $? due to $previous_command"' EXITобратите внимание, что если вы пытаетесь отследить свой скрипт, чтобы увидеть, что он делает, забудьте все это и используйте
set -x.
Bash имеет встроенные функции для доступа к последней выполненной команде. Но это последняя целая команда (например, целое ), а не отдельные простые команды, как вы изначально просили.
!:0= название команды.
!:1= первый параметр предыдущей команды
!:*= все параметры предыдущей команды
!:-1= последний параметр предыдущей команда
!!= предыдущая командная строкаetc.
Итак, самый простой ответ на вопрос, собственно:
echo !!...альтернативно:
echo "Last command run was ["!:0"] with arguments ["!:*"]"попробуйте сами!
echo this is a test echo !!в скрипте расширение истории по умолчанию отключено, вам нужно включить его с помощью
set -o history -o histexpand
после прочтения ответ С Жиль, я решил посмотреть, если
$BASH_COMMANDvar также был доступен (и желаемое значение) вEXITловушка - и это!Итак, следующий скрипт bash работает так, как ожидалось:
#!/bin/bash exit_trap () { local lc="$BASH_COMMAND" rc=$? echo "Command [$lc] exited with code [$rc]" } trap exit_trap EXIT set -e echo "foo" false 12345 echo "bar"выход
foo Command [false 12345] exited with code [1]
barникогда не печатается, потому чтоset -eвызывает bash для выхода из сценария, когда команда терпит неудачу и ложная команда всегда терпит неудачу (по определению). Элемент12345перешел кfalseпросто там, чтобы показать, что аргументы неудачной команды также захвачены (falseкоманда игнорирует любые переданные ей аргументы)
я смог добиться этого с помощью
set -xв главном скрипте (который заставляет скрипт распечатывать каждую выполняемую команду) и писать сценарий-оболочку, который просто показывает последнюю строку вывода, сгенерированнуюset -x.Это основной скрипт:
#!/bin/bash set -x echo some command here echo last commandи это скрипт:
#!/bin/sh ./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //'запуск сценария-оболочки приводит к получению следующего вывода:
echo last command
между последней командой ($_) и последней ошибкой ( $?) переменные параметры. Если вы попытаетесь сохранить один из них в собственной переменной, оба уже столкнулись с новыми значениями из-за команды set. На самом деле, последняя команда не имеет никакого значения вообще в этом случае.
вот что я сделал, чтобы сохранить (почти) обе информации в собственных переменных, поэтому мой скрипт bash может определить, была ли какая-либо ошибка и установить заголовок с последним запуском команда:
# This construct is needed, because of a racecondition when trying to obtain # both of last command and error. With this the information of last error is # implied by the corresponding case while command is retrieved. if [[ "${?}" == 0 && "${_}" != "" ]] ; then # Last command MUST be retrieved first. LASTCOMMAND="${_}" ; RETURNSTATUS='✓' ; elif [[ "${?}" == 0 && "${_}" == "" ]] ; then LASTCOMMAND='unknown' ; RETURNSTATUS='✓' ; elif [[ "${?}" != 0 && "${_}" != "" ]] ; then # Last command MUST be retrieved first. LASTCOMMAND="${_}" ; RETURNSTATUS='✗' ; # Fixme: "$?" not changing state until command executed. elif [[ "${?}" != 0 && "${_}" == "" ]] ; then LASTCOMMAND='unknown' ; RETURNSTATUS='✗' ; # Fixme: "$?" not changing state until command executed. fiэтот скрипт сохранит информацию, если произошла ошибка и получит последнюю команду запуска. Из-за racecondition я не могу сохранить фактическое значение. Кроме того, большинство команд на самом деле даже не заботятся об ошибках noumbers, они просто возвращают что-то отличное от "0". Вы заметите это, если вы используете расширение errono bash.
это должно быть возможно с чем-то вроде скрипта" стажер " для bash, например, в расширении bash, но я не знакомый с чем-то подобным, и он также не будет совместим.
коррекция
Я не думал, что можно получить обе переменные одновременно. Хотя мне нравится стиль кода, я предполагал, что он будет интерпретироваться как две команды. Это было неправильно, поэтому мой ответ сводится к:
# Because of a racecondition, both MUST be retrieved at the same time. declare RETURNSTATUS="${?}" LASTCOMMAND="${_}" ; if [[ "${RETURNSTATUS}" == 0 ]] ; then declare RETURNSYMBOL='✓' ; else declare RETURNSYMBOL='✗' ; fiхотя мой пост может не получить никакого положительного рейтинга, я решил свою проблему сам, наконец. И это кажется уместным в отношении первоначальный пост. :)
Comments