Что такое конвейер в linux

Обновлено: 05.07.2024

Операционные системы Unix традиционно используют такие понятия, как стандартный ввод, вывод и вывод ошибки. Чаще всего ввод — это клавиатура, а вывод это на кран. Но конечно же мы можем выводить исполнение какой-то команды в файл и передавать другой команде, потому что работая в Linux , создается такая последовательность из команд, т.е результат предыдущей мы отправляем следующей и т.д

Целью данной статьи является рассмотреть:

  • Перенаправление стандартных ввода, вывода и ошибок;
  • Передача вывода одной команды в качестве аргументов другой;
  • Получение выходных данных в файл и на стандартный вывод;

Для начала воспользуемся командой wc которая посчитает, количество слов, символов и строк в файле wc test.txt.

Мы можем указать данной команде другой input. Мы можем ей сказать взять информацию из файла, т.е. записать вот таким образом wc < test.txt т.е. данной команде передать информацию из этого файла. И данная команда отработав посчитает в принципе то же самое. В таком варианте команда не знает с каким файлом она работает ей просто поступает вывод из файла. Файл выводит свой результат этой команде. Такая стрелочка редко используется, чаще используется стрелка в другую сторону. Например, мы можем список файлов вывести командой ls. А можем сказать, чтобы данная команда отправила результат не на наш стандартный вывод т.к. результат всех наших команд по умолчанию выводится в консоль, а например в файл ls > list.txt. По сути означает выполнить команду, а результат передать в файл. Фал можно посмотреть командой cat list.txt.

И мы можем убедится, что в данном файле находится перечень, всего что находилось в данной папке. Если мы выполним еще раз команду ls > list.txt, то данный файл каждый раз будет перезаписываться. Если же мы хотим, чтобы наш файл не перезаписывался, а дописывался, используем другую стрелочку ls >> list.txt.

И теперь вы можете видеть, что файл стал больше. Т.е. у нас записалось, то что было, а затем еще раз добавилось. Если опять выполнить команду со стрелочками >> , то опять допишется информация в файл. Вот таким образом работают “стрелочки”.

Стандартный вывод ошибок.

Следовательно, на экране мы уже не видим ошибки, потому что она отправилась в указанный файл.

Кстати мы можем объединить стандартный вывод команды и стандартный вывод ошибки. Например: ls bob > result.txt 2> error.txt. Выведи содержимое папки bob в файл result.txt, а если возникнет ошибка внеси в файл error.txt.

Конвейер

Конвейер умеет передавать выходные данные из одной программы, как входные данные для другой. Т.е. выполняется команда, мы получаем результат и передаем эти данные далее на обработку другой программе.

Но есть другая команда tee которая позволяет работать немного удобнее. Например: ls | tee output.txt. Те данная команда выводит информацию сразу на экран и в указанный файл. Что достаточно удобно с точки зрения работы с выводами.

И еще одна команда xargs – она построчно работает с выводами. Если у нас есть какая-то команда, которая выдает нам вывод в виде нескольких строк ответа, то мы можем эти строки построчно передавать этой команде, т.е. не одной кашей, а построчно. Например find . –name “*.txt” найти все файлы в текущем каталоге с расширением txt. И если бы мы захотели удалить все эти файлы нам бы пришлось построчно их удалять, но мы можем сказать, чтобы выходные данные были переданы по конвейеру xargs и удалить.

Pipe (конвеер) – это однонаправленный канал межпроцессного взаимодействия. Термин был придуман Дугласом Макилроем для командной оболочки Unix и назван по аналогии с трубопроводом. Конвейеры чаще всего используются в shell-скриптах для связи нескольких команд путем перенаправления вывода одной команды (stdout) на вход (stdin) последующей, используя символ конвеера ‘|’:

grep выполняет регистронезависимый поиск строки “error” в файле log, но результат поиска не выводится на экран, а перенаправляется на вход (stdin) команды wc, которая в свою очередь выполняет подсчет количества строк.

Логика

Конвеер обеспечивает асинхронное выполнение команд с использованием буферизации ввода/вывода. Таким образом все команды в конвейере работают параллельно, каждая в своем процессе.

Размер буфера начиная с ядра версии 2.6.11 составляет 65536 байт (64Кб) и равен странице памяти в более старых ядрах. При попытке чтения из пустого буфера процесс чтения блокируется до появления данных. Аналогично при попытке записи в заполненный буфер процесс записи будет заблокирован до освобождения необходимого места.
Важно, что несмотря на то, что конвейер оперирует файловыми дескрипторами потоков ввода/вывода, все операции выполняются в памяти, без нагрузки на диск.
Вся информация, приведенная ниже, касается оболочки bash-4.2 и ядра 3.10.10.

