Programing

stdout 및 stderr를 다른 변수로 캡처

lottogame 2020. 12. 5. 09:09
반응형

stdout 및 stderr를 다른 변수로 캡처


임시 파일을 사용하지 않고 stdout 및 stderr을 다른 변수 에 저장하거나 캡처 할 수 있습니까? 지금은 실행할 때 stdout out및 stderr 을 가져 오기 위해 이것을 수행 하지만 임시 파일을 피하고 싶습니다.errsome_command

error_file=$(mktemp)
out=$(some_command 2>$error_file)
err=$(< error_file)
rm $error_file

좋아, 약간 못 생겼지 만 여기에 해결책이 있습니다.

unset t_std t_err
eval "$( (echo std; echo err >&2) \
        2> >(readarray -t t_err; typeset -p t_err) \
         > >(readarray -t t_std; typeset -p t_std) )"

경우는 (echo std; echo err >&2)실제 명령으로 대체해야합니다. stdout의 출력은 $t_std줄 바꿈 (the -t)과 stderr생략하여 배열 줄에 저장 됩니다 $t_err.

배열이 마음에 들지 않으면 할 수 있습니다.

unset t_std t_err
eval "$( (echo std; echo err >&2 ) \
        2> >(t_err=$(cat); typeset -p t_err) \
         > >(t_std=$(cat); typeset -p t_std) )"

마지막 수정 var=$(cmd)으로 이동 $?하는 값을 제외하고는 의 동작을 거의 모방합니다 .

unset t_std t_err t_ret
eval "$( (echo std; echo err >&2; exit 2 ) \
        2> >(t_err=$(cat); typeset -p t_err) \
         > >(t_std=$(cat); typeset -p t_std); t_ret=$?; typeset -p t_ret )"

여기 $?에 보존됩니다$t_ret

GNU bash 버전 4.2.37 (1) -release (i486-pc-linux-gnu)를 사용하여 Debian wheezy에서 테스트되었습니다 .


조나단이 답을 가지고 있습니다. 참고로 이것은 ksh93 트릭입니다. (고대가 아닌 버전이 필요합니다).

function out {
    echo stdout
    echo stderr >&2
}

x=${ { y=$(out); } 2>&1; }
typeset -p x y # Show the values

생산하다

x=stderr
y=stdout

${ cmds;}구문은 서브 쉘을 생성하지 않습니다 단지 명령을 대체합니다. 명령은 현재 셸 환경에서 실행됩니다. 시작 부분의 공백이 중요합니다 ( {예약어 임).

내부 명령 그룹의 Stderr은 stdout으로 리디렉션됩니다 (내부 대체에 적용됨). 다음으로 stdout이에 out할당되고 y리디렉션 된 stderr가 명령 대체의 하위 쉘에 x대한 일반적인 손실없이에 의해 캡처됩니다 y.

다른 셸에서는 불가능합니다. 출력을 캡처하는 모든 구조는 생산자를 하위 셸에 넣어야하므로이 경우 할당이 포함됩니다.

업데이트 : 이제 mksh에서도 지원됩니다.


이 명령은 현재 실행중인 셸에서 stdout (stdval) 및 stderr (errval) 값을 모두 설정합니다.

eval "$( execcommand 2> >(setval errval) > >(setval stdval); )"

이 함수가 정의 된 경우 :

function setval { printf -v "$1" "%s" "$(cat)"; declare -p "$1"; }

execcommand를 캡처 된 명령 ( "ls", "cp", "df"등)으로 변경합니다.


이 모든 것은 setval 함수를 사용하여 캡처 된 모든 값을 텍스트 줄로 변환 할 수 있다는 아이디어에 기반을두고 있으며, setval은이 구조의 각 값을 캡처하는 데 사용됩니다.

execcommand 2> CaptureErr > CaptureOut

각 캡처 값을 setval 호출로 변환합니다.

execcommand 2> >(setval errval) > >(setval stdval)

실행 호출 안에 모든 것을 래핑하고 에코합니다.

echo "$( execcommand 2> >(setval errval) > >(setval stdval) )"

각 setval이 생성하는 선언 호출을 받게됩니다.

declare -- stdval="I'm std"
declare -- errval="I'm err"

해당 코드를 실행하고 변수를 설정하려면 eval을 사용하십시오.

eval "$( execcommand 2> >(setval errval) > >(setval stdval) )"

마지막으로 설정된 변수를 에코합니다.

echo "std out is : |$stdval| std err is : |$errval|

반환 (종료) 값을 포함 할 수도 있습니다.
완전한 bash 스크립트 예제는 다음과 같습니다.

#!/bin/bash --

# The only function to declare:
function setval { printf -v "$1" "%s" "$(cat)"; declare -p "$1"; }

# a dummy function with some example values:
function dummy { echo "I'm std"; echo "I'm err" >&2; return 34; }

# Running a command to capture all values
#      change execcommand to dummy or any other command to test.
eval "$( dummy 2> >(setval errval) > >(setval stdval); <<<"$?" setval retval; )"

echo "std out is : |$stdval| std err is : |$errval| return val is : |$retval|"

이것은 다른 변수에서 stdout과 stderr를 잡기위한 것입니다. 잡으려는 stderr경우 그대로두면 stdout더 좋고 짧은 해결책이 있습니다.

하기 요약 모든 것을 까지 독자의 이익을 위해, 여기입니다

간편한 재사용 가능 bash솔루션

이 버전은 서브 쉘을 사용하며 tempfiles 없이 실행됩니다 . ( tempfile서브 셸없이 실행 되는 버전의 경우 다른 답변을 참조하십시오 .)

: catch STDOUT STDERR cmd args..
catch()
{
eval "$({
__2="$(
  { __1="$("${@:3}")"; } 2>&1;
  ret=$?;
  printf '%q=%q\n' "$1" "$__1" >&2;
  exit $ret
  )"
ret="$?";
printf '%s=%q\n' "$2" "$__2" >&2;
printf '( exit %q )' "$ret" >&2;
} 2>&1 )";
}

사용 예 :

dummy()
{
echo "$3" >&2
echo "$2" >&1
return "$1"
}

catch stdout stderr dummy 3 $'\ndiffcult\n data \n\n\n' $'\nother\n difficult \n  data  \n\n'

printf 'ret=%q\n' "$?"
printf 'stdout=%q\n' "$stdout"
printf 'stderr=%q\n' "$stderr"

이것은 인쇄

ret=3
stdout=$'\ndiffcult\n data '
stderr=$'\nother\n difficult \n  data  '

그래서 그것에 대해 더 깊이 생각하지 않고 사용할 수 있습니다. 그냥 catch VAR1 VAR2앞에 놓으면 command args..완료됩니다.

일부 if cmd args..; thenif catch VAR1 VAR2 cmd args..; then. 정말 복잡하지 않습니다.

토론

Q : 어떻게 작동합니까?

여기에있는 다른 답변의 아이디어를 함수로 래핑하여 쉽게 재사용 할 수 있습니다.

catch()기본적으로 eval두 변수를 설정하는 데 사용 합니다. https://stackoverflow.com/a/18086548 과 유사합니다.

다음의 전화를 고려하십시오 catch out err dummy 1 2a 3b.

  • 지금 eval "$({및를 건너 뛰겠습니다 __2="$(. 나중에 이것에 대해 설명하겠습니다.

  • __1="$("$("${@:3}")"; } 2>&1;이 실행 dummy 1 2 3저장 그건 stdout__1나중에 사용하기 위해. 그래서 __1이된다 2a. 또한 리디렉션 stderrdummystdout외부 캐치 수집 할 수있는 등stdout

  • ret=$?; 종료 코드를 포착합니다. 1

  • printf '%q=%q\n' "$1" "$__1" >&2;다음 출력 out=2astderr. stderr현재 같이, 여기에 사용되는 stdout이미의 역할을 취한 stderrdummy명령.

  • exit $ret그런 다음 종료 코드 ( 1)를 다음 단계 로 전달합니다 .

이제 바깥쪽으로 __2="$( ... )":

  • 이 캐치 stdout는 IS 위의 stderrdummy변수로, 통화 __2. ( __1여기서 다시 사용할 수 있지만 __2덜 혼란스럽게 만들었습니다.) 그래서 __2이된다3b

  • ret="$?";1(에서 dummy) (반환 된) 반환 코드를 다시 잡습니다.

  • printf '%s=%q\n' "$2" "$__2" >&2;다음 출력 err=3astderr. stderr이미 다른 변수를 출력하는 데 사용되었으므로 다시 사용됩니다 out=2a.

  • printf '( exit %q )' "$ret" >&2; then outputs the code to set the proper return value. I did not find a better way, as assignig it to a variable needs a variable name, which then cannot be used as first oder second argument tocatch`.

최적화 printfprintf '%s=%q\n( exit %q )"$ __ 2" "$ ret"`과 같이 2 개를 하나의 것으로 작성할 수 있습니다 .

그래서 우리는 지금까지 무엇을 가지고 있습니까?

stderr에 다음과 같이 작성했습니다.

out=2a
err=3b
( exit 1 )

어디 out출신 $1, 2a출신 stdoutdummy, err출신 $2, 3b출신 stderrdummy, 그리고는 1리턴 코드에서입니다 dummy.

주의하시기 바랍니다 %q의 형식 printf이 올 때 쉘이 적절한 (단일) 인수를 볼 수 있도록, 인용 부호 처리합니다 eval. 2a그리고 3b그들은 문자 그대로 복사됩니다, 너무 간단합니다.

이제 바깥쪽으로 eval "$({ ... } 2>&1 )";:

이것은 위의 모든 것을 실행하여 두 개의 변수를 출력하고 exit, 그것을 잡아서 (그것에 따라 2>&1)를 사용하여 현재 쉘로 구문 분석합니다 eval.

이렇게하면 2 개의 변수가 설정되고 반환 코드도 함께 설정됩니다.

Q : eval악한 것을 사용합니다 . 그래서 안전합니까?

  • 만큼이로 printf %q더 버그가 없습니다, 그것은 안전합니다. 하지만 항상 매우 조심해야합니다. ShellShock에 대해 생각하면됩니다.

Q : 버그?

  • 다음을 제외하고 명백한 버그는 알려져 있지 않습니다.

    • 큰 출력을 포착하려면 모든 것이 변수로 들어가고 셸에서 역 분석되어야하므로 큰 메모리와 CPU가 필요합니다. 따라서 현명하게 사용하십시오.
    • 평소와 같이 마지막 $(echo $'\n\n\n\n') 뿐만 아니라 모든 줄 바꿈을 삼킨다 . 이것은 POSIX 요구 사항입니다. LF를 손상시키지 않으려면 출력에 후행 문자를 추가하고 다음 레시피에서와 같이 나중에 제거하십시오 ( x으로 끝나는 파일을 가리키는 소프트 링크를 읽을 수 있는 후행 을보십시오 $'\n').

      target="$(readlink -e "$file")x"
      target="${target%x}"
      
    • 쉘 변수는 바이트 NUL ( $'\0')을 전달할 수 없습니다 . stdout또는 에서 발생하는 경우 단순히 무시됩니다 stderr.

  • 주어진 명령은 하위 하위 쉘에서 실행됩니다. 따라서에 액세스 할 $PPID수 없으며 셸 변수를 변경할 수도 없습니다 . catch내장 기능을 포함하여 쉘 기능을 사용할 수 있지만 쉘 변수를 변경할 수는 없습니다 (내에서 실행중인 모든 것이이를 수행 $( .. )할 수 없기 때문입니다). 따라서 현재 셸에서 함수를 실행하고 stderr / stdout을 포착해야하는 경우 tempfiles를 사용 하여 일반적인 방법으로 수행해야합니다 . (이렇게하는 방법이 있습니다. 셸을 중단해도 일반적으로 잔해가 남지 않지만 이것은 복잡하고 그 자체로 대답 할 가치가 있습니다.)

Q : Bash 버전?

  • Bash 4 이상이 필요하다고 생각합니다 (으로 인해 printf %q)

Q : 여전히 어색해 보입니다.

  • 권리. 여기에 또 다른 대답ksh훨씬 더 깔끔하게 수행 할 수있는 방법을 보여줍니다 . 그러나 나는 익숙하지 않다 ksh. 그래서 나는 그것을 다른 사람들에게 맡겨서 비슷한 재사용하기 쉬운 레시피를 ksh만든다.

Q : ksh그렇다면 왜 사용하지 않습니까?

  • 이것은 bash해결책 이기 때문에

Q : 스크립트를 개선 할 수 있습니다.

  • 물론 일부 바이트를 짜서 더 작거나 더 이해하기 어려운 솔루션을 만들 수 있습니다. 그냥 가세요;)

