Глава 16. Перенаправление ввода/вывода


В системе по-умолчанию всегда открыты три "файла"stdin (клавиатура), stdout (экран) и stderr (вывод сообщений об ошибках на экран). Эти, и любые другие открытые файлы, могут быть перенапрвлены. В данном случае, термин "перенаправление" означает получить вывод из файла, команды, программы, сценария или даже отдельного блока в сценарии (см. Пример 3-1 и Пример 3-2) и передать его на вход в другой файл, команду, программу или сценарий.

С каждым открытым файлом связан дескриптор файла. [45] Дескрипторы файлов stdin, stdout и stderr — 0, 1 и 2, соответственно. При открытии дополнительных файлов, дескрипторы с 3 по 9 остаются незанятыми. Иногда дополнительные дескрипторы могут сослужить неплохую службу, временно сохраняя в себе ссылку на stdin, stdout или stderr. [46] Это упрощает возврат дескрипторов в нормальное состояние после сложных манипуляций с перенаправлением и перестановками (см. Пример 16-1).

  1.    COMMAND_OUTPUT >
  2.       # Перенаправление stdout (вывода) в файл.
  3.       # Если файл отсутствовал, то он создется, иначе — перезаписывается.
  4.       ls -lR > dir-tree.list
  5.       # Создает файл, содержащий список дерева каталогов.
  6.    : > filename
  7.       # Операция > усекает файл "filename" до нулевой длины.
  8.       # Если до выполнения операции файла не существовало,
  9.       # то создается новый файл с нулевой длиной (тот же эффект дает команда 'touch').
  10.       # Символ : выступает здесь в роли местозаполнителя, не выводя ничего.
  11.    > filename
  12.       # Операция > усекает файл "filename" до нулевой длины.
  13.       # Если до выполнения операции файла не существовало,
  14.       # то создается новый файл с нулевой длиной (тот же эффект дает команда 'touch').
  15.       # (тот же результат, что и выше — ": >", но этот вариант неработоспособен
  16.       # в некоторых командных оболочках.)
  17.    COMMAND_OUTPUT >>
  18.       # Перенаправление stdout (вывода) в файл.
  19.       # Создает новый файл, если он отсутствовал, иначе — дописывает в конец файла.
  20.       # Однострочные команды перенаправления
  21.       # (затрагивают только ту строку, в которой они встречаются):
  22.       # --------------------------------------------------------------------
  23.    1>filename
  24.       # Перенаправление вывода (stdout) в файл "filename".
  25.    1>>filename
  26.       # Перенаправление вывода (stdout) в файл "filename", файл открывается в режиме добавления.
  27.    2>filename
  28.       # Перенаправление stderr в файл "filename".
  29.    2>>filename
  30.       # Перенаправление stderr в файл "filename", файл открывается в режиме добавления.
  31.    &>filename
  32.       # Перенаправление stdout и stderr в файл "filename".
  33.       #==============================================================================
  34.       # Перенаправление stdout, только для одной строки.
  35.       LOGFILE=script.log
  36.       echo "Эта строка будет записана в файл \"$LOGFILE\"." 1>$LOGFILE
  37.       echo "Эта строка будет добавлена в конец файла \"$LOGFILE\"." 1>>$LOGFILE
  38.       echo "Эта строка тоже будет добавлена в конец файла \"$LOGFILE\"." 1>>$LOGFILE
  39.       echo "Эта строка будет выведена на экран и не попадет в файл \"$LOGFILE\"."
  40.       # После каждой строки, сделанное перенаправление автоматически "сбрасывается".
  41.       # Перенаправление stderr, только для одной строки.
  42.       ERRORFILE=script.errors
  43.       bad_command1 2>$ERRORFILE       #  Сообщение об ошибке запишется в $ERRORFILE.
  44.       bad_command2 2>>$ERRORFILE      #  Сообщение об ошибке добавится в конец $ERRORFILE.
  45.       bad_command3                    #  Сообщение об ошибке будет выведено на stderr,
  46.                                       #+ и не попадет в $ERRORFILE.
  47.       # После каждой строки, сделанное перенаправление также автоматически "сбрасывается".
  48.       #==============================================================================
  49.    2>&1
  50.       # Перенаправляется stderr на stdout.
  51.       # Сообщения об ошибках передаются туда же, куда и стандартный вывод.
  52.    i>&j
  53.       # Перенаправляется файл с дескриптором i в j.
  54.       # Вывод в файл с дескриптором i передается в файл с дескриптором j.
  55.    >&j
  56.       # Перенаправляется  файл с дескриптором 1 (stdout) в файл с дескриптором j.
  57.       # Вывод на stdout передается в файл с дескриптором j.
  58.    0< FILENAME
  59.     < FILENAME
  60.       # Ввод из файла.
  61.       # Парная команде ">", часто встречается в комбинации с ней.
  62.       #
  63.       # grep search-word <filename
  64.    [j]<>filename
  65.       # Файл "filename" открывается на чтение и запись, и связывается с дескриптором "j".
  66.       # Если "filename" отсутствует, то он создается.
  67.       # Если дескриптор "j" не указан, то, по-умолчанию, бередся дескриптор 0, stdin.
  68.       #
  69.       # Как одно из применений этого — запись в конкретную позицию в файле.
  70.       echo 1234567890 > File    # Записать строку в файл "File".
  71.       exec 3<> File       # Открыть "File" и связать с дескриптором 3.
  72.       read -n 4 <&3             # Прочитать 4 символа.
  73.       echo -n . >&3             # Записать символ точки.
  74.       exec 3>&-                 # Закрыть дескриптор 3.
  75.       cat File                  # ==> 1234.67890
  76.       # Произвольный доступ, да и только!
  77.    |
  78.       # Конвейер (канал).
  79.       # Универсальное средство для объединения команд в одну цепочку.
  80.       # Похоже на ">", но на самом деле — более обширная.
  81.       # Используется для объединения команд, сценариев, файлов и программ в одну цепочку (конвейер).
  82.       cat *.txt | sort | uniq > result-file
  83.       # Содержимое всех файлов .txt сортируется, удаляются повторяющиеся строки,
  84.       # результат сохраняется в файле "result-file".