Простой дебаг


Утилита strace позволяет отследить системные вызовы в процессе выполнения программы:
Видно, что для создания конвеера используется системный вызов pipe(), а также, что оба процесса выполняются параллельно в разных потоках.

Исходный код, уровень 1, shell

Т. к. лучшая документация — исходный код, обратимся к нему. Bash использует Yacc для парсинга входных команд и возвращает ‘command_connect()’, когда встречает символ ‘|’.
parse.y:
Также здесь мы видим обработку пары символов ‘|&’, что эквивалентно перенаправлению как stdout, так и stderr в конвеер. Далее обратимся к command_connect():make_cmd.c:
где connector это символ ‘|’ как int. При выполнении последовательности команд (связанных через ‘&’, ‘|’, ‘;’, и т. д.) вызывается execute_connection():execute_cmd.c:

PIPE_IN и PIPE_OUT — файловые дескрипторы, содержащие информацию о входном и выходном потоках. Они могут принимать значение NO_PIPE, которое означает, что I/O является stdin/stdout.
execute_pipeline() довольно объемная функция, имплементация которой содержится в execute_cmd.c. Мы рассмотрим наиболее интересные для нас части.
execute_cmd.c:
Таким образом, bash обрабатывает символ конвейера путем системного вызова pipe() для каждого встретившегося символа ‘|’ и выполняет каждую команду в отдельном процессе с использованием соответствующих файловых дескрипторов в качестве входного и выходного потоков.

Исходный код, уровень 2, ядро

Обратимся к коду ядра и посмотрим на имплементацию функции pipe(). В статье рассматривается ядро версии 3.10.10 stable.
fs/pipe.c (пропущены незначительные для данной статьи участки кода):
Если вы обратили внимание, в коде идет проверка на флаг O_NONBLOCK. Его можно выставить используя операцию F_SETFL в fcntl. Он отвечает за переход в режим без блокировки I/O потоков в конвеере. В этом режиме вместо блокировки процесс чтения/записи в поток будет завершаться с errno кодом EAGAIN.

Максимальный размер блока данных, который будет записан в конвейер, равен одной странице памяти (4Кб) для архитектуры arm:
arch/arm/include/asm/limits.h:
Для ядер >= 2.6.35 можно изменить размер буфера конвейера:
Максимально допустимый размер буфера, как мы видели выше, указан в файле /proc/sys/fs/pipe-max-size.

Tips & trics

В примерах ниже будем выполнять ls на существующую директорию Documents и два несуществующих файла: ./non-existent_file и. /other_non-existent_file.

Перенаправление и stdout, и stderr в pipe

или же можно использовать комбинацию символов ‘|&’ (о ней можно узнать как из документации к оболочке (man bash), так и из исходников выше, где мы разбирали Yacc парсер bash):

Перенаправление _только_ stderr в pipe

Shoot yourself in the foot
Важно соблюдать порядок перенаправления stdout и stderr. Например, комбинация ‘>/dev/null 2>&1′ перенаправит и stdout, и stderr в /dev/null.

Получение корректного кода завершения конвейра

По умолчанию, код завершения конвейера — код завершения последней команды в конвеере. Например, возьмем исходную команду, которая завершается с ненулевым кодом:
И поместим ее в pipe:
Теперь код завершения конвейера — это код завершения команды wc, т.е. 0.

Обычно же нам нужно знать, если в процессе выполнения конвейера произошла ошибка. Для этого следует выставить опцию pipefail, которая указывает оболочке, что код завершения конвейера будет совпадать с первым ненулевым кодом завершения одной из команд конвейера или же нулю в случае, если все команды завершились корректно:
Shoot yourself in the foot
Следует иметь в виду “безобидные” команды, которые могут вернуть не ноль. Это касается не только работы с конвейерами. Например, рассмотрим пример с grep:
Здесь мы печатаем все найденные строки, приписав ‘new_’ в начале каждой строки, либо не печатаем ничего, если ни одной строки нужного формата не нашлось. Проблема в том, что grep завершается с кодом 1, если не было найдено ни одного совпадения, поэтому если в нашем скрипте выставлена опция pipefail, этот пример завершится с кодом 1:
В больших скриптах со сложными конструкциями и длинными конвеерами можно упустить этот момент из виду, что может привести к некорректным результатам.