Q : 오타가 있습니다. : catch STDOUT STDERR cmd args..읽을 것이다# catch STDOUT STDERR cmd args..

  • 실제로 이것은 의도 된 것입니다. 댓글이 조용히 삼키는 동안 :나타납니다 bash -x. 따라서 함수 정의에 오타가있는 경우 파서가 어디에 있는지 확인할 수 있습니다. 이것은 오래된 디버깅 트릭입니다. 그러나 약간 조심하십시오 :. 인수 내에서 깔끔한 부작용을 쉽게 만들 수 있습니다 .

편집 : .NET에서 ;단일 라이너를 더 쉽게 만들 수 있도록 몇 가지를 더 추가했습니다 catch(). 그리고 그것이 어떻게 작동하는지 섹션을 추가했습니다.


기술적으로 명명 된 파이프는 임시 파일이 아니며 여기에서 언급하는 사람은 없습니다. 그들은 파일 시스템에 아무것도 저장하지 않으며 연결하자마자 삭제할 수 있습니다 (그러므로 볼 수 없습니다).

#!/bin/bash -e

foo () {
    echo stdout1
    echo stderr1 >&2
    sleep 1
    echo stdout2
    echo stderr2 >&2
}

rm -f stdout stderr
mkfifo stdout stderr
foo >stdout 2>stderr &             # blocks until reader is connected
exec {fdout}<stdout {fderr}<stderr # unblocks `foo &`
rm stdout stderr                   # filesystem objects are no longer needed

