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