Программа обычно ценна тем, что может обрабатывать данные: принимать одно, на выходе выдавать другое, причём в качестве данных может выступать практически что угодно: текст, числа, звук, видео. Потоки входных и выходных данных для команды называются ввод и вывод. Потоков ввода и вывода у каждой программы может быть и по несколько. В Linux каждый процесс, при создании в обязательном порядке получает так называемые стандартный ввод (standard input, stdin) и стандартный вывод (standard output, stdout) и стандартный вывод ошибок (standard error, stderr).

Стандартные потоки ввода/вывода предназначены в первую очередь для обмена текстовой информацией. Тут даже не важно, кто общается с помощью текстов: человек с программой или программы междй собой — главное, чтобы у них был канал передачи данных и чтобы они говорили « на одном языке ».

При работе с командной строкой, стандартный ввод командной оболочки связан с клавиатурой, а стандартный вывод и вывод ошибок — с экраном монитора (или окном эмулятора терминала). Покажем на примере простейшей команды — cat. Обычно команда cat читает данные из всех файлов, которые указаны в качестве её параметров, и посылает считанное непосредственно в стандартный вывод (stdout). Следовательно, команда

выведет на экран сначала содержимое файла history-final , а затем — файла masters-thesis .

Однако если имя файла не указано, программа cat читает входные данные из stdin и немедленно возвращает их в stdout (никак не изменяя). Данные проходят через cat , как через трубу. Приведём пример:

Каждую строчку, вводимую с клавиатуры, программа cat немедленно возвращает на экран. При вводе информации со стандартного ввода конец текста сигнализируется вводом специальной комбинации клавиш, как правило Ctrl - D .

Приведём другой пример. Команда sort читает строки вводимого текста (также из stdin, если не указано ни одного имени файла) и выдаёт набор этих строк в упорядоченном виде на stdout. Проверим её действие.

Как видно, после нажатия Ctrl - D , sort вывела строки упорядоченными в алфавитном порядке.

Перенаправление ввода и вывода

Можно увидеть, что результат работы команды sort не выводится на экран, однако он сохраняется в файле с именем shopping-list . Выведем на экран содержимое этого файла:

Однако можно поступить иначе, перенаправив не только стандартный вывод, но и стандартный ввод утилиты из файла, используя для этого символ < :

Результат команды sort < items эквивалентен команде sort items , однако первая из них демонстрирует следующее: при выдаче команды sort < items система ведёт себя так, как если бы данные, которые содержатся в файле items , были введены со стандартного ввода. Перенаправление осуществляется командной оболочкой. Команде sort не сообщалось имя файла items : эта команда читала данные из своего стандартного ввода, как если бы мы вводили их с клавиатуры.

Введём понятие фильтра. Фильтром является программа, которая читает данные из стандартного ввода, некоторым образом их обрабатывает и результат направляет на стандартный вывод. Когда применяется перенаправление, в качестве стандартного ввода и вывода могут выступать файлы. Как указывалось выше, по умолчанию, stdin и stdout относятся к клавиатуре и к экрану соответственно. Программа sort является простым фильтром — она сортирует входные данные и посылает результат на стандартный вывод. Совсем простым фильтром является программа cat — она ничего не делает с входными данными, а просто пересылает их на выход.

Использование состыкованных команд (конвейер)

Выше уже демонстрировалось, как использовать программу sort в качестве фильтра. В этих примерах предполагалось, что исходные данные находятся в некотором файле или что эти исходные данные будут введены с клавиатуры (стандартного ввода). Однако как поступить, если вы хотите отсортировать данные, которые являются результатом работы какой-либо другой команды, например, ls ?

Будем сортировать данные в обратном алфавитном порядке; это делается опцией -r команды sort . Если вы хотите перечислить файлы в текущем каталоге в обратном алфавитном порядке, один из способов сделать это будет таким. Применим сначала команду ls :

Теперь перенаправляем выход команды ls в файл с именем file-list

Здесь выход команды ls сохранен в файле, а после этого этот файл был обработан командой sort -r . Однако этот путь является неизящным и требует использования временного файла для хранения выходных данных программы ls .

Эта команда короче, чем совокупность команд, и её проще набирать.

Рассмотрим другой полезный пример. Команда

выдаёт длинный список файлов. Большая часть этого списка пролетает по экрану слишком быстро, чтобы содержимое этого списка можно было прочитать. Попробуем использовать команду more для того, чтобы выводить этот список частями:

Теперь можно этот список « перелистывать ».