stdout=$(cat <&$fdout)
stderr=$(cat <&$fderr)

echo $stdout
echo $stderr

exec {fdout}<&- {fderr}<&- # free file descriptors, optional

이런 식으로 여러 백그라운드 프로세스를 가질 수 있으며 편리한 시간 등에 stdout 및 stderr를 비동기 적으로 수집 할 수 있습니다.

하나의 프로세스에만 필요한 경우 {fdout}/{fderr}구문 (무료 fd를 찾습니다) 대신 3과 4와 같은 하드 코딩 된 fd 번호를 사용할 수 있습니다.


eval이 마음에 들지 않았으므로 여기에 몇 가지 리디렉션 트릭을 사용하여 프로그램 출력을 변수로 캡처 한 다음 해당 변수를 구문 분석하여 다른 구성 요소를 추출하는 솔루션이 있습니다. -w 플래그는 청크 크기를 설정하고 중간 형식의 std-out / err 메시지 순서에 영향을줍니다. 1은 오버 헤드 비용으로 잠재적으로 높은 해상도를 제공합니다.

#######                                                                                                                                                                                                                          
# runs "$@" and outputs both stdout and stderr on stdin, both in a prefixed format allowing both std in and out to be separately stored in variables later.                                                                  
# limitations: Bash does not allow null to be returned from subshells, limiting the usefullness of applying this function to commands with null in the output.                                                                   
# example:                                                                                                                                                                                                                       
#  var=$(keepBoth ls . notHere)                                                                                                                                                                                                  
#  echo ls had the exit code "$(extractOne r "$var")"                                                                                                                                                                            
#  echo ls had the stdErr of "$(extractOne e "$var")"                                                                                                                                                                            
#  echo ls had the stdOut of "$(extractOne o "$var")"                                                                                                                                                                            
keepBoth() {                                                                                                                                                                                                                     
  (                                                                                                                                                                                                                              
    prefix(){                                                                                                                                                                                                                    
      ( set -o pipefail                                                                                                                                                                                                          
        base64 -w 1 - | (                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
          while read c                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
          do echo -E "$1" "$c"                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
          done                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
        )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
      )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    ( (                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
        "$@" | prefix o >&3                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        echo  ${PIPESTATUS[0]} | prefix r >&3                                                                                                                                                                                                                                                                                                                                                                                                                                                           
      ) 2>&1 | prefix e >&1                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
    ) 3>&1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
  )                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

extractOne() { # extract                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
  echo "$2" | grep "^$1" | cut --delimiter=' ' --fields=2 | base64 --decode -                                                                                                                                                                                                                                                                                                                                                                                                                           
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

간단히 말해서 대답은 '아니오'라고 생각합니다. 캡처 $( ... )는 변수에 대한 표준 출력 만 캡처합니다. 표준 오류를 별도의 변수로 캡처하는 방법은 없습니다. 그래서, 당신이 가진 것은 그것이 얻는 것만 큼 깔끔합니다.


어때 ... = D

GET_STDERR=""
GET_STDOUT=""
get_stderr_stdout() {
    GET_STDERR=""
    GET_STDOUT=""
    unset t_std t_err
    eval "$( (eval $1) 2> >(t_err=$(cat); typeset -p t_err) > >(t_std=$(cat); typeset -p t_std) )"
    GET_STDERR=$t_err
    GET_STDOUT=$t_std
}

get_stderr_stdout "command"
echo "$GET_STDERR"
echo "$GET_STDOUT"

독자의 이익을 위해 여기에 tempfiles를 사용하는 솔루션이 있습니다.

문제는 tempfiles 를 사용하지 않는 것이 었습니다 . 그러나 이것은 /tmp/쉘이 죽을 경우 tempfile 의 원치 않는 오염 때문일 수 있습니다 . kill -9일부의 경우 trap 'rm "$tmpfile1" "$tmpfile2"' 0발화하지 않습니다.

사용할tempfile있지만 잔해절대 남기고 싶지 않은 경우 다음 레시피를 참조하십시오.

다시 그것은 catch()(내 다른 대답같이 ) 호출되고 동일한 호출 구문을 가지고 있습니다.

catch stdout stderr command args..

# Wrappers to avoid polluting the current shell's environment with variables

: catch_read returncode FD variable
catch_read()
{
eval "$3=\"\`cat <&$2\`\"";
# You can use read instead to skip some fork()s.
# However read stops at the first NUL byte,
# also does no \n removal and needs bash 3 or above:
#IFS='' read -ru$2 -d '' "$3";
return $1;
}
: catch_1 tempfile variable comand args..
catch_1()
{
{
rm -f "$1";
"${@:3}" 66<&-;
catch_read $? 66 "$2";
} 2>&1 >"$1" 66<"$1";
}

: catch stdout stderr command args..
catch()
{
catch_1 "`tempfile`" "${2:-stderr}" catch_1 "`tempfile`" "${1:-stdout}" "${@:3}";
}

기능 :

  • 및에 tempfile대해 두 개의를 생성 합니다. 그러나 거의 즉시 제거되어 매우 짧은 시간 동안 만 존재합니다.stdoutstderr

  • catch_1()캐치 stdout변수로 (FD 1) 이동 stderr하려면 stdout, 다음은 ( "왼쪽")되도록 catch_1그 잡을 수있다.

  • 의 처리 catch는 오른쪽에서 왼쪽으로 수행되므로 왼쪽 catch_1이 마지막에 실행되고 stderr.

발생할 수있는 최악의 경우 일부 임시 파일이에 표시 /tmp/되지만이 경우 항상 비어 있습니다. (채워지기 전에 제거됩니다.). Linux에서 tmpfs는 주 메모리 GB 당 약 128K 파일을 지원하므로 일반적으로 이것은 문제가되지 않습니다.

  • 주어진 명령은 모든 로컬 쉘 변수에 액세스하고 변경할 수도 있습니다. 따라서 부작용이있는 쉘 함수를 호출 할 수 있습니다!

  • 이것은 tempfile호출을 위해 두 번만 포크됩니다 .

버그 :

  • tempfile실패한 경우 좋은 오류 처리가 누락되었습니다 .

  • 이것은 일반적인 \n셸 제거를 수행합니다. 의 주석을 참조하십시오 catch_read().

  • 파일 설명자를 사용 66하여 데이터를 명령으로 파이프 할 수 없습니다 . 필요한 경우 리디렉션을 위해 다른 설명자를 사용 42하십시오 (매우 오래된 셸은 최대 9 개의 FD 만 제공함).

  • $'\0'에서 NUL 바이트 ( )를 처리 할 수 ​​없습니다 . (NUL은 무시됩니다. 변형의 경우 NUL 뒤에있는 모든 것이 무시됩니다.)stdoutstderrread

참고 :

  • Unix를 사용하면 삭제 된 파일에 대한 참조를 유지하는 한 (예 : 열린 파일 핸들) 삭제 된 파일에 액세스 할 수 있습니다. 이런 식으로 열어서 제거 할 수 있습니다.

명령 1) 상태 저장 부작용이없고 2) 계산적으로 저렴하다면 가장 쉬운 해결책은 두 번 실행하는 것입니다. 나는 주로 디스크가 작동할지 여부를 아직 알지 못하는 부팅 시퀀스 중에 실행되는 코드에 이것을 사용했습니다. 제 경우에는 작았 기 some_command때문에 두 번 실행해도 성능 저하가 없었고 명령에도 부작용이 없었습니다.

주요 이점은 이것이 깨끗하고 읽기 쉽다는 것입니다. 여기에있는 솔루션은 매우 영리하지만 더 복잡한 솔루션을 포함하는 스크립트를 유지해야하는 사람이되기 싫습니다. 시나리오가 작동하는 경우 간단한 실행-두 번 접근 방식을 권장합니다. 훨씬 깨끗하고 유지 관리하기 쉽기 때문입니다.

예:

output=$(getopt -o '' -l test: -- "$@")
errout=$(getopt -o '' -l test: -- "$@" 2>&1 >/dev/null)
if [[ -n "$errout" ]]; then
        echo "Option Error: $errout"
fi

다시 말하지만, getopt에는 부작용이 없기 때문에 이것은 괜찮습니다. 내 부모 코드가 전체 프로그램에서 100 회 미만으로이 호출을 호출하고 사용자는 100 회 getopt 호출과 200 회 getopt 호출을 알아 차리지 못하기 때문에 성능에 안전하다는 것을 알고 있습니다.


OP가 원했던 것과는 다르지만 다른 옵션과는 다른 간단한 변형이 있습니다. 파일 설명자를 재정렬하여 원하는 것을 얻을 수 있습니다.

테스트 명령 :

%> cat xx.sh  
#!/bin/bash
echo stdout
>&2 echo stderr

그 자체로 :

%> ./xx.sh
stdout
stderr

이제 stdout을 인쇄하고 stderr를 변수에 캡처하고 stdout을 파일에 기록합니다.

%> export err=$(./xx.sh 3>&1 1>&2 2>&3 >"out")
stdout
%> cat out    
stdout
%> echo
$err 
stderr

또는 stdout을 기록하고 stderr를 변수에 캡처합니다.

export err=$(./xx.sh 3>&1 1>out 2>&3 )
%> cat out
stdout
%> echo $err
stderr

당신은 아이디어를 얻습니다.


한 가지 해결 방법은 엉망이지만이 페이지의 일부 제안보다 더 직관적 일 수 있습니다. 출력 스트림에 태그를 지정하고 병합 한 후 태그를 기반으로 분할하는 것입니다. 예를 들어 "STDOUT"접두사를 사용하여 stdout에 태그를 지정할 수 있습니다.

function someCmd {
    echo "I am stdout"
    echo "I am stderr" 1>&2
}

ALL=$({ someCmd | sed -e 's/^/STDOUT/g'; } 2>&1)
OUT=$(echo "$ALL" | grep    "^STDOUT" | sed -e 's/^STDOUT//g')
ERR=$(echo "$ALL" | grep -v "^STDOUT")

```

If you know that stdout and/or stderr are of a restricted form, you can come up with a tag which does not conflict with their allowed content.


WARNING: NOT (yet?) WORKING!

The following seems a possible lead to get it working without creating any temp files and also on POSIX sh only; it requires base64 however and due to the encoding/decoding may not be that efficient and use also "larger" memory.

  • Even in the simple case, it would already fail, when the last stderr line has no newline. This can be fixed at least in some cases with replacing exe with "{ exe ; echo >&2 ; }", i.e. adding a newline.
  • The main problem is however that everything seems racy. Try using an exe like:

    exe() { cat /usr/share/hunspell/de_DE.dic cat /usr/share/hunspell/en_GB.dic >&2 }

and you'll see that e.g. parts of the base64 encoded line is on the top of the file, parts at the end, and the non-decoded stderr stuff in the middle.

Well, even if the idea below cannot be made working (which I assume), it may serve as an anti-example for people who may falsely believe it could be made working like this.

Idea (or anti-example):

#!/bin/sh

exe()
{
        echo out1
        echo err1 >&2
        echo out2
        echo out3
        echo err2 >&2
        echo out4
        echo err3 >&2
        echo -n err4 >&2
}


r="$(  { exe  |  base64 -w 0 ; }  2>&1 )"

echo RAW
printf '%s' "$r"
echo RAW

o="$( printf '%s' "$r" | tail -n 1 | base64 -d )"
e="$( printf '%s' "$r" | head -n -1  )"
unset r    

echo
echo OUT
printf '%s' "$o"
echo OUT
echo
echo ERR
printf '%s' "$e"
echo ERR

gives (with the stderr-newline fix):

$ ./ggg 
RAW
err1
err2
err3
err4

b3V0MQpvdXQyCm91dDMKb3V0NAo=RAW

OUT
out1
out2
out3
out4OUT

ERR
err1
err2
err3
err4ERR

(At least on Debian's dash and bash)

참고URL : https://stackoverflow.com/questions/11027679/capture-stdout-and-stderr-into-different-variables

반응형