[bash] 변수에 표준 오류를 저장하는 방법
다음과 같은 스크립트가 있다고 가정 해 봅시다.
쓸모없는
echo "This Is Error" 1>&2
echo "This Is Output"
그리고 또 다른 쉘 스크립트가 있습니다 :
alsoUseless.sh
./useless.sh | sed 's/Output/Useless/'
“This Is Error”또는 useless.sh의 다른 stderr를 변수로 캡처하고 싶습니다. 그것을 ERROR라고하자.
내가 stdout을 사용하고 있음에 주목하십시오. stdout을 계속 사용하고 싶습니다.이 경우 stderr을 stdout으로 리디렉션하는 것은 도움이되지 않습니다.
기본적으로 저는하고 싶습니다
./useless.sh 2> $ERROR | ...
그러나 그것은 분명히 작동하지 않습니다.
나도 할 수 있다는 걸 알아
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
그러나 그것은 추악하고 불필요합니다.
불행히도, 여기에 답변이 없으면 내가해야 할 일입니다.
다른 방법이 있기를 바라고 있습니다.
더 좋은 아이디어가 있습니까?
답변
오류 파일을 캡처하는 것이 더 깔끔합니다.
ERROR=$(</tmp/Error)
쉘은 이것을 인식 cat
하고 데이터를 얻기 위해 ‘ ‘를 실행할 필요가 없습니다 .
더 큰 질문은 어렵다. 쉬운 방법이 없다고 생각합니다. 오류를 표준 출력으로 리디렉션 할 수 있도록 전체 파이프 라인을 하위 셸에 빌드하여 최종 표준 출력을 파일로 전송해야합니다.
ERROR=$( { ./useless.sh | sed s/Output/Useless/ > outfile; } 2>&1 )
세미콜론이 필요하다는 것을 명심하십시오 (클래식 쉘-Bourne, Korn-아마도 Bash에서도). ‘ {}
‘는 동봉 된 명령에 대한 I / O 리디렉션을 수행합니다. 작성된 것처럼 오류 sed
도 캡처합니다 .
경고 : 공식적으로 테스트되지 않은 코드-위험 부담으로 사용하십시오.
답변
alsoUseless.sh
이를 useless.sh
통해와 같은 명령을 통해 스크립트 의 출력을 파이프하고 라는 변수에 sed
저장할 수 있습니다. 파이프 결과는 표시 를 위해 전송 되거나 다른 명령으로 파이프됩니다.stderr
error
stdout
이 작업을 수행하는 데 필요한 리디렉션을 관리하기 위해 몇 가지 추가 파일 설명자를 설정합니다.
#!/bin/bash
exec 3>&1 4>&2 #set up extra file descriptors
error=$( { ./useless.sh | sed 's/Output/Useless/' 2>&4 1>&3; } 2>&1 )
echo "The message is \"${error}.\""
exec 3>&- 4>&- # release the extra file descriptors
답변
stderr을 stdout으로 리디렉션하고 stdout을 / dev / null로 $()
리디렉션 한 다음 백틱을 사용하거나 리디렉션 된 stderr를 캡처합니다.
ERROR=$(./useless.sh 2>&1 >/dev/null)
답변
이 질문에 대한 많은 중복이 있으며, 대부분은 stderr 및 stdout 과 종료 코드를 동시에 캡처하지 않으려는 사용 시나리오가 약간 더 단순합니다 .
if result=$(useless.sh 2>&1); then
stdout=$result
else
rc=$?
stderr=$result
fi
성공한 경우 올바른 출력이 나오거나 실패한 경우 stderr의 진단 메시지가 예상되는 일반적인 시나리오에서 작동합니다.
쉘의 제어문은 이미 $?
후드 아래에서 검사 합니다. 그래서 보이는 것
cmd
if [ $? -eq 0 ], then ...
어색하고 단조로운 표현입니다
if cmd; then ...
답변
# command receives its input from stdin.
# command sends its output to stdout.
exec 3>&1
stderr="$(command </dev/stdin 2>&1 1>&3)"
exitcode="${?}"
echo "STDERR: $stderr"
exit ${exitcode}
답변
독자의 이익을 위해 여기 에이 레시피
- stderr를 변수로 잡기 위해 oneliner로 재사용 가능
- 여전히 명령의 리턴 코드에 대한 액세스를 제공합니다
- 임시 파일 디스크립터 3을 희생합니다 (물론 변경 가능)
- 그리고이 임시 파일 설명자를 내부 명령에 노출시키지 않습니다
당신이 안으로 stderr
일부 를 잡고 싶다면 할 수 있습니다command
var
{ var="$( { command; } 2>&1 1>&3 3>&- )"; } 3>&1;
그 후에는 모든 것이 있습니다.
echo "command gives $? and stderr '$var'";
command
간단한 경우 (와 같지 않음 a | b
) 내부를 {}
멀리 둘 수 있습니다.
{ var="$(command 2>&1 1>&3 3>&-)"; } 3>&1;
재사용이 용이 한 기능으로 싸여 있음 bash
(아마도 버전 3 이상 필요 local -n
) :
: catch-stderr var cmd [args..]
catch-stderr() { local -n v="$1"; shift && { v="$("$@" 2>&1 1>&3 3>&-)"; } 3>&1; }
설명 :
local -n
별명 “$ 1″(에 대한 변수catch-stderr
)3>&1
파일 설명자 3을 사용하여 stdout 포인트를 저장합니다.{ command; }
(또는 “$ @”)는 출력 캡처 내에서 명령을 실행합니다$(..)
- 정확한 순서는 여기서 중요합니다 (잘못된 방법으로 파일 디스크립터를 잘못 섞습니다).
2>&1
stderr
출력 캡처로 리디렉션$(..)
1>&3
stdout
출력 캡처에서$(..)
다시stdout
파일 디스크립터 3에 저장된 “외부”로 경로 재 지정합니다. 이는stderr
여전히 FD 1이 이전에 지정한 위치를 나타냅니다.$(..)
3>&-
그런 다음 더 이상 필요command
하지 않은 파일 디스크립터 3을 닫아 갑자기 알 수없는 열린 파일 디스크립터가 나타나지 않게합니다. 외부 셸에는 여전히 FD 3이 열려 있지만command
보이지 않습니다.- 후자는 중요합니다. 일부 프로그램
lvm
은 예기치 않은 파일 디스크립터에 대해 불평 하기 때문 입니다. 그리고 우리가 무엇을 포착 할 것인지에lvm
대해 불평합니다stderr
!
적절하게 조정하면이 레시피로 다른 파일 설명자를 잡을 수 있습니다. 물론 파일 디스크립터 1을 제외하고 (여기서 리디렉션 로직은 잘못되었지만 파일 디스크립터 1의 경우 var=$(command)
평소처럼 사용할 수 있습니다 ).
파일 디스크립터 3이 희생된다는 점에 유의하십시오. 해당 파일 디스크립터가 필요한 경우, 번호를 자유롭게 변경하십시오. 그러나 일부 쉘 (1980 년대 이후)은 99>&1
인수가 9
뒤에 오는 것으로 이해할 수 있습니다 9>&1
(이것은 문제가되지 않습니다 bash
).
또한 변수를 통해이 FD 3을 구성 할 수있는 것은 쉬운 일이 아닙니다. 이렇게하면 내용을 읽을 수 없게됩니다.
: catch-var-from-fd-by-fd variable fd-to-catch fd-to-sacrifice command [args..]
catch-var-from-fd-by-fd()
{
local -n v="$1";
local fd1="$2" fd2="$3";
shift 3 || return;
eval exec "$fd2>&1";
v="$(eval '"$@"' "$fd1>&1" "1>&$fd2" "$fd2>&-")";
eval exec "$fd2>&-";
}
보안 정보 : 처음 3 개의 인수를
catch-var-from-fd-by-fd
타사에서 가져 와서는 안됩니다. 항상 “정적”방식으로 명시 적으로 제공하십시오.그러니
catch-var-from-fd-by-fd $var $fda $fdb $command
절대 안돼!변수 변수 이름을 전달하는 경우 최소한 다음과 같이 수행하십시오.
local -n var="$var"; catch-var-from-fd-by-fd var 3 5 $command
이것은 여전히 모든 악용으로부터 당신을 보호하지는 않지만 최소한 일반적인 스크립팅 오류를 감지하고 피하는 데 도움이됩니다.
노트:
catch-var-from-fd-by-fd var 2 3 cmd..
와 같다catch-stderr var cmd..
shift || return
올바른 인수 수를 잊어 버린 경우 추악한 오류를 방지하는 방법입니다. 아마도 쉘을 종료하는 것이 다른 방법 일 것입니다 (그러나 이것은 명령 줄에서 테스트하기가 어렵습니다).- 루틴은 이해하기 쉽도록 작성되었습니다. 함수가 필요하지 않도록 함수를 다시 작성할 수는
exec
있지만 실제로는 추악합니다. - 이 루틴은
bash
불필요 하게 다시 작성할 수 있으므로 필요하지 않습니다local -n
. 그러나 지역 변수를 사용할 수 없으며 매우 추악합니다! - 또한
eval
s는 안전한 방식으로 사용됩니다. 일반적으로eval
위험한 것으로 간주됩니다. 그러나이 경우"$@"
(임의의 명령을 실행하기 위해)를 사용하는 것보다 더 나쁘지 않습니다 . 그러나 여기에 표시된대로 정확하고 올바른 인용 부호를 사용해야합니다 (그렇지 않으면 매우 위험합니다 ).
답변
내가 한 방법은 다음과 같습니다.
#
# $1 - name of the (global) variable where the contents of stderr will be stored
# $2 - command to be executed
#
captureStderr()
{
local tmpFile=$(mktemp)
$2 2> $tmpFile
eval "$1=$(< $tmpFile)"
rm $tmpFile
}
사용 예 :
captureStderr err "./useless.sh"
echo -$err-
그것은 않는 임시 파일을 사용합니다. 그러나 적어도 못생긴 물건은 기능에 싸여 있습니다.