Операции перенаправления и/или конвейеры могут комбинироваться в одной командной строке.

  1. command < input-file > output-file
  2. command1 | command2 | command3 > output-file
См. Пример 12-26 и Пример A-17.

Допускается перенаправление нескольких потоков в один файл.

  1. ls -yz >> command.log 2>&1
  2. # Сообщение о неверной опции "yz" в команде "ls" будет записано в файл "command.log".
  3. # Поскольку stderr перенаправлен в файл.
  4. #  Обратите внимание: следующая строка даст иной результат.
  5. ls -yz 2>&1 >> command.log
  6. #  Сообщение об ошибке не попадет в файл.
  7. #  Если производится перенаправление обоих устройств, stdout и stderr,
  8. #+ то порядок действий изменяется.


Закрытие дескрипторов файлов

n<&-

Закрыть дескриптор входного файла n.

0<&-, <&-

Закрыть stdin.

n>&-

Закрыть дескриптор выходного файла n.

1>&-, >&-

Закрыть stdout.

Дочерние процессы наследуют дескрипторы открытых файлов. По этой причине и работают конвейеры. Чтобы предотвратить наследование дескрипторов — закройте их перед запуском дочернего процесса.

  1. # В конвейер передается только stderr.
  2. exec 3>&1                              # Сохранить текущее "состояние" stdout.
  3. ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Закрыть дескр. 3 для 'grep' (но не для 'ls').
  4. #              ^^^^   ^^^^
  5. exec 3>&-                              # Теперь закрыть его для оставшейся части сценария.
  6. # Спасибо S.C.


Дополнительные сведения о перенаправлении ввода/вывода вы найдете в Приложение E.


[45]    дескриптор файла — это просто число, по которому система идентифицирует открытые файлы. Рассматривайте его как упрощенную версию указателя на файл.

[46]    При использрвании дескриптора с номером 5 могут возникать проблемы. Когда Bash порождает дочерний процесс, например командой exec, то дочерний процесс наследует дескриптор 5 как "открытый" (см. архив почты Чета Рамея (Chet Ramey), SUBJECT: RE: File descriptor 5 is held open) Поэтому, лучше не использовать этот дескриптор.