Bash 스크립트에 /dev/stdout 대상 위치를 저장하는 방법은 무엇입니까?

Bash 스크립트에 /dev/stdout 대상 위치를 저장하는 방법은 무엇입니까?

/dev/stdout첫 번째 파일 설명자를 다른 위치로 바꾸기 전에 원래 위치를 유지 하려는 bash 스크립트가 있습니다 .

그래서 자연스럽게 다음과 같은 글을 썼습니다.

old_stdout=$(readlink -f /dev/stdout)

하지만 작동하지 않습니다. 나는 문제를 빨리 이해했습니다.

test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18

분명히 $()하위 쉘에서 실행되면 하위 쉘이 상위 쉘로 연결됩니다.

/dev/stdout그래서 질문은: bash 스크립트에서 위치를 문자열로 저장하는 안정적인(Linux 배포판 간의 이식성으로 제한됨) 방법이 있습니까 ?

답변1

파일 설명자를 저장하려면 다른 파일 설명자에 복사하세요. 해당 파일의 경로를 저장하는 것만으로는 충분하지 않습니다. 열기 모드, 열기 플래그, 파일의 현재 위치 등을 저장해야 합니다. 물론 익명 파이프나 소켓에는 경로가 없으므로 이는 작동하지 않습니다. 저장하고 싶은 것은파일 설명 열기fd에서 참조한 것처럼 fd를 복사하면 실제로 동일한 fd에 새 fd가 반환됩니다.파일 설명 열기.

파일 설명자를 다른 파일 설명자로 복사하려면 Bourne과 유사한 셸을 다음 구문과 함께 사용하세요.

exec 3>&1

위에서 fd 1이 fd 3에 복사되었습니다.

이전에 열려 있던 fd 3이 무엇이든 닫히지만, fd 3부터 9까지(보통 그 이상, 최대 99까지 yash)는 이 목적을 위해 예약되어 있으며(0, 1 또는 2와 달리 특별한 의미가 없음) 쉘은 이를 알지 못합니다. 내부 업무에 사용합니다. fd 3이 일찍 열리는 유일한 이유는 스크립트 1 에서 실행했거나 호출자에 의해 유출되었기 때문입니다.

그런 다음 stdout을 다른 것으로 변경할 수 있습니다.

exec > /dev/null

그런 다음 표준 출력을 복원합니다.

exec >&3 3>&-

( 3>&-더 이상 필요하지 않은 파일 설명자를 닫습니다).

이제 문제는 ksh를 제외하고 그 이후에 실행하는 모든 명령이 exec 3>&1fd 3을 상속한다는 것입니다. fd 유출입니다. 일반적으로 큰 문제는 아니지만 문제가 발생할 수 있습니다.

ksh설정실행 시 닫기이러한 fd(2개 이상의 fd에 대해)에는 플래그가 있지만 다른 쉘에는 플래그가 없으며 다른 쉘에서 플래그를 수동으로 설정할 수 있는 방법이 없습니다.

다른 쉘의 해결 방법은 각 명령에 대해 fd 3을 닫는 것입니다. 예를 들면 다음과 같습니다.

exec 3>&-

exec > file.log

ls 3>&-
uname 3>&-

exec >&3 3>&-

문제. 여기서 가장 좋은 방법은 exec전혀 사용하지 않고 명령 그룹을 리디렉션하는 것입니다.

{
  ls
  uname
} > file.log

거기에서 쉘은 stdout을 저장하고 나중에 복원하는 일을 담당합니다( yash내부적으로 fd에 복사하여 이 작업을 수행합니다( 의 경우 99 이상).실행 시 닫기플래그 설정).

참고 1

이제 이러한 fd 3~9를 광범위하게 또는 함수 내에서 사용하는 경우 관리가 번거롭고 문제가 될 수 있습니다. 특히 스크립트가 이러한 fd를 사용할 수 있는 일부 타사 코드를 사용하는 경우 더욱 그렇습니다.

일부 쉘( zsh, bash, ksh93, 이 기능을 추가했습니다(올리버 키들(Oliver Kiddle)이 제안한 것zsh) 2005년 비슷한 시기에 개발자들 사이의 논의 끝에) 10보다 큰 첫 번째 여유 fd를 할당하는 대체 구문이 있었는데, 이는 이 경우에 도움이 될 것입니다.

myfunction() {
  local fd
  exec {fd}>&1
  # stdout was duplicated onto a new fd above 10, whose actual value
  # is stored in the fd variable
  ...
  # it should even be safe to re-enter the function here
  ...
  exec >&"$fd" {fd}>&-
}

답변2

$$대화형 쉘이거나 관련 쉘 PID를 작성하는 경우 현재 프로세스 PID를 얻습니다.

따라서 다음을 사용할 수 있습니다.

readlink -f /proc/$$/fd/1

예:

% readlink -f /proc/$$/fd/1
/dev/pts/33

% var=$(readlink -f /proc/$$/fd/1)

% echo $var                       
/dev/pts/33

답변3

보시다시피, bash 스크립트는 일반 프로그래밍 언어처럼 파일 설명자를 할당할 수 없습니다.

가장 간단한 해결책은 하위 쉘을 사용하여 리디렉션하려는 항목을 실행하여 전체 표준 I/O를 통해 처리가 최상위 쉘로 복원될 수 있도록 하는 것입니다.

또 다른 해결책은 ttyTTY 장치를 식별하고 스크립트에서 I/O를 제어하는 ​​것입니다. 예를 들어:

dev=$(tty)

그럼 당신은 할 수 있습니다 ...

echo message > $dev

관련 정보