Stephane Chazelas가 작성, 본 문서의 저자가 교정
어떤 명령어든 처음 세 개의 파일 디스크립터가 사용 가능하기를 기대합니다. 첫 번째는 읽기용인 fd 0(표준입력)이고, 나머지 둘은 쓰기용으로 fd 1과 fd 2입니다.
표준입력(stdin), 표준출력(stdout), 표준에러(stderr)는 각 명령어들과 연관이 있습니다. ls 2>&1 은 ls 명령어의 표준에러를 임시로 쉘의 표준출력과 동일한 "리소스"로 연결시켜 줍니다.
관습적으로 한 명령어는 fd 0 (표준입력)에서 입력을 읽고, fd 1 (표준출력)으로 보통의 결과들을 출력하고, 에러 출력은 fs 2 (표준에러)로 합니다. 만약에 이들중 하나라도 열려 있지 않다면 문제가 생길지도 모릅니다.
bash$ cat /etc/passwd >&-
cat: standard output: Bad file descriptor
|
예를 들어, xterm을 실행시키면 먼저 자신을 초기화하고 사용자 쉘을 띄우기 전에 터미널 디바이스(/dev/pts/<n> 나 비슷한것)를 세 번 엽니다.
이 시점에서 Bash 는 이 세 개의 파일 디스크립터들을 상속받고 Bash 에서 실행시키는 각각의 명령어(자식 프로세스)들은 다시 차례대로 이 디스크립터들을 상속받습니다. 재지향이란 이 파일 디스크립터중의 하나를 다른 파일(혹은 파이프나 사용 가능한 모든 것)로 재할당 시키는 것입니다. 파일 디스크립터는 지역적(locally)으로 재할당 될 수도 있고(명령어, 명령어 그룹, 서브쉘, while, if, case, for 루프 등을 위해서), 쉘의 나머지 부분을 위해서 전역적(globally)으로 될 수도 있습니다(exec를 써서).
ls > /dev/null 은 ls의 fs 1 을 /dev/null으로 연결해서 실행하라는 뜻입니다.
bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 363 bozo 0u CHR 136,1 3 /dev/pts/1 bash 363 bozo 1u CHR 136,1 3 /dev/pts/1 bash 363 bozo 2u CHR 136,1 3 /dev/pts/1 bash$ exec 2> /dev/null bash$ lsof -a -p $$ -d0,1,2 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME bash 371 bozo 0u CHR 136,1 3 /dev/pts/1 bash 371 bozo 1u CHR 136,1 3 /dev/pts/1 bash 371 bozo 2w CHR 1,3 120 /dev/null bash$ bash -c 'lsof -a -p $$ -d0,1,2' | cat COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 379 root 0u CHR 136,1 3 /dev/pts/1 lsof 379 root 1w FIFO 0,0 7118 pipe lsof 379 root 2u CHR 136,1 3 /dev/pts/1 bash$ echo "$(bash -c 'lsof -a -p $$ -d0,1,2' 2>&1)" COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME lsof 426 root 0u CHR 136,1 3 /dev/pts/1 lsof 426 root 1w FIFO 0,0 7520 pipe lsof 426 root 2w FIFO 0,0 7520 pipe |
이것은 다른 형태의 재지향에서도 동작합니다.
연습문제: 다음 스크립트를 분석해 보세요.
#! /usr/bin/env bash
mkfifo /tmp/fifo1 /tmp/fifo2
while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 &
exec 7> /tmp/fifo1
exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&7)
exec 3>&1
(
(
(
while read a; do echo "FIFO2: $a"; done < /tmp/fifo2 | tee /dev/stderr | tee /dev/fd/4 | tee /dev/fd/5 | tee /dev/fd/6 >&7 &
exec 3> /tmp/fifo2
echo 1st, to stdout
sleep 1
echo 2nd, to stderr >&2
sleep 1
echo 3rd, to fd 3 >&3
sleep 1
echo 4th, to fd 4 >&4
sleep 1
echo 5th, to fd 5 >&5
sleep 1
echo 6th, through a pipe | sed 's/.*/PIPE: &, to fd 5/' >&5
sleep 1
echo 7th, to fd 6 >&6
sleep 1
echo 8th, to fd 7 >&7
sleep 1
echo 9th, to fd 8 >&8
) 4>&1 >&3 3>&- | while read a; do echo "FD4: $a"; done 1>&3 5>&- 6>&-
) 5>&1 >&3 | while read a; do echo "FD5: $a"; done 1>&3 6>&-
) 6>&1 >&3 | while read a; do echo "FD6: $a"; done 3>&-
rm -f /tmp/fifo1 /tmp/fifo2
# 각 명령어와 서브쉘에 대해서 어떤 fs 가 무엇을 가르키는지를 알아내 보세요.
exit 0 |