현재 환경을 수정하는 함수의 표준 오류를 캡처하는 방법은 무엇입니까?

현재 환경을 수정하는 함수의 표준 오류를 캡처하는 방법은 무엇입니까?

module기능환경 모듈1 패키지는 다양한 환경 변수를 수정하여 작업을 수행합니다.현재의쉘 프로세스.

안타깝게도 이 함수 는 성공 여부에 관계없이 0 2 를 반환하므로 클라이언트 스크립트가 실패에 적절하게 대응하기 어렵습니다.

모든 매개변수를 직접 전달하고 실패 시 0이 아닌 값을 올바르게 반환하는 mymodule함수 래퍼를 구현하고 싶습니다 .modulemodulemodule

mymodule실패를 감지하는 유일한 방법은 module출력 module함수가 stderr(있는 경우)에 무엇을 쓰는지 확인하는 것입니다.

문제는 작업을 mymodule취소하지 않고는 이 출력을 얻을 수 있는 합리적인 방법을 생각할 수 없다는 것입니다 . 더 구체적으로 말하면, stderr를 변수로 캡처하기 module위해 제가 생각할 수 있는 거의 모든 방법은 하위 프로세스에서 실행되어야 하며, 이는 해당 작업을 수행하는 것을 방지합니다(현재 셸을 수정해야 함).modulemodule

위의 예외는 stderr를 임시 파일로 리디렉션하는 것이지만 함수가 실행될 때마다 파일을 생성한다는 아이디어는 module싫습니다 .module

현재 환경에서 mymodule호출 module하고 동시에 변수에 표준 오류를 캡처할 수 있는 방법이 있습니까?

zsh나는 두 가지 모두에 대한 답변에 관심이 있습니다 bash.


1. 교류하지 마세요.Lmod 환경 모듈인터페이스가 매우 유사한 패키지입니다.

2 적어도 제가 사용해야 했던 이전 버전 3.2.9의 경우는 그랬습니다. 나는 이것을 통제할 수 없습니다.

답변1

Bash와 zsh 모두공동 프로세스(불행히도 약간 다름) 기본적으로 다음과 같이 끝납니다.전화pipe그리고 호출 셸에서 표준 입력 및 표준 출력을 사용할 수 있는 하위 프로세스를 생성합니다. 기본적으로 쉘은 현재 프로세스에서 실행 x되지만 모두 , 및 실행됩니다.yx | cmd | yxycmd아니요파이프라인 실행 환경에서.

module 2>&...이를 통해 현재 쉘에서 무언가를 실행할 수 있습니다 . 보조 프로세스로 사용하면(예:) 모든 것을 직접 다시 반복한 다음 나중에 출력을 다시 현재 셸로 다시 읽을 수 있습니다....modulecatcmdy <&...

또 다른 옵션은 표준 오류를 다른 백그라운드 프로세스로 리디렉션하고 wait해당 반환 코드를 얻는 것입니다. 아래에서 이 두 가지 문제를 모두 논의하겠습니다.


이 가짜 기능을 테스트용으로 사용하여 module마음대로 오류를 켜고 끌 수 있도록 하겠습니다. if 는 우리가 볼 수 있도록 현재 쉘 환경을 수정하고 stderr에 "err"을 인쇄합니다. 필요에 따라 이 줄에 주석을 달겠습니다.

module() {
        sleep 1
        FOO=$(date)
        echo err >&2
} 

catBash에서 공동 프로세스를 실행 하면 module의 stderr을 그 안으로 리디렉션하고 cat의 stdout에서 읽어 원하는 작업을 수행할 수 있습니다.

coproc cat
module 2>&${COPROC[1]}
exec {COPROC[1]}>&-
if grep -q err <&${COPROC[0]}
then
        echo got an error
else
        echo no error
fi

zsh에서는 다음과 같아야 합니다.

module 2>&p
exec 4<&p
coproc :
if grep -q err <&4

대신, 중간에.

