developer tip

Bash에서 실행 된 마지막 명령을 반향합니까?

copycodes 2020. 10. 25. 12:28
반응형

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하나의 명령으로 모든 매개 변수를 실행하고 명령과 그 실패의 오류 코드를 표시 - - 기능 다음과 같은 혜택을 :
에 -I에만 필요 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했는데 함수가이를 깨뜨리지 않았습니다. 지금까지 내가 찾은 유일한 문제는 깨지는 에코를 실행하는 것이지만 어쨌든 에코를 확인할 계획은 없습니다.


명령 기록은 대화 형 기능입니다. 기록에는 완전한 명령 만 입력됩니다. 예를 들어, case쉘이 구문 분석을 완료 하면 구성이 전체로 입력됩니다. history내장 된 히스토리를 조회 하지도 (쉘 확장을 통해 인쇄하지도 않음 ( !:p)) 단순한 명령 호출을 인쇄하는 것이 원하는대로 수행되지 않습니다.

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에는 실행 된 마지막 명령에 액세스하는 기능이 내장되어 있습니다. 그러나 이것은 case당신이 원래 요청한 것과 같은 개별적인 간단한 명령이 아니라 마지막 전체 명령 (예 : 전체 명령)입니다.

!:0 = 실행 된 명령의 이름.

!:1 = 이전 명령의 첫 번째 매개 변수

!:* = 이전 명령의 모든 매개 변수

!:-1 = 이전 명령의 마지막 매개 변수

!! = 이전 명령 줄

기타

따라서 질문에 대한 가장 간단한 대답은 실제로 다음과 같습니다.

echo !!

... 또는 :

echo "Last command run was ["!:0"] with arguments ["!:*"]"

직접 시도해보십시오!

echo this is a test
echo !!

스크립트에서 히스토리 확장은 기본적으로 꺼져 있으며 다음을 사용하여 활성화해야합니다.

set -o history -o histexpand

Gilles답변읽은 후 트랩 에서 var도 사용할 수 있는지 (및 원하는 값) 확인하기로 결정했습니다 .$BASH_COMMANDEXIT

따라서 다음 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 is never printed because set -e causes bash to exit the script when a command fails and the false command always fails (by definition). The 12345 passed to false is just there to show that the arguments to the failed command are captured as well (the false command ignores any arguments passed to it)


I was able to achieve this by using set -x in the main script (which makes the script print out every command that is executed) and writing a wrapper script which just shows the last line of output generated by set -x.

This is the main script:

#!/bin/bash
set -x
echo some command here
echo last command

And this is the wrapper script:

#!/bin/sh
./test.sh 2>&1 | grep '^\+' | tail -n 1 | sed -e 's/^\+ //'

Running the wrapper script produces this as output:

echo last command

history | tail -2 | head -1 | cut -c8-999

tail -2 returns the last two command lines from history head -1 returns just first line cut -c8-999 returns just command line, removing PID and spaces.


There is a racecondition between the last command ($_) and last error ( $?) variables. If you try to store one of them in an own variable, both encountered new values already because of the set command. Actually, last command hasn't got any value at all in this case.

Here is what i did to store (nearly) both informations in own variables, so my bash script can determine if there was any error AND setting the title with the last run command:

   # 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

This script will retain the information, if an error occured and will obtain the last run command. Because of the racecondition i can not store the actual value. Besides, most commands actually don't even care for error noumbers, they just return something different from '0'. You'll notice that, if you use the errono extention of bash.

It should be possible with something like a "intern" script for bash, like in bash extention, but i'm not familiar with something like that and it wouldn't be compatible as well.

CORRECTION

I didn't think, that it was possible to retrieve both variables at the same time. Although i like the style of the code, i assumed it would be interpreted as two commands. This was wrong, so my answer devides down to:

   # 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

Although my post might not get any positive rating, i solved my problem myself, finally. And this seems appropriate regarding the intial post. :)

참고URL : https://stackoverflow.com/questions/6109225/echoing-the-last-command-run-in-bash

반응형