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 функция их не сломала. Единственная проблема, которую я нашел до сих пор, - это запустить эхо, которое ломается, но я все равно не планирую проверять свои Эхо.

626   5  

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_COMMAND var также был доступен (и желаемое значение) в 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

    Ничего не найдено.