Redirections from script himself
You could plan redirections from the script itself:
#!/bin/bash
exec 1>>logfile.txt
exec 2>&1
/bin/ls -ld /tmp /tnt
Running this will create/append logfile.txt, containing:
/bin/ls: cannot access '/tnt': No such file or directory
drwxrwxrwt 2 root root 4096 Apr 5 11:20 /tmp
Or
#!/bin/bash
exec 1>>logfile.txt
exec 2>>errfile.txt
/bin/ls -ld /tmp /tnt
While create or append standard output to logfile.txt and create or append errors output to errfile.txt.
Log to many different files
You could create two different logfiles, appending to one overall log and recreating another last log:
#!/bin/bash
if [ -e lastlog.txt ] ;then
mv -f lastlog.txt lastlog.old
fi
exec 1> >(tee -a overall.log /dev/tty >lastlog.txt)
exec 2>&1
ls -ld /tnt /tmp
Running this script will
- if
lastlog.txtalready exist, rename them tolastlog.old(overwritinglastlog.oldif they exist). - create a new
lastlog.txt. - append everything to
overall.log - output everything to the terminal.
Simple and combined logs
#!/bin/bash
[ -e lastlog.txt ] && mv -f lastlog.txt lastlog.old
[ -e lasterr.txt ] && mv -f lasterr.txt lasterr.old
exec 1> >(tee -a overall.log combined.log /dev/tty >lastlog.txt)
exec 2> >(tee -a overall.err combined.log /dev/tty >lasterr.txt)
ls -ld /tnt /tmp
So you have
lastlog.txtlast run log filelasterr.txtlast run error filelastlog.oldprevious run log filelasterr.oldprevious run error fileoverall.logappended overall log fileoverall.errappended overall error filecombined.logappended overall error and log combined file.- still output to the terminal
And for interactive session, use stdbuf:
stdbuf: Regarding Fonic’ comment and after some test, I have to agree: with tee, stdbuf is useless. But …
If you plan to use this in *interactive* shell, you must tell `tee` to not buffering his input/output:
# Source this to multi-log your session
[ -e lasterr.txt ] && mv -f lasterr.txt lasterr.old
[ -e lastlog.txt ] && mv -f lastlog.txt lastlog.old
exec 2> >(exec stdbuf -i0 -o0 tee -a overall.err combined.log /dev/tty >lasterr.txt)
exec 1> >(exec stdbuf -i0 -o0 tee -a overall.log combined.log /dev/tty >lastlog.txt)
Once sourced this, you could try:
ls -ld /tnt /tmp
More complex sample
From my 3 remarks about how to Convert Unix timestamp to a date string
I’ve used more complex command to parse and reassemble squid‘s log in real time: As each line begin by an UNIX EPOCH with milliseconds, I split the line on 1st dot, add @ symbol before EPOCH SECONDS to
pass them to date -f - +%F %T then reassemble date‘s output and the rest of line with a dot by using paste -d ..
exec {datesfd}<> <(:)
tail -f /var/log/squid/access.log |
tee >(
exec sed -u 's/^([0-9]+)..*/@1/'|
stdbuf -o0 date -f - +%F %T >&$datesfd
) |
sed -u 's/^[0-9]+.//' |
paste -d . /dev/fd/$datesfd -
With date, stdbuf was required…
Some explanations about exec and stdbuf commands:
-
Running
forksby using$(...)or<(...)is done by running subshell wich will execute binaries in another subshell (subsubshell). Theexeccommand tell shell that there are not further command in script to be run, so binary (stdbuf ... tee) will be executed as replacement process, at same level (no need to reserve more memory for running another sub-process).From
bash‘s man page (man -P'less +/^ *exec ' bash):exec [-cl] [-a name] [command [arguments]] If command is specified, it replaces the shell. No new process is created....This is not really needed, but reduce system footprint.
-
From
stdbuf‘s man page:NAME stdbuf - Run COMMAND, with modified buffering operations for its standard streams.This will tell system to use unbuffered I/O for
teecommand. So all outputs will be updated immediately, when some input are coming.
Часто возникает необходимость, чтобы скрипт командного интерпретатора Bash выводил результат своей работы. По умолчанию он отображает стандартный поток данных — окно терминала. Это удобно для обработки результатов небольшого объёма или, чтобы сразу увидеть необходимые данные.
В интерпретаторе можно делать вывод в файл Bash. Применяется это для отложенного анализа или сохранения массивного результата работы сценария. Чтобы сделать это, используется перенаправление потока вывода с помощью дескрипторов.
Стандартные дескрипторы вывода
В системе GNU/Linux каждый объект является файлом. Это правило работает также для процессов ввода/вывода. Каждый файловый объект в системе обозначается дескриптором файла — неотрицательным числом, однозначно определяющим открытые в сеансе файлы. Один процесс может открыть до девяти дескрипторов.
В командном интерпретаторе Bash первые три дескриптора зарезервированы для специального назначения:
| Дескриптор | Сокращение | Название |
|---|---|---|
| 0 | STDIN | Стандартный ввод |
| 1 | STDOUT | Стандартный вывод |
| 2 | STDERR | Стандартный вывод ошибок |
Их предназначение — обработка ввода/вывода в сценариях. По умолчанию стандартным потоком ввода является клавиатура, а вывода — терминал. Рассмотрим подробно последний.
1. Перенаправление стандартного потока вывода
Для того, чтобы перенаправить поток вывода с терминала в файл, используется знак «больше» (>).
#!/bin/bash
echo "Строка 1"
echo "Промежуточная строка" > file
echo "Строка 2" > file
Как результат, «Строка 1» выводится в терминале, а в файл file записывается только «Строка 2»:

Связано это с тем, что > перезаписывает файл новыми данными. Для того, чтобы дописать информацию в конец файла, используется два знака «больше» (>>).
#!/bin/bash
echo "Строка 1"
echo "Промежуточная строка" > file
echo "Строка 2" >> file

Здесь «Промежуточная строка» перезаписала предыдущее содержание file, а «Строка 2» дописалась в его конец.
Если во время использования перенаправления вывода интерпретатор обнаружит ошибку, то он не запишет сообщение о ней в файл.
#!/bin/bash
ls badfile > file2
echo "Строка 2" >> file2

В данном случае ошибка была в том, что команда ls не смогла найти файл badfile, о чём Bash и сообщил. Но вывелось сообщение в терминал, а не записалось в файл. Всё потому, что использование перенаправления потоков указывает интерпретатору отделять мух от котлет ошибки от основной информации.
Это особенно полезно при выполнении сценариев в фоновом режиме, где приходится предусматривать вывод сообщений в журнал. Но так как ошибки в него писаться не будут, нужно отдельно перенаправлять поток ошибок для того, чтобы выполнить их вывод в файл Linux.
2. Перенаправление потока ошибок
В командном интерпретаторе для обработки сообщений об ошибках предназначен дескриптор STDERR, который работает с ошибками, сформированными как от работы интерпретатора, так и самим скриптом.
По умолчанию STDERR указывает в то же место, что и STDOUT, хотя для них и предназначены разные дескрипторы. Но, как было показано в примере, использование перенаправления заставляет Bash разделить эти потоки.
Чтобы выполнить перенаправление вывода в файл Linux для ошибок, следует перед знаком«больше» указать дескриптор 2.
#!/bin/bash
ls badfile 2> errors
echo "Строка 1" > file3
echo "Строка 2" >> file3

В результате работы скрипта создан файл errors, в который записана ошибка выполнения команды ls, а в file3 записаны предназначенные строки. Таким образом, выполнение сценария не сопровождается выводом информации в терминал.
Пример того, как одна команда возвращает и положительный результат, и ошибку:
ls -lh test badtest 2> errors

Команда ls попыталась показать наличие файлов test и badtest. Первый присутствовал в текущем каталоге, а второй — нет. Но сообщение об ошибке было записано в отдельный файл.
Если возникает необходимость выполнить вывод команды в файл Linux, включая её стандартный поток вывода и ошибки, стоит использовать два символа перенаправления, перед которыми стоит указывать необходимый дескриптор.
ls -lh test test2 badtest 2> errors 1> output

Результат успешного выполнения записан в файл output, а сообщение об ошибке — в errors.
По желанию можно выводить и ошибки, и обычные данные в один файл, используя &>.
ls -lh test badtest &> output

Обратите внимание, что Bash присваивает сообщениям об ошибке более высокий приоритет по сравнению с данными, поэтому в случае общего перенаправления ошибки всегда будут располагаться в начале.
Временные перенаправления в скриптах
Если есть необходимость в преднамеренном формировании ошибок в сценарии, можно каждую отдельную строку вывода перенаправлять в STDERR. Для этого достаточно воспользоваться символом перенаправления вывода, после которого нужно использовать & и номер дескриптора, чтобы перенаправить вывод в STDERR.
#!/bin/bash
echo "Это сообщение об ошибке" >&2
echo "Это нормальное сообщение"
При выполнении программы обычно нельзя будет обнаружить отличия:

Вспомним, что GNU/Linux по умолчанию направляет вывод STDERR в STDOUT. Но если при выполнении скрипта будет перенаправлен поток ошибок, то Bash, как и полагается, разделит вывод.

Этот метод хорошо подходит для создания собственных сообщений об ошибках в сценариях.
Постоянные перенаправления в скриптах
Если в сценарии необходимо перенаправить вывод в файл Linux для большого объёма данных, то указание способа вывода в каждой инструкции echo будет неудобным и трудоёмким занятием. Вместо этого можно указать, что в ходе выполнения данного скрипта должно осуществляться перенаправление конкретного дескриптора с помощью команды exec:
#!/bin/bash
exec 1> testout
echo "Это тест перенаправления всего вывода"
echo "из скрипта в другой файл"
echo "без использования временного перенаправления"

Вызов команды exec запускает новый командный интерпретатор и перенаправляет стандартный вывод в файл testout.
Также существует возможность перенаправлять вывод (в том числе и ошибок) в произвольном участке сценария:
#!/bin/bash
exec 2> testerror
echo "Это начально скрипта"
echo "И это первые две строки"
exec 1> testout
echo "Вывод сценария перенаправлен"
echo "из с терминала в другой файл"
echo "но эта строка записана в файл ошибок" >&2

Такой метод часто применяется при необходимости перенаправить лишь часть вывода скрипта в другое место, например в журнал ошибок.
Выводы
Перенаправление в скриптах Bash, чтобы выполнить вывод в файл Bash, является хорошим средством ведения различных журналов, особенно в фоновом режиме.
Использование временного и постоянного перенаправлений в сценариях позволяет создавать собственные сообщения об ошибках для записи в отличное от STDOUT место.

Статья распространяется под лицензией Creative Commons ShareAlike 4.0 при копировании материала ссылка на источник обязательна .
Всё в Linux — это файлы, в том числе — ввод и вывод. Операционная система идентифицирует файлы с использованием дескрипторов. Каждому процессу позволено иметь до девяти открытых дескрипторов файлов. Оболочка bash резервирует первые три дескриптора с идентификаторами 0, 1 и 2. Вот что они означают.
- 0, STDIN — стандартный поток ввода
- 1, STDOUT — стандартный поток вывода
- 2, STDERR — стандартный поток ошибок
STDIN
STDIN — это стандартный поток ввода оболочки. Для терминала стандартный ввод — это клавиатура. Когда используется символ перенаправления ввода — <, Linux заменяет дескриптор файла стандартного ввода на тот, который указан в команде. Система читает файл и обрабатывает данные так, будто они введены с клавиатуры.
Многие команды bash принимают ввод из STDIN, если в командной строке не указан файл, из которого надо брать данные. Например, это справедливо для команды cat.
Когда вы вводите команду cat в командной строке, не задавая параметров, она принимает ввод из STDIN. После того, как вы вводите очередную строку, cat просто выводит её на экран. И останавливается после того как получает EOF. EOF вводится нажатием сочетания клавиш Ctrl+D.
STDOUT
STDOUT — стандартный поток вывода оболочки. По умолчанию это — экран. Большинство bash-команд выводят данные в STDOUT, что приводит к их появлению в консоли. Данные можно перенаправить в файл, для этого используется символ перенаправления вывода >.
Можно осуществить перенаправление вывода в файл с добавлением в конец с помощью >>. При этом информация, хранящаяся в файле не будет удалена, а вся новая информация будет добавлена в конец этого файла.
STDERR
STDERR представляет собой стандартный поток ошибок оболочки. По умолчанию этот дескриптор указывает на то же самое, на что указывает STDOUT, именно поэтому при возникновении ошибки мы видим сообщение на экране.
Рассмотрим простой пример — нужно записать в файл data.txt содержимое директории data. Если директория не существует, сообщение об ошибке надо записать в файл error.txt. Чтобы этого добиться, нужно использовать команды перенаправления для соответствующих дескрипторов с указанием файлов, куда должны попадать ошибки и стандартный вывод:
$ ls data 1> data.txt 2> error.txt
Если надо, STDERR и STDOUT можно перенаправить в один и тот же файл, воспользовавшись &>:
$ ls data &> data-error.txt
Устаревшая форма записи:
$ ls data > data-error.txt 2>&1
Оболочка определяет порядок перенаправления потоков в командной строке слева направо, поэтому приведенный выше пример нельзя записать так:
$ ls data 2>&1 > data-error.txt
В этом случае сначала вывод ошибок перенаправляется в стандартный вывод (на экран), а потом стандартный вывод перенаправляется в файл data-error.txt. То есть, ошибки будут выведены в консоль, а данные — в файл.
Для дозаписи в файл data-error.txt
$ ls data &>> data-error.txt
Перенаправление вывода в скриптах
Существует два метода перенаправления вывода в сценариях командной строки:
- Временное перенаправление, или перенаправление вывода одной строки
- Постоянное перенаправление, или перенаправление всего вывода сценария
Временное перенаправление вывода
В сценарии можно перенаправить вывод отдельной строки в STDERR. Чтобы это сделать, достаточно использовать команду перенаправления, указав дескриптор STDERR, при этом перед номером дескриптора надо поставить символ амперсанда:
#!/bin/bash echo "This is an error" >&2 echo "This is normal output"
Если запустить этот скрипт, обе строки попадут на экран, так как, как вы уже знаете, по умолчанию ошибки выводятся туда же, куда и обычные данные.
$ ./test.sh This is an error This is normal output
Запустим скрипт так, чтобы вывод STDERR попадал в файл:
$ ./test.sh 2> error.txt This is normal output $ cat error.txt This is an error
Теперь обычный вывод делается в консоль, а сообщения об ошибках попадают в файл error.txt.
Постоянное перенаправление вывода
Если в скрипте нужно перенаправлять много выводимых на экран данных, добавлять >&2 к каждому вызову echo неудобно. Вместо этого можно задать перенаправление вывода в определённый дескриптор на время выполнения скрипта, воспользовавшись командой exec:
#!/bin/bash exec 1> output.txt echo "Это пример перенаправления всего" echo "вывода сценария в файл output.txt" echo "без перенаправления каждой строки"
После запуска сценария весь вывод будет перенаправлен в файл output.txt.
Команду exec можно использовать не только в начале скрипта, но и в других местах:
#!/bin/bash # ошибки перенаправляем в файл error.txt exec 2> error.txt # строка будет выведена на экран echo "Первый вывод сценария" # перенаправляем вывод в файл output.txt exec 1> output.txt # строка будет записана в файл output.txt echo "Второй вывод сценария" # строка будет записана в файл error.txt echo "Третий вывод сценария" >&2
Перенаправление ввода в скриптах
Для перенаправления ввода можно воспользоваться тем же способом, который использовался для перенаправления вывода. Например, команда exec позволяет сделать источником данных для STDIN какой-нибудь файл:
$ exec 0< data.txt
Эта команда указывает оболочке на то, что источником вводимых данных должен стать файл data.txt, а не обычный STDIN. Посмотрим на перенаправление ввода в действии:
#!/bin/bash exec 0< data.txt count=1 while read line ; do echo "Line #$count: $line" count=$(( $count + 1 )) done
Подавление вывода
Иногда надо сделать так, чтобы команды в скрипте, который, например, может исполняться как фоновый процесс, ничего не выводили на экран. Для этого можно перенаправить вывод в /dev/null.
Вот, например, как подавить вывод сообщений об ошибках:
$ ls -al badfile.txt file.txt 2> /dev/null
Тот же подход используется, если, например, надо очистить файл, не удаляя его:
$ cat /dev/null > data.txt
Поиск:
Bash • CLI • Linux • stderr • stdin • stdout • Поток
Из статьи вы узнаете про стандартные потоки ввода и вывода, и перенаправление этих потоков в файл или от одного процесса другому.
Стандартные потоки ввода вывода
В этом курсе мы работает в терминале, вводим какие-то команды и иногда получаем какой-нибудь вывод. То есть консольные утилиты получают от нас какую-то информацию и могут выводить нам информацию на терминал.
Я уже писал о том, что в Linux всё считается файлом. Из этого следует, когда команда выводит результат своей работы, она пишет в какой-то файл. А когда получает данные, она читает какой-то файл.
По умолчанию, файл, из которого осуществляется чтение, называется стандартным потоком ввода, а в который осуществляется запись — стандартным потоком вывода.
Также существует стандартный поток ошибок — это файл, в который процесс записывает ошибки, если они возникают при работе.
В Linux стандартные потоки это виртуальные файлы и по умолчанию стандартные потоки вывода ассоциированы с экраном терминала пользователя. Поэтому вывод результата или ошибок поступает на экран терминала. А стандартный поток ввода связан с клавиатурой терминала, поэтому чтение данных происходит с клавиатуры.
| Название | Файловый дескриптор |
Связанное устройство |
Файл |
|---|---|---|---|
| stdin стандартный поток ввода |
0 | клавиатура терминала |
/dev/stdin |
| stdout стандартный поток вывода |
1 | экран терминала |
/dev/stdout |
| stderr стандартный поток ошибок |
2 | экран терминала |
/dev/stderr |
Вот как эти файлы увидеть:
alex@deb-11:~$ ls -l /dev/std* lrwxrwxrwx 1 root root 15 сен 9 10:57 /dev/stderr -> /proc/self/fd/2 lrwxrwxrwx 1 root root 15 сен 9 10:57 /dev/stdin -> /proc/self/fd/0 lrwxrwxrwx 1 root root 15 сен 9 10:57 /dev/stdout -> /proc/self/fd/1 alex@deb-11:~$ ls -l /proc/self/fd/[0,1,2] lrwx------ 1 alex alex 64 сен 12 14:28 /proc/self/fd/0 -> /dev/pts/0 lrwx------ 1 alex alex 64 сен 12 14:28 /proc/self/fd/1 -> /dev/pts/0 lrwx------ 1 alex alex 64 сен 12 14:28 /proc/self/fd/2 -> /dev/pts/0 alex@deb-11:~$ ls -l /dev/pts/0 crw--w---- 1 alex tty 136, 0 сен 12 14:28 /dev/pts/0
Из вывода мы можем понять что файлы потоков это символические ссылки, ведущие на номера файловых дескрипторов. А эти файловые дескрипторы ведут на одно и тоже устройство – /dev/pts/0. Это устройство называется псевдо-терминалом. Именно этому псевдо-терминалу (pts/0) подключен я по ssh:
alex@deb-11:~$ loginctl list-sessions
SESSION UID USER SEAT TTY
83 1000 alex pts/0
1 sessions listed.
alex@deb-11:~$ w
14:43:19 up 3 days, 3:45, 1 user, load average: 0,00, 0,00, 0,00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
alex pts/0 172.28.80.14 10:46 0.00s 0.17s 0.00s w
И все эти потоки можно перенаправлять, например можно пустить:
- stdout не на терминал, а в файл;
- stdout от одного процесса на stdin другому;
- stdout в один файл, а stderr в в другой.
Про эти файлы можно почитать в официальном мануале здесь, или выполнив команду man stdin.
Перенаправление потоков stdout и stderr в файл
Перенаправление stdout в файл
Допустим мы запустили какую-то команду, которая выводит нам что-нибудь на экран терминала:
alex@deb-11:~$ id uid=1000(alex) gid=1000(alex) группы=1000(alex),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev)
Мы можем перенаправить результат в файл с помощью символа “>“:
alex@deb-11:~$ id > id.txt alex@deb-11:~$ cat id.txt uid=1000(alex) gid=1000(alex) группы=1000(alex),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev)
Как видим на терминале ничего показано не было, а всё записалось в файл. При этом, если бы файла не было то он создастся. А если бы файл был, то он пере-запишется, то есть все содержимое файла очищается и заменяется.
Если мы не хотим пере-записывать файл целиком, а хотим дописать в файл, то нужно использовать “>>“:
alex@deb-11:~$ id root >> id.txt alex@deb-11:~$ cat id.txt uid=1000(alex) gid=1000(alex) группы=1000(alex),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),109(netdev) uid=0(root) gid=0(root) группы=0(root)
Перенаправление stderr в файл
Если нам нужно перенаправить stderr в файл, то используется “2>“:
alex@deb-11:~$ touch test.txt alex@deb-11:~$ ls test.txt test2.txt ls: невозможно получить доступ к 'test2.txt': Нет такого файла или каталога test.txt alex@deb-11:~$ ls test.txt test2.txt 2> ls-error.txt test.txt alex@deb-11:~$ cat ls-error.txt ls: невозможно получить доступ к 'test2.txt': Нет такого файла или каталога
Как видим при таком перенаправлении stdout идет на терминал, а stderr в файл.
Тут как и с перенаправлением stdout:
- “2>” — перезапишет файл;
- “2>>” — допишет файл.
Перенаправление потоков stdout и stderr в файл одновременно
Вы можете простым образом перенаправить оба потока:
alex@deb-11:~$ ls test.txt test2.txt > ls-out.txt 2> ls-error.txt alex@deb-11:~$ cat ls-out.txt test.txt alex@deb-11:~$ cat ls-error.txt ls: невозможно получить доступ к 'test2.txt': Нет такого файла или каталога
А чтобы все перенаправить в один файл используется довольно интересная конструкция: > file 2>&1:
alex@deb-11:~$ ls test.txt test2.txt > ls-out.txt 2>&1 alex@deb-11:~$ cat ls-out.txt ls: невозможно получить доступ к 'test2.txt': Нет такого файла или каталога test.txt
То есть мы перенаправляем stdout в файл, а stderr в stdout, напомню что stdout имеет файловый дескриптор 1.
Есть такой файл в Linux – /dev/null, это как черная дыра, все что идет в /dev/null никуда не сохраняется. Во многих инструкциях можно найти примерно такую команду:
# команда > /dev/null 2>&1
Это делает процесс безмолвным, весь результат и все ошибки от выполнения подобной команды будут уходить в никуда.
Перенаправление stdout одного процесса на stdin другого
Допустим первая команда выводит какой-то результат, и нам нужно этот результат использовать как входные данные для следующей команды. В этом случае используется “|” (пайплайн (pipeline)).
Я очень долго путал stdin с параметрами команды, то есть я думал что:
alex@deb-11:~$ ls test.txt test.txt
Результат предыдущей команды “test.txt” пере-направится как параметр. То есть я думал что:
alex@deb-11:~$ ls test.txt | cat
будет равен:
alex@deb-11:~$ cat test.txt 1 2 3
Но на самом деле:
alex@deb-11:~$ ls test.txt | cat test.txt
Как же это работает на самом деле?
Pipeline заставляет cat читать не из файла а из stdout предыдущей команды. А первая команда пишет в stdout слово “test.txt“, вот и cat читает посимвольно слово “test.txt“.
Чтобы пайплайны работали, вторая команда должна уметь читать из stdin, а это умеют далеко не все утилиты. Но почти все утилиты, которые умеют читать данные из файла могут читать и из stdin. Как пример, могу привести следующие утилиты которые умеют принимать данные из stdin: cat, grep, less, tail, head, wc.
Вот еще один пример, найдем все файлы, в которых есть буква “l“:
alex@deb-11:~$ ls apache2_2.4.53-1~deb11u1_amd64.deb date.log ls-out.txt rootCA.srl site.key test.txt crash id.txt rootCA.crt site.crt sysadminium.cnf timer.sh crash.c ls-error.txt rootCA.key site.csr testfolder ulimit-t.sh alex@deb-11:~$ ls | grep l date.log ls-error.txt ls-out.txt rootCA.srl testfolder ulimit-t.sh
Итог
Мы узнали про стандартные потоки ввода и вывода: stdin, stdout, stderr. Научились перенаправить stdout и stderr в файл и перенаправлять stdout одной команды на stdin другой.
В статье все примеры были проведены на Debian 11, но всё точно также будет работать и в Ubuntu 22.04.
Сводка