두 경우 모두 module현재 셸에서 명령을 실행하고 거기에서 오류 출력을 읽을 수 있습니다. 함수가 if정상적으로 반환될 수 있습니다.

cat현재 실행 환경에서 실행되는 것을 제외한 모든 것아니요파이프라인과 같은 독립적인 환경을 만듭니다. echo $FOO마지막에 이를 확인하면 module현재 환경에서 실행한 이후 날짜가 업데이트된 것을 확인할 수 있습니다.


또는 백그라운드 프로세스가 모든 작업을 수행할 수 있습니다. 이것은 Bash에서 작동합니다:

exec 2> >( if grep -q . ; then exit 7 ; else exit 0 ; fi )
PID=$!
module
exec 2>&-
wait $PID
echo $?

위의 내용은 최상위 하위 프로세스의 내용을 출력 7하거나 기반으로 반환 코드를 사용하여 원하는 대로 수행하도록 조정할 수 있습니다. 프로세스 대체가 설정되지 않았기 0때문에 zsh에서는 그렇지 않습니다. 이 문제는 해결 가능해야 하지만 시도를 중단했습니다. $!임시 파일 대신 고정 fifo도 여기서 작동합니다.

이 경우 양쪽에서 FD 2를 저장하고 복원할 수도 있습니다.

답변2

"환경 모듈"이 어떻게 작동하는지 모르겠지만 설명에 따르면 환경 변수를 설정하는 쉘 함수이고 표준 오류 출력은 별도의 프로세스에서 실행할 필요 없이 캡처/일치되어야 한다고 가정합니다.

좋든 싫든, 신뢰할 수 있고 확실한 유일한 방법은 표준 오류를 임시 파일로 리디렉션하는 것입니다. 명명된 파이프를 사용하는 것도 마찬가지로 불편하고(여전히 임시 파일을 만들어야 합니다!) 더 까다롭습니다. 코프로세스를 사용하는 것은무겁고 번거롭고 휴대하기 어려움.

bash(그리고 오직 에서만 bash) 당신은 다음을 활용할 수 있습니다문서화되지 않은 기능( $!프로세스 교체에서 PID로 설정 >(...)) 다음과 같은 상황을 제거하십시오.

module 2> >(grep error)
wait $! && echo failed

이 예제에서는 module자체적으로 혼란스러울 수 있는 하위 항목을 생성하지 않는다고 가정합니다 $!.

답변3

Linux에서는 bash 및 zsh를 사용하여 다음을 수행할 수 있습니다.

my_module() {
  chmod u+w /dev/fd/3 # only needed in bash 5+
  module 2> /dev/fd/3 3>&-
  ! grep -q err /dev/fd/3
} 3<<< ''

이는 3<<< ''처음에 빈 줄을 포함하는 문자열입니다. zsh둘 다 bash여기에 문자열을 구현하고 여기에 삭제된 임시 파일로 문서화합니다. Linux(및 Cygwin이지만 일반적으로 다른 시스템은 아님)에서 파일을 열면 /dev/fd/3fd 3이 가리키는 파일이 삭제되었더라도(다른 시스템에서는 fd 3을 복사함) 열리므로 이는 임시 파일을 처리하는 방법입니다. 매우 깔끔한 방법 거기. 파일이 삭제되었으므로 정리에 대해 걱정할 필요가 없으며 짧은 시간 동안 FS에만 표시됩니다(그러나 버전 5부터 해당 파일 bash에 대한 쓰기 권한이 제거되었으므로 수정해야 합니다). 이것 chmod).

module여기에서 (별도의 프로세스에서 병렬이 아닌) 순차적으로 실행하려면 임시 파일이 필요합니다 grep. 파이프(Michael의 접근 방식과 유사하지만 @mosvy의 접근 방식과는 다름)를 사용하여 이 작업을 수행하면 파이프를 채울 만큼 충분한 데이터 출력이 있는 경우 교착 상태가 발생합니다.

관련 정보