Можно пойти дальше и состыковать более двух команд. Рассмотрим команду head , которая является фильтром следующего свойства: она выводит первые строки из входного потока (в нашем случае на вход будет подан выход от нескольких состыкованных команд). Если мы хотим вывести на экран последнее по алфавиту имя файла в текущем каталоге, можно использовать следующую длинную команду:

где команда head -1 выводит на экран первую строку получаемого ей входного потока строк (в нашем случае поток состоит из данных от команды ls ), отсортированных в обратном алфавитном порядке.

Недеструктивное перенаправление вывода

Эффект от использования символа > для перенаправления вывода файла является деструктивным; иными словами, команда

уничтожит содержимое файла file-list , если этот файл ранее существовал, и создаст на его месте новый файл. Если вместо этого перенаправление будет сделано с помощью символов >> , то вывод будет приписан в конец указанного файла, при этом исходное содержимое файла не будет уничтожено. Например, команда

приписывает вывод команды ls в конец файла file-list .

Следует иметь в виду, что перенаправление ввода и вывода и стыкование команд осуществляется командными оболочками, которые поддерживают использование символов > , >> и | . Сами команды не способны воспринимать и интерпретировать эти символы.

Pipe (конвеер) – это однонаправленный канал межпроцессного взаимодействия. Термин был придуман Дугласом Макилроем для командной оболочки Unix и назван по аналогии с трубопроводом. Конвейеры чаще всего используются в shell-скриптах для связи нескольких команд путем перенаправления вывода одной команды (stdout) на вход (stdin) последующей, используя символ конвеера ‘|’:

grep выполняет регистронезависимый поиск строки “error” в файле log, но результат поиска не выводится на экран, а перенаправляется на вход (stdin) команды wc, которая в свою очередь выполняет подсчет количества строк.

Логика

Конвеер обеспечивает асинхронное выполнение команд с использованием буферизации ввода/вывода. Таким образом все команды в конвейере работают параллельно, каждая в своем процессе.

Размер буфера начиная с ядра версии 2.6.11 составляет 65536 байт (64Кб) и равен странице памяти в более старых ядрах. При попытке чтения из пустого буфера процесс чтения блокируется до появления данных. Аналогично при попытке записи в заполненный буфер процесс записи будет заблокирован до освобождения необходимого места.
Важно, что несмотря на то, что конвейер оперирует файловыми дескрипторами потоков ввода/вывода, все операции выполняются в памяти, без нагрузки на диск.
Вся информация, приведенная ниже, касается оболочки bash-4.2 и ядра 3.10.10.

Простой дебаг


Утилита strace позволяет отследить системные вызовы в процессе выполнения программы:
Видно, что для создания конвеера используется системный вызов pipe(), а также, что оба процесса выполняются параллельно в разных потоках.

Исходный код, уровень 1, shell

Т. к. лучшая документация — исходный код, обратимся к нему. Bash использует Yacc для парсинга входных команд и возвращает ‘command_connect()’, когда встречает символ ‘|’.
parse.y:
Также здесь мы видим обработку пары символов ‘|&’, что эквивалентно перенаправлению как stdout, так и stderr в конвеер. Далее обратимся к command_connect():make_cmd.c:
где connector это символ ‘|’ как int. При выполнении последовательности команд (связанных через ‘&’, ‘|’, ‘;’, и т. д.) вызывается execute_connection():execute_cmd.c:

PIPE_IN и PIPE_OUT — файловые дескрипторы, содержащие информацию о входном и выходном потоках. Они могут принимать значение NO_PIPE, которое означает, что I/O является stdin/stdout.
execute_pipeline() довольно объемная функция, имплементация которой содержится в execute_cmd.c. Мы рассмотрим наиболее интересные для нас части.
execute_cmd.c:
Таким образом, bash обрабатывает символ конвейера путем системного вызова pipe() для каждого встретившегося символа ‘|’ и выполняет каждую команду в отдельном процессе с использованием соответствующих файловых дескрипторов в качестве входного и выходного потоков.

Исходный код, уровень 2, ядро

Обратимся к коду ядра и посмотрим на имплементацию функции pipe(). В статье рассматривается ядро версии 3.10.10 stable.
fs/pipe.c (пропущены незначительные для данной статьи участки кода):
Если вы обратили внимание, в коде идет проверка на флаг O_NONBLOCK. Его можно выставить используя операцию F_SETFL в fcntl. Он отвечает за переход в режим без блокировки I/O потоков в конвеере. В этом режиме вместо блокировки процесс чтения/записи в поток будет завершаться с errno кодом EAGAIN.

