임시 파일 없이 STDERR 및 STDOUT을 다른 변수로 리디렉션

임시 파일 없이 STDERR 및 STDOUT을 다른 변수로 리디렉션
func() {
    echo 'hello'
    echo 'This is an error' >&2
}

a=$(func)
b=???

b임시 파일을 생성하지 않고 stderr를 변수 로 리디렉션하고 싶습니다 .

 echo $b
 # output should be: "This is an error"

작동하지만 임시 파일을 사용하는 솔루션:

touch temp.txt
exec 3< temp.txt
a=$(func 2> temp.txt);
cat <&3
rm temp.txt

문제는 어떻게 stderr리디렉션 합니까?임시 파일 없이 func변수에 기능을 적용하나요 ?b

답변1

zsh쓰기 가능한 임시 파일을 사용하여 Linux 및 여기에 있는 문서(예: 5.1 또는 5.1 이전) 를 구현하는 셸에서 bash다음을 수행할 수 있습니다.

{
  out=$(
    chmod u+w /dev/fd/3 && # needed for bash5.0
      ls /dev/null /x 2> /dev/fd/3
  )
  status=$?
  err=$(cat<&3)
} 3<<EOF
EOF

printf '%s=<%s>\n' out "$out" err "$err" status "$status"

( ls /dev/null /xstdout 및 stderr에 무언가를 인쇄하는 예제 명령은 어디에 있습니까?)

를 사용하면 zsh다음 작업도 수행할 수 있습니다.

(){ out=$(ls /dev/null /x 2> $1) status=$? err=$(<$1);} =(:)

( =(cmd)임시 파일과 익명 함수를 사용한 프로세스 교체 형식입니다 (){ code; } args.)

어쨌든 임시 파일을 사용하고 싶을 것입니다. 파이프를 사용하는 모든 솔루션은 출력이 크면 교착 상태가 발생하기 쉽습니다. 두 개의 개별 파이프를 통해 stdout 및 stderr을 읽고 루프에서 select()/ poll()및 일부 읽기를 사용하여 잠금을 발생시키지 않고 두 파이프에서 데이터를 읽을 수 있지만 이는 매우 복잡하며 AFAIK는 zsh내장 select()지원만 가능하고 yash원시 인터페이스는 하나만 있습니다. pipe()(자세한 내용은 참조쉘 리디렉션을 사용하여 동일한 파일 설명자 읽기/쓰기).

또 다른 접근 방식은 스트림 중 하나를 임시 파일 대신 임시 메모리에 저장하는 것입니다. 다음과 같습니다( zsh또는 bash구문):

{
  IFS= read -rd '' err
  IFS= read -rd '' out
  IFS= read -rd '' status
} < <({ out=$(ls /dev/null /x); } 2>&1; printf '\0%s' "$out" "$?")

(명령이 NUL을 출력하지 않는다고 가정)

여기에는 $err후행 줄바꿈이 포함됩니다.

다른 접근 방식은 stdout과 stderr을 다르게 장식하고 읽을 때 장식을 제거하는 것입니다.

out= err= status=
while IFS= read -r line; do
  case $line in
    (out:*)    out=$out${line#out:}$'\n';;
    (err:*)    err=$err${line#err:}$'\n';;
    (status:*) status=${line#status:};;
  esac
done < <(
  {
    {
      ls /dev/null /x |
        grep --label=out --line-buffered -H '^' >&3
      echo >&3 "status:${PIPESTATUS[0]}" # $pipestatus[1] in zsh
    } 2>&1 |
      grep --label=err --line-buffered -H '^'
  } 3>&1

)

GNU grep와 줄이 충분히 짧다고 가정합니다. 라인이 PIPEBUF(Linux의 경우 4K)보다 크면 두 s의 출력 라인이 결국 grep청크로 분할될 수 있습니다.

답변2

글쎄, 임시 파일 없이 한 변수에서 stderr을 캡처하고 다른 변수에서 stdout을 캡처하는 것은 결코 쉬운 작업이 아닙니다.

이것은 유효한 예입니다

func() {
    echo 'hello'
    echo 'This is an error' >&2
}

result=$(
    { stdout=$(func) ; } 2>&1
    echo -e "mysuperuniqueseparator\n"
    echo -e "${stdout}\n"
)
var_out=${result#*mysuperuniqueseparator$'\n'}
var_err=${result%$'\n'mysuperuniqueseparator*}

나는 이것이 stderr을 stdout으로 리디렉션하고 두 변수를 구분 기호를 사용하여 변수에 넣은 다음 두 부분으로 나누는 더러운 방법이기 때문에 만족스럽지 않습니다.

추가하다:

분명히 이것은 명령의 표준 출력이나 표준 오류에 사용하는 구분 기호 문자열이 포함될 수 있으므로 신뢰할 수 없습니다.

여기에서 가져옴http://mywiki.wooledge.org/BashFAQ/002

답변3

임시 파일/fifo, 흥미로운 평가/파일 설명자 등이 없습니다.

x=$((echo 'this is stdout'; echo 'this is stderr' 1>&2; exit 123) 2> >(sed -r 's/^/2/g') 1> >(sed -r 's/^/1/g'))

echo $? ### exit code is preserved
# 123

echo "$x" | sed '/^2/d;s/^1//g'  ### stdout
# this is stdout

echo "$x" | sed '/^1/d;s/^2//g'  ### stderr
# this is stderr

참고: 대규모 출력에는 효율적이지 않을 수 있습니다.

관련 정보