Имя статьи
Объединение п перенаправление команд
Описание
Из статьи вы узнаете про стандартные потоки ввода и вывода, и перенаправление этих потоков в файл или от одного процесса другому
Фундаментальные основы Linux. Часть IV. Программные каналы и команды
Оригинал: Linux Fundamentals
Автор: Paul Cobbaut
Дата публикации: 16 октября 2014 г.
Перевод: А.Панин
Дата перевода: 15 декабря 2014 г.
Глава 16. Перенаправление потоков ввода/вывода
Одной из мощных возможностей командной оболочки системы Unix является механизм перенаправления потоков ввода/вывода с возможностью задействования программных каналов.
В данной главе даются пояснения относительно перенаправления стандартных потоков ввода, вывода и ошибок.
Потоки данных stdin, stdout и stderr
Командная оболочка bash поддерживает три типа базовых потоков данных; она принимает данные из стандартного потока ввода stdin (поток 0), отправляет данные в стандартный поток вывода stdout (поток 1), а также отправляет сообщения об ошибках в стандартный поток ошибок stderr (поток 2).
Приведенная ниже иллюстрация является графической интерпретацией этих трех потоков данных.
Клавиатура обычно служит источником данных для стандартного потока ввода stdin, в то время, как стандартные потоки вывода stdout и ошибок stderr используются для вывода данных. Новых пользователей Linux может смущать подобное разделение, так как не существует очевидного способа дифференцирования стандартных потоков вывода stdout и ошибок stderr. Опытные же пользователи знают о том, что разделение стандартных потоков вывода и ошибок может оказаться весьма полезным.
В следующем разделе будет рассказано о том, как осуществляется перенаправление упомянутых потоков данных.
Перенаправление стандартного потока вывода
Операция перенаправления потока данных stdout (>)
Перенаправление стандартного потока вывода stdout может быть осуществлено с помощью символа знака "больше". В том случае, если при разборе строки команды командная оболочка обнаруживает символ знака >, она удаляет данные из файла и перенаправлет данные из стандартного потока вывода в него.
Нотация > фактически является аббревиатурой для 1> (в данном случае стандартный поток вывода обозначается как поток номер 1).
[paul@RHELv4u3 ~]$ echo Сегодня холодно! Сегодня холодно! [paul@RHELv4u3 ~]$ echo Сегодня холодно! > winter.txt [paul@RHELv4u3 ~]$ cat winter.txt Сегодня холодно! [paul@RHELv4u3 ~]$
Обратите внимание на то, что командная оболочка bash фактически удаляет описание операции перенаправления потока данных из строки команды перед исполнением этой команды, представленной аргументом 0. Это значит, что в случае исполнения данной команды:
echo привет > greetings.txt
командная оболочка будет рассматривать только два аргумента (echo = аргумент 0, привет = аргумент 1). Описание операции перенаправления потока данных удаляется перед началом подсчета количества аргументов.
Содержимое выходного файла удаляется
В том случае, если в процессе разбора строки команды командная оболочка обнаружит символ знака >, содержимое указанного после него файла будет удалено! Ввиду того, что описанная процедура выполняется перед извлечением аргумента 0, содержимое файла будет удалено даже в случае неудачного исполнения команды!
[paul@RHELv4u3 ~]$ cat winter.txt Сегодня холодно! [paul@RHELv4u3 ~]$ zcho Сегодня холодно! > winter.txt -bash: zcho: команда не найдена.. [paul@RHELv4u3 ~]$ cat winter.txt [paul@RHELv4u3 ~]$
Параметр командной оболочки noclobber
Удаление содержимого файла при использовании оператора > может быть предотвращено путем установки параметра командной оболочки noclobber.
[paul@RHELv4u3 ~]$ cat winter.txt Сегодня холодно! [paul@RHELv4u3 ~]$ set -o noclobber [paul@RHELv4u3 ~]$ echo Сегодня холодно! > winter.txt -bash: winter.txt: не могу переписать уже существующий файл [paul@RHELv4u3 ~]$ set +o noclobber [paul@RHELv4u3 ~]$
Нейтрализация влияния параметра командной оболочки noclobber
Влияние параметра командной оболочки noclobber может быть нейтрализовано с помощью оператора >|.
[paul@RHELv4u3 ~]$ set -o noclobber [paul@RHELv4u3 ~]$ echo Сегодня холодно! > winter.txt -bash: winter.txt: не могу переписать уже существующий файл [paul@RHELv4u3 ~]$ echo Сегодня очень холодно! >| winter.txt [paul@RHELv4u3 ~]$ cat winter.txt Сегодня очень холодно! [paul@RHELv4u3 ~]$
Оператор дополнения >>
Следует использовать оператор >> для записи данных из стандартного потока вывода в конец файла без предварительного удаления содержимого этого файла.
[paul@RHELv4u3 ~]$ echo Сегодня холодно! > winter.txt [paul@RHELv4u3 ~]$ cat winter.txt Сегодня холодно! [paul@RHELv4u3 ~]$ echo Когда же наступит лето ? >> winter.txt [paul@RHELv4u3 ~]$ cat winter.txt Сегодня холодно! Когда же наступит лето ? [paul@RHELv4u3 ~]$
Перенаправление стандартного потока ошибок
Операция перенаправления потока данных stderr (2>)
Перенаправление стандартного потока ошибок осуществляется с помощью оператора 2>. Такое перенаправление может оказаться очень полезным для предотвращения заполнения вашего экрана сообщениями об ошибках.
В примере ниже показана методика перенаправления данных из стандартного потока вывода в файл, а данных из стандартного потока ошибок — в специальный файл устройства /dev/null. Запись 1> идентична записи >.
[paul@RHELv4u3 ~]$ find / > allfiles.txt 2> /dev/null [paul@RHELv4u3 ~]$
Операция перенаправления нескольких потоков данных 2>&1
Для перенаправления данных как из стандартного потока вывода, так и из стандартного потока ошибок в один и тот же файл следует использовать конструкцию 2>&1.
[paul@RHELv4u3 ~]$ find / > allfiles_and_errors.txt 2>&1 [paul@RHELv4u3 ~]$
Помните о том, что последовательность операций перенаправления потоков данных имеет значение. К примеру, команда
ls > dirlist 2>&1
позволяет перенаправить как данные из стандартного потока вывода (с файловым дескриптором 1), так и данные из стандартного потока ошибок (с файловым дескриптором 2) в файл dirlist, в то время, как команда
ls 2>&1 > dirlist
позволяет перенаправить только данные из стандартного потока вывода в файл dirlist, так как с помощью данной команды осуществляется копирование дескриптора стандартного потока вывода в дескриптор стандартного потока ошибок перед тем, как стандартный поток вывода перенаправляется в файл dirlist.
Перенаправление стандартного потока вывода и программные каналы
По умолчанию вы не можете использовать утилиту grep для обработки данных стандартного потока ошибок stderr приложения при использовании программных каналов в рамках строки команды, так как данная утилита получает данные исключительно из стандартного потока вывода stdout приложения.
paul@debian7:~$ rm file42 file33 file1201 | grep file42 rm: невозможно удалить "file42": Нет такого файла или каталога rm: невозможно удалить "file33": Нет такого файла или каталога rm: невозможно удалить "file1201": Нет такого файла или каталога
С помощью конструкции 2>&1 вы можете переправить данные из стандартного потока ошибок stderr в стандартный поток вывода stdout приложения. Это обстоятельство позволяет обрабатывать передаваемые посредством программного канала данные из обоих потоков с помощью следующей команды.
paul@debian7:~$ rm file42 file33 file1201 2>&1 | grep file42 rm: невозможно удалить "file42": Нет такого файла или каталога
Вы не можете одновременно использовать конструкции 1>&2 и 2>&1 для осуществления обмена файловых дескрипторов между стандартным потоком вывода stdout и стандартным потоком ошибок stderr.
paul@debian7:~$ rm file42 file33 file1201 2>&1 1>&2 | grep file42 rm: невозможно удалить "file42": Нет такого файла или каталога paul@debian7:~$ echo file42 2>&1 1>&2 | sed 's/file42/FILE42/' FILE42
Вам потребуется третий поток данных для осуществления обмена файловых дескрипторов между стандартным потоком вывода stdout и стандартным потоком ошибок stderr перед символом для создания программного канала.
paul@debian7:~$ echo file42 3>&1 1>&2 2>&3 | sed 's/file42/FILE42/' file42 paul@debian7:~$ rm file42 3>&1 1>&2 2>&3 | sed 's/file42/FILE42/' rm: невозможно удалить "FILE42": Нет такого файла или каталога
Объединение стандартных потоков вывода stdout и ошибок stderr
Конструкция &> позволяет объединить стандартные потоки вывода stdout и ошибок stderr в рамках одного потока данных (причем данные будут сохраняться в файле).
paul@debian7:~$ rm file42 &> out_and_err paul@debian7:~$ cat out_and_err rm: невозможно удалить "file42": Нет такого файла или каталога paul@debian7:~$ echo file42 &> out_and_err paul@debian7:~$ cat out_and_err file42 paul@debian7:~$
Перенаправление стандартного потока ввода
Операция перенаправления потока данных stdin (<)
Перенаправление стандартного потока ввода stdin осуществляется с помощью оператора < (являющегося краткой версией оператора 0<).
[paul@RHEL4b ~]$ cat < text.txt one two [paul@RHEL4b ~]$ tr 'onetw' 'ONEZZ' < text.txt ONE ZZO [paul@RHEL4b ~]$
Структура < here document
Структура here document (иногда называемая структурой here-is-document) является механизмом для ввода данных до момента обнаружения определенной последовательности символов (обычно EOF). Маркер EOF может быть либо введен вручную, либо вставлен автоматически при нажатии комбинации клавиш Ctrl-D.
[paul@RHEL4b ~]$ cat <EOF > text.txt > один > два > EOF [paul@RHEL4b ~]$ cat text.txt один два [paul@RHEL4b ~]$ cat <brol > text.txt > brel > brol [paul@RHEL4b ~]$ cat text.txt brel [paul@RHEL4b ~]$
Структура < here string
Структура here string может использоваться для непосредственной передачи строк команде. При использовании данной структуры достигается такой же эффект, как и при использовании команды echo строка | команда (но вы сможете избежать создания одного дополнительного процесса).
paul@ubu1110~$ base64 < linux-training.be bGludXgtdHJhaW5pbmcuYmUK paul@ubu1110~$ base64 -d << bGludXgtdHJhaW5pbmcuYmUK linux-training.be
Для получения дополнительной информации об алгоритме base64 следует обратиться к стандарту rfc 3548.
Неоднозначное перенаправление потоков ввода/вывода
Командная оболочка будет осуществлять разбор всей строки команды перед осуществлением перенаправления потоков ввода/вывода. Следующая команда является хорошо читаемой и корректной:
cat winter.txt > snow.txt 2> errors.txt
Но следующая команды также является корректной, хотя и хуже читается:
2> errors.txt cat winter.txt > snow.txt
Даже следующая команда будет прекрасно интерпретироваться командной оболочкой:
< winter.txt > snow.txt 2> errors.txt cat
Быстрая очистка содержимого файла
Так какой же самый быстрый способ очистки содержимого файла?
>foo
А какой самый быстрый способ очистки содержимого файла в случае активации параметра командной оболочки noclobber?
>|bar
Практическое задание: перенаправление потоков ввода/вывода
-
1. Активируйте параметр командной оболочки
noclobber. -
2. Проверьте, активирован ли параметр
noclobber, повторив вызов команды вывода содержимого директорииlsдля директории/etcс перенаправлением данных из стандартного потока вывода в файл. -
3. Какой из символов представляет параметр
noclobberв списке всех параметров командной оболочки. -
4. Деактивируйте параметр
noclobber. -
5. Убедитесь в том, что вы имеете доступ к двум командным оболочкам, открытым на одном компьютере. Создайте пустой файл
tailing.txt. После этого выполните командуtail -f tailing.txt. Используйте вторую командную оболочку длядобавлениястроки текста в этот файл. Убедитесь в том, что эта строка была выведена в первой командной оболочке. -
6. Создайте файл, содержащий имена пяти людей. Используйте команду
catи механизм перенаправления потоков ввода/вывода для создания файла, а также структуруhere documentдля завершения ввода.
Корректная процедура выполнения практического задания: перенаправление потоков ввода/вывода
-
1. Активируйте параметр командной оболочки
noclobber. -
2. Проверьте, активирован ли параметр
noclobber, повторив вызов команды вывода содержимого директорииlsдля директории/etcс перенаправлением данных из стандартного потока вывода в файл. -
ls /etc > etc.txt ls /etc > etc.txt (команда не должна работать)
-
3. Какой из символов представляет параметр
noclobberв списке всех параметров командной оболочки. -
echo $- (параметр noclobber представлен символом C)
-
4. Деактивируйте параметр
noclobber. -
5. Убедитесь в том, что вы имеете доступ к двум командным оболочкам, открытым на одном компьютере. Создайте пустой файл
tailing.txt. После этого выполните командуtail -f tailing.txt. Используйте вторую командную оболочку длядобавлениястроки текста в этот файл. Убедитесь в том, что эта строка была выведена в первой командной оболочке. -
paul@deb503:~$ > tailing.txt paul@deb503:~$ tail -f tailing.txt hello world в другой командной оболочке: paul@deb503:~$ echo hello >> tailing.txt paul@deb503:~$ echo world >> tailing.txt
-
6. Создайте файл, содержащий имена пяти людей. Используйте команду
catи механизм перенаправления потоков ввода/вывода для создания файла, а также структуруhere documentдля завершения ввода. -
paul@deb503:~$ cat > tennis.txt < ace > Justine Henin > Venus Williams > Serena Williams > Martina Hingis > Kim Clijsters > ace paul@deb503:~$ cat tennis.txt Justine Henin Venus Williams Serena Williams Martina Hingis Kim Clijsters paul@deb503:~$