Максимальный размер блока данных, который будет записан в конвейер, равен одной странице памяти (4Кб) для архитектуры arm:
arch/arm/include/asm/limits.h:
Для ядер >= 2.6.35 можно изменить размер буфера конвейера:
Максимально допустимый размер буфера, как мы видели выше, указан в файле /proc/sys/fs/pipe-max-size.

Tips & trics

В примерах ниже будем выполнять ls на существующую директорию Documents и два несуществующих файла: ./non-existent_file и. /other_non-existent_file.

Перенаправление и stdout, и stderr в pipe

или же можно использовать комбинацию символов ‘|&’ (о ней можно узнать как из документации к оболочке (man bash), так и из исходников выше, где мы разбирали Yacc парсер bash):

Перенаправление _только_ stderr в pipe

Shoot yourself in the foot
Важно соблюдать порядок перенаправления stdout и stderr. Например, комбинация ‘>/dev/null 2>&1′ перенаправит и stdout, и stderr в /dev/null.

Получение корректного кода завершения конвейра

По умолчанию, код завершения конвейера — код завершения последней команды в конвеере. Например, возьмем исходную команду, которая завершается с ненулевым кодом:
И поместим ее в pipe:
Теперь код завершения конвейера — это код завершения команды wc, т.е. 0.

Обычно же нам нужно знать, если в процессе выполнения конвейера произошла ошибка. Для этого следует выставить опцию pipefail, которая указывает оболочке, что код завершения конвейера будет совпадать с первым ненулевым кодом завершения одной из команд конвейера или же нулю в случае, если все команды завершились корректно:
Shoot yourself in the foot
Следует иметь в виду “безобидные” команды, которые могут вернуть не ноль. Это касается не только работы с конвейерами. Например, рассмотрим пример с grep:
Здесь мы печатаем все найденные строки, приписав ‘new_’ в начале каждой строки, либо не печатаем ничего, если ни одной строки нужного формата не нашлось. Проблема в том, что grep завершается с кодом 1, если не было найдено ни одного совпадения, поэтому если в нашем скрипте выставлена опция pipefail, этот пример завершится с кодом 1:
В больших скриптах со сложными конструкциями и длинными конвеерами можно упустить этот момент из виду, что может привести к некорректным результатам.

Linux - начинающим. Часть 7. Потоки, перенаправление потоков, конвейер

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

Прежде всего определимся с терминами. Мы часто говорим: "консоль", "терминал", "командная строка" - не сильно задумываясь о смысле этих слов и подразумевая под этим в большинстве своем CLI - интерфейс командной строки. Во многих случаях такое упрощение допустимо и широко используется в профессиональной среде. Но в данном случае точный смысл этих терминов имеет значение для правильного понимания происходящих процессов.

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

Начнем с основного понятия - терминал. Он уходит корнями в далекое (по компьютерным меркам) прошлое и обозначает собой оконечное устройство, предназначенное для взаимодействия оператора и компьютера, к одному компьютеру может быть присоединено несколько терминалов, каждый из которых работает самостоятельно и независимо от других. Смысл современного терминала, а приложение для работы с командной строкой называется в Linux именно так, не изменился и сегодня, хотя, если быть точными, его название - эмулятор терминала.

linux-standard-stream-pipeline-001.jpg

Данное приложение все также эмулирует оконечное устройство, предназначенное для взаимодействия пользователя с компьютером. Точно также мы можем запустить сразу несколько терминалов, каждый из которых будет работать независимо. Кроме того, следует понимать, что терминал может быть запущен как локально, так и удаленно, способ подключения к компьютеру может быть разным, но свойства терминала от этого не изменяются.

Работая с терминалом нам нужно каким-то образом передавать ему команды и получать результаты. Для этого предназначена консоль - совокупность устройств ввода-вывода обеспечивающих взаимодействие пользователя и компьютера. В качестве консоли на современных ПК выступают клавиатура и монитор, но это только один из возможных вариантов, например, в самых первых моделях терминалов в качестве консоли использовался телетайп. Консоль всегда подключена к текущему рабочему месту, в то время как терминал может быть запущен и удаленно.

Но в нашей схеме все еще чего-то не хватает. При помощи консоли мы вводим определенные команды и передаем их в терминал, а дальше? Терминал - это всего лишь оконечное устройство для взаимодействия с компьютером, но не сам компьютер, выполнять команды или производить какие-либо другие вычисления он не способен. Поэтому на сцену выходит третий компонент - командный интерпретатор. Это специальная программа, обеспечивающая базовое взаимодействие пользователя и ОС, а также дающая возможность запускать другие программы. В большинстве Linux-дистрибутивов командным интерпретатором по умолчанию является bash.

linux-standard-stream-pipeline-002.jpg

Теперь все становится на свои места. Для каждого сеанса взаимодействия пользователя и компьютера создается отдельный терминал, внутри терминала работает специальная программа - командный интерпретатор. При помощи консоли пользователь передает командному интерпретатору или запущенной с его помощью программе входящие данные и получает назад результат их работы. Осталось разобраться каким именно образом это происходит.

Для взаимодействия запускаемых в терминале программ и пользователя используются стандартные потоки ввода-вывода, имеющие зарезервированный номер (дескриптор) зарезервированный на уровне операционной системы. Всего существует три стандартных потока:

Как мы помним, в основе философии Linux лежит понятие - все есть файл. Стандартные потоки не исключение, с точки зрения любой программы - это специальные файлы, которые открываются либо на чтение (поток ввода), либо на запись (поток вывода). Это вызывает очевидный вопрос, а можно ли вместо консоли использовать файлы? Да, можно и здесь мы вплотную подошли к понятию перенаправления потоков.

Перенаправление потоков

Начнем с наиболее простого и понятного - потока вывода. В него программа отправляет результат своей работы, и он отображается на подключенном к консоли устройстве вывода, в современных системах это экран. Допустим мы хотим получить список файлов в директории, для этого мы набираем на устройстве ввода консоли команду:

Которая через стандартный поток ввода будет доставлена командному интерпретатору, тот запустит указанную программу и передаст ей требуемые аргументы, а результат вернет через стандартный поток вывода на устройство отображения информации.

linux-standard-stream-pipeline-003.jpg

Но что, если мы хотим сохранить результат работы команды в файл? Нет ничего проще, воспользуемся перенаправлением, для этого следует использовать знак > .

На экран теперь не будет выведено ничего, но весь вывод команды окажется в файле result, который мы можем прочитать следующим образом:

linux-standard-stream-pipeline-004.jpg

При таком перенаправлении вывода файл-приемник каждый раз будет создаваться заново, т.е. будет перезаписан. Это очень важный момент, сразу и навсегда запомните > - всегда перезаписывает файл!

Можно ли этого избежать? Можно, для того, чтобы перенаправленный поток был дописан в конец уже существующего файла используйте для перенаправления знак >> .

Немного изменим последовательность команд:

linux-standard-stream-pipeline-006.jpg

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

Пойдем дальше. Как видим в выводе кроме списка файлов присутствуют строки "итого", нам они не нужны, и мы хотим от них избавиться. В этом нам поможет утилита grep, которая позволяет отфильтровать строки согласно некому выражению. Например, можно сделать так:

linux-standard-stream-pipeline-007.jpg

В целом результат достигнут, но ценой трех команд и наличием одного промежуточного файла. Можно ли сделать проще?

До этого мы перенаправляли поток вывода, но тоже самое можно сделать и с потоком ввода, используя для этого знак < . Например, мы можем сделать так:

Но это ничего не изменит, поэтому мы пойдем другим путем и перенаправим на ввод одной команды вывод другой:

На первый взгляд выглядит несколько сложно, но обратимся к man по grep:

В данном примере весь вывод стандартного потока ошибок будет перенаправлен в пустое устройство /dev/null.

Но можно пойти и другим путем, перенаправив поток ошибок в стандартный поток вывода:

Конвейер

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

Самый простой пример:

Первая команда выведет список всех установленных пакетов, вторая отфильтрует только те, в наименовании которых есть строка "gnome".

linux-standard-stream-pipeline-011.jpg

Длинна конвейера ограничена лишь нашей фантазией и здравым смыслом, никаких ограничений со стороны системы в этом нет. Но также в Linuх нет и единственно верных путей, каждую задачу можно решить самыми различными способами. Возвращаясь к получению списка файлов двух директорий мы можем сделать так:

linux-standard-stream-pipeline-012.jpg

Какой из этих способов лучше? Любой, Linux ни в чем не ограничивает пользователей и предоставляет им много путей для решения одной и той же задачи.

Еще один важный момент, если вы повышаете права с помощью команды sudo, то данное повышение прав через конвейер не распространяется. Например, если мы решим выполнить:

То первая команда будет выполнена с правами суперпользователя, а вторая с правами запустившего терминал пользователя.

Читайте также: