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 |