[bash] 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]

barset -e명령이 실패하고 false 명령이 항상 실패 할 때 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


답변

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

tail -2기록에서 마지막 두 명령 줄을
head -1반환합니다. 첫 번째 줄만
cut -c8-999반환합니다. 명령 줄 만 반환하고 PID와 공백을 제거합니다.


답변

마지막 명령 ($ _)과 마지막 오류 ($?) 변수 사이에 경쟁 조건이 있습니다. 그들 중 하나를 자신의 변수에 저장하려고하면 둘 다 이미 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

이 스크립트는 오류가 발생한 경우 정보를 유지하고 마지막 실행 명령을 얻습니다. 경쟁 조건으로 인해 실제 값을 저장할 수 없습니다. 게다가 대부분의 명령은 실제로 오류 번호를 신경 쓰지 않고 ‘0’과 다른 것을 반환합니다. bash의 errono 확장을 사용하면 알 수 있습니다.

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

내 게시물이 긍정적 인 평가를받지 못할 수도 있지만 마침내 내 문제를 직접 해결했습니다. 그리고 이것은 초기 포스트와 관련하여 적절 해 보입니다. 🙂


답변