Из статьи вы узнаете про стандартные потоки ввода и вывода, и перенаправление этих потоков в файл или от одного процесса другому.

Стандартные потоки ввода вывода

В этом курсе мы работает в терминале, вводим какие-то команды и иногда получаем какой-нибудь вывод. То есть консольные утилиты получают от нас какую-то информацию и могут выводить нам информацию на терминал.

Я уже писал о том, что в Linux всё считается файлом. Из этого следует, когда команда выводит результат своей работы, она пишет в какой-то файл. А когда получает данные, она читает какой-то файл.

По умолчанию, файл, из которого осуществляется чтение, называется стандартным потоком ввода, а в который осуществляется запись — стандартным потоком вывода.

Также существует стандартный поток ошибок — это файл, в который процесс записывает ошибки, если они возникают при работе.

В Linux стандартные потоки это виртуальные файлы и по умолчанию стандартные потоки вывода ассоциированы с экраном терминала пользователя. Поэтому вывод результата или ошибок поступает на экран терминала. А стандартный поток ввода связан с клавиатурой терминала, поэтому чтение данных происходит с клавиатуры.

НазваниеФайловый
дескриптор
Связанное
устройство
Файл
stdin
стандартный поток ввода
0клавиатура
терминала
/dev/stdin
stdout
стандартный поток вывода
1экран
терминала
/dev/stdout
stderr
стандартный поток ошибок
2экран
терминала
/dev/stderr
Таблица файлов стандартных потоков в Linux

Вот как эти файлы увидеть:

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. Как пример, могу привести следующие утилиты которые умеют принимать данные из stdincat, 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.


Сводка
Объединение п перенаправление команд
Имя статьи
Объединение п перенаправление команд
Описание
Из статьи вы узнаете про стандартные потоки ввода и вывода, и перенаправление этих потоков в файл или от одного процесса другому

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *