태그 보관물: variables

variables

변수에 표준 오류를 저장하는 방법 ERROR라고하자. 내가 stdout을 사용하고 있음에 주목하십시오. stdout을

다음과 같은 스크립트가 있다고 가정 해 봅시다.

쓸모없는

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저장할 수 있습니다. 파이프 결과는 표시 를 위해 전송 되거나 다른 명령으로 파이프됩니다.stderrerrorstdout

이 작업을 수행하는 데 필요한 리디렉션을 관리하기 위해 몇 가지 추가 파일 설명자를 설정합니다.

#!/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일부 를 잡고 싶다면 할 수 있습니다commandvar

{ 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>&1stderr출력 캡처로 리디렉션$(..)
    • 1>&3stdout출력 캡처에서 $(..)다시 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. 그러나 지역 변수를 사용할 수 없으며 매우 추악합니다!
  • 또한 evals는 안전한 방식으로 사용됩니다. 일반적으로 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-

그것은 않는 임시 파일을 사용합니다. 그러나 적어도 못생긴 물건은 기능에 싸여 있습니다.