스크립트 stderr 및 stdout을 파일로 리디렉션하고 stdout도 tty로 유지하시겠습니까?

스크립트 stderr 및 stdout을 파일로 리디렉션하고 stdout도 tty로 유지하시겠습니까?

내 스크립트에는 현재 다음이 있습니다.

exec > >(tee -a /tmp/history.log) 2>&1

그러면 모든 명령에 대한 stderr 및 stdout이 로그 파일 및 tty에 기록됩니다. 불행히도 이로 인해 tty가 매우 시끄러워지므로 터미널에 stdout을 두고 파일에 stdout과 stderr을 모두 두는 것이 좋습니다(올바른 순서로 있으므로 추가를 위해 파일을 두 번 열면 작동하지 않습니다). 나는 평생 동안 tee /dev/tty그것을 작동시키거나 사용하는 데 필요한 마법의 exec 호출을 알아낼 수 없습니다 .

답변1

tee파일 설명자로 직접 출력하는 것은 불가능하지만 프로세스 대체를 사용하여 해결할 수 있습니다 cat.

exec 3>&1 &>log 1> >(tee >(cat >&3))

따라서 stdout은 fd3을 통해 출력으로 이동하고 stdout과 stderr은 모두 로그로 이동합니다.

답변2

exec 2>> /tmp/history.log | tee -a /tmp/history.log

이는 표준 출력을 시작하기 전에 표준 오류를 리디렉션합니다 tee. 즉, 오류가 표준 출력 파이프(따라서 터미널로)로 전달되지 않는다는 의미입니다.

답변3

exec 2>>/tmp/history.log 1> >(tee -a /tmp/history.log >&1)도움이 될 수도 있지만 올바른 순서가 보장되지는 않습니다. 이 정렬은 보고된 바와 같이 잘 알려진 문제인 것 같습니다.여기그리고여기.

이 명령은 stderr를 기록 파일로 리디렉션한 2>>/tmp/history.log다음 tees stdout을 동일한 파일로 사용합니다 1> >(tee -a /tmp/history.log. 마지막으로 stdout으로 다시 연결됩니다: >&1.

이 방법에는 주의사항이 있습니다. 제가 수행한 일부 테스트에서는 출력 순서에 오류가 있을 수 있습니다.

find /etc/ -name interfaces예를 들어 테스트 용으로 사용합니다 . 이 명령의 출력은 다음과 같습니다.

$ find /etc/ -name interfaces
/etc/network/interfaces
find: `/etc/lvm/backup': Permission denied
find: `/etc/lvm/archive': Permission denied
find: `/etc/cups/ssl': Permission denied
/etc/cups/interfaces
find: `/etc/ssl/private': Permission denied
find: `/etc/polkit-1/localauthority': Permission denied

find /etc/ -name interfaces 2>>output 1> >(tee -a output >&1)스크립트에서 실행 하면 출력 파일에 다음이 포함됩니다.

+ find /etc/ -name interfaces
++ tee -a output
find: `/etc/lvm/backup'/etc/network/interfaces
: Permission denied
find: `/etc/lvm/archive': Permission denied
find: `/etc/cups/ssl': Permission denied
find: `/etc/ssl/private': Permission denied
/etc/cups/interfaces
find: `/etc/polkit-1/localauthority': Permission denied

stderr의 이 부분은 두 줄로 분할되었습니다.

find: `/etc/lvm/backup': Permission denied

모든 경우에 이런 일이 발생하는 것은 아니지만 주의해야 할 사항이 있습니다. 또한 위에서 언급한 것처럼 순서가 일치하지 않습니다.

답변4

stdout의 복사본을 만들고 하나의 복사본을 stderr과 병합하려고 합니다. 따라서 stdout만 통과해야 하며 tee로그 파일에 기록하는 두 가지 다른 프로세스가 있습니다. 가장 쉬운 방법은 로그 파일을 두 번 여는 것입니다.

exec > >(tee -a /tmp/history.log) 2>>/tmp/history.log

또는 직접 출력에 사용되는 것과 동일한 파일 설명자에 tee 쓰기를 수행할 수 있습니다. 이렇게 하려면 먼저 stderr을 로그 파일로 리디렉션한 다음 teestdout 복사를 stderr로 호출합니다.

exec 2>>/tmp/history.log
exec > >(tee -a /dev/fd/2)

이로 인해 데이터의 순서가 잘못될 수 있습니다., tee가 일부 이전 stdout 출력을 로그 파일에 복사하는 동안 프로그램이 계속해서 stderr(로그 파일에 직접)에 데이터를 쓸 수 있기 때문입니다.

이러한 재정렬을 방지하려면 두 스트림 모두 두 개의 입력 파이프가 있는 동일한 프로그램을 거쳐야 합니다. 두 개의 입력을 받아 순차적으로 처리하는 표준 명령줄 도구는 없습니다. 동일한 프로그램을 사용하더라도 파이프 자체의 버퍼링으로 인해 재정렬이 여전히 가능합니다. 해결책이 생각나지 않습니다.

또한 프로그램의 출력이 더 이상 터미널로 전송되지 않기 때문에 프로그램은 라이브러리 수준에서 크기 기반 출력 버퍼링(stdio 버퍼링)을 켤 수 있습니다. 이로 인해 더 많은 재정렬이 발생합니다. 이를 방지하려면(잠재적인 성능 저하) 다음을 수행하십시오.stdbuf버퍼링 사용 또는 끄기unbuffer.

관련 정보