Приложение E. Подробное введение в операции ввода-вывода и перенаправление ввода-вывода


написано Stephane Chazelas и дополнено автором документа

Практически любая команда предполагает доступность 3-х файловых дескрипторов. Первый — 0 (стандвртный ввод, stdin), доступный для чтения. И два других — 1 (stdout) и 2 (stderr), доступные для записи.

Запись, типа ls 2>&1, означает временное перенаправление вывода, с устройства stderr на устройство stdout.

В соответствии с соглашениями, команды принимают ввод из файла с дескриптором 0 (stdin), выводят результат работы в файл с дескриптором 1 (stdout), а сообщения об ошибках — в файл с дескриптором 2 (stderr). Если какой либо из этих трех дескрипторов окажется закрытым, то могут возникнуть определенные проблемы:

  1. bash$ cat /etc/passwd >&-
  2. cat: standard output: Bad file descriptor
  3.      

К примеру, когда пользователь запускает xterm, то он сначала выполняет процедуру инициализации, а затем, перед запуском командной оболочки, xterm трижды открывает терминальные устройства (/dev/pts/<n>, или нечто подобное).

После этого, командная оболочка наследует эти три дескриптора, и любая команда, запускаемая в этой оболочке, так же наследует их. Термин перенаправление — означает переназначение одного файлового дескриптора на другой (канал (конвейер) или что-то другое). Переназначение может быть выполнено локально (для отдельной команды, для группы команд, для подоболочки, для операторов while, if, case, for...) или глобально (с помощью exec).

ls > /dev/null — означает запуск команды ls с файловым дескриптором 1, присоединенным к устройству /dev/null.

  1. bash$ lsof -a -p $$ -d0,1,2
  2. COMMAND PID     USER   FD   TYPE DEVICE SIZE NODE NAME
  3.  bash    363 bozo        0u   CHR  136,1         3 /dev/pts/1
  4.  bash    363 bozo        1u   CHR  136,1         3 /dev/pts/1
  5.  bash    363 bozo        2u   CHR  136,1         3 /dev/pts/1
  6. bash$ exec 2> /dev/null
  7. bash$ lsof -a -p $$ -d0,1,2
  8. COMMAND PID     USER   FD   TYPE DEVICE SIZE NODE NAME
  9.  bash    371 bozo        0u   CHR  136,1         3 /dev/pts/1
  10.  bash    371 bozo        1u   CHR  136,1         3 /dev/pts/1
  11.  bash    371 bozo        2w   CHR    1,3       120 /dev/null
  12. bash$ bash -c 'lsof -a -p $$ -d0,1,2' | cat
  13. COMMAND PID USER   FD   TYPE DEVICE SIZE NODE NAME
  14.  lsof    379 root    0u   CHR  136,1         3 /dev/pts/1
  15.  lsof    379 root    1w  FIFO    0,0      7118 pipe
  16.  lsof    379 root    2u   CHR  136,1         3 /dev/pts/1
  17. bash$ echo "$(bash -c 'lsof -a -p $$ -d0,1,2' 2>&1)"
  18. COMMAND PID USER   FD   TYPE DEVICE SIZE NODE NAME
  19.  lsof    426 root    0u   CHR  136,1         3 /dev/pts/1
  20.  lsof    426 root    1w  FIFO    0,0      7520 pipe
  21.  lsof    426 root    2w  FIFO    0,0      7520 pipe


Упражнение: Проанализируйте следующий сценарий.

  1. #! /usr/bin/env bash
  2. mkfifo /tmp/fifo1 /tmp/fifo2
  3. while read a; do echo "FIFO1: $a"; done < /tmp/fifo1 &
  4. exec 7> /tmp/fifo1
  5. exec 8> >(while read a; do echo "FD8: $a, to fd7"; done >&7)
  6. exec 3>&1
  7. (
  8.  (
  9.   (
  10.    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 &
  11.    exec 3> /tmp/fifo2
  12.    echo 1st, to stdout
  13.    sleep 1
  14.    echo 2nd, to stderr >&2
  15.    sleep 1
  16.    echo 3rd, to fd 3 >&3
  17.    sleep 1
  18.    echo 4th, to fd 4 >&4
  19.    sleep 1
  20.    echo 5th, to fd 5 >&5
  21.    sleep 1
  22.    echo 6th, through a pipe | sed 's/.*/PIPE: &, to fd 5/' >&5
  23.    sleep 1
  24.    echo 7th, to fd 6 >&6
  25.    sleep 1
  26.    echo 8th, to fd 7 >&7
  27.    sleep 1
  28.    echo 9th, to fd 8 >&8
  29.   ) 4>&1 >&3 3>&- | while read a; do echo "FD4: $a"; done 1>&3 5>&- 6>&-
  30.  ) 5>&1 >&3 | while read a; do echo "FD5: $a"; done 1>&3 6>&-
  31. ) 6>&1 >&3 | while read a; do echo "FD6: $a"; done 3>&-
  32. rm -f /tmp/fifo1 /tmp/fifo2
  33. # Выясните, куда переназначены файловые дескрипторы каждой команды и подоболочки.
  34. exit 0