Pipe используется для передачи вывода в другую программу или утилиту .
Redirect используется для передачи вывода в файл или поток .
Пример: thing1 > thing2
противthing1 | thing2
thing1 > thing2
- Ваша оболочка запустит программу с именем
thing1
- Все, что
thing1
выводится, будет помещено в файл с именем thing2
. (Примечание - если thing2
существует, он будет перезаписан)
Если вы хотите передать вывод из программы thing1
в вызываемую программу thing2
, вы можете сделать следующее:
thing1 > temp_file && thing2 < temp_file
которые бы
- запустить программу с именем
thing1
- сохранить вывод в файл с именем
temp_file
- Запустите программу с именем
thing2
, делая вид, что человек на клавиатуре напечатал содержимое в temp_file
качестве ввода.
Однако, это неуклюже, поэтому они сделали трубы как более простой способ сделать это. thing1 | thing2
делает то же самое, что иthing1 > temp_file && thing2 < temp_file
РЕДАКТИРОВАТЬ, чтобы предоставить более подробную информацию к вопросу в комментарии:
Если >
попытаться использовать оба метода: «передать в программу» и «записать в файл», это может вызвать проблемы в обоих направлениях.
Первый пример: вы пытаетесь записать в файл. Уже существует файл с таким именем, который вы хотите перезаписать. Тем не менее, файл является исполняемым. Предположительно, он попытается выполнить этот файл, передав входные данные. Вам нужно будет сделать что-то наподобие записи результата в новое имя файла, а затем переименовать файл.
Второй пример: как отметил Флориан Диш, что если в другом месте системы есть другая команда с таким же именем (которая находится в пути выполнения). Если вы намеревались создать файл с таким именем в вашей текущей папке, вы застряли.
В-третьих: если вы неправильно введете команду, она не предупредит вас о том, что команда не существует. Прямо сейчас, если вы напечатаете, ls | gerp log.txt
это скажет вам bash: gerp: command not found
. Если >
подразумевать и то и другое, он просто создаст для вас новый файл (затем предупредите, что не знает, что с ним делать log.txt
).
thing1 > temp_file && thing2 < temp_file
чтобы сделать проще с трубами. Но почему бы не использовать>
оператор для этого, например,thing1 > thing2
для командthing1
иthing2
? Почему дополнительный оператор|
?less
, например?thing | less
иthing > less
совершенно разные, так как они делают разные вещи. То, что вы предлагаете, создаст двусмысленность.tee
команда делает что-то другое.tee
записывает вывод как на экран (stdout
), так и в файл. Редирект делает только файл.Если значение
foo > bar
будет зависеть от того, существует ли команда с именемbar
, которая сделает использование перенаправления намного сложнее и более подвержена ошибкам: каждый раз, когда я хочу перенаправить в файл, мне сначала нужно было проверить, есть ли команда с именем, например, мой целевой файл.источник
bar
в каталог, который является частью вашей$PATH
переменной env. Если вы находитесь в чем-то вроде / bin, то это может быть проблемой. Но даже в этом случаеbar
должен быть установлен исполняемый набор разрешений, чтобы оболочка проверяла не только поиск исполняемого файла,bar
но и фактически могла его выполнить. И если проблема заключается в перезаписи существующих файлов,noclober
опция оболочки должна предотвращать перезапись существующих файлов при перенаправлениях.Из Руководства по системному администрированию Unix и Linux:
Итак, моя интерпретация такова: если это команда для команды, используйте канал. Если вы выводите в файл или из файла, используйте перенаправление.
источник
Между двумя операторами существует существенная разница:
ls > log.txt
-> Эта команда отправляет вывод в файл log.txt.ls | grep file.txt
-> Эта команда отправляет выходные данные команды ls в grep с помощью pipe (|
), а команда grep ищет файл file.txt во входных данных, предоставленных ей предыдущей командой.Если бы вам пришлось выполнить ту же задачу, используя первый сценарий, то это было бы:
Таким образом, канал (с
|
) используется для отправки вывода другой команде, а перенаправление (с>
) используется для перенаправления вывода в какой-либо файл.источник
Существует большая синтаксическая разница между ними:
Вы можете думать о переадресовывает , как это:
cat [<infile] [>outfile]
. Это означает, что порядок не имеет значения: такcat <infile >outfile
же, какcat >outfile <infile
. Вы даже можете смешивать перенаправления с другими аргументами:cat >outfile <infile -b
и то,cat <infile -b >outfile
и другое прекрасно. Также вы можете нанизывать более чем один вход или выход (входы будут читаться последовательно и все выходные данные будут записаны в каждый выходной файл):cat >outfile1 >outfile2 <infile1 <infile2
. Целью или источником перенаправления может быть либо имя файла, либо имя потока (например, & 1, по крайней мере, в bash).Но каналы полностью отделяют одну команду от другой, вы не можете смешивать их с аргументами:
Канал принимает все данные, записанные в стандартный вывод команды 1, и отправляет их на стандартный ввод команды 2.
Вы также можете объединить трубопровод и перенаправление. Например:
Первая
cat
будет читать строки из infile, затем одновременно записывать каждую строку в outfile и отправлять ее второйcat
.Во втором случае
cat
стандартный ввод сначала читает из канала (содержимое infile), затем читает из infile2, записывая каждую строку в outfile2. После запуска outfile будет копией infile, а outfile2 будет содержать infile, за которым следует infile2.Наконец, вы действительно делаете что-то действительно похожее на ваш пример, используя перенаправление «here string» (только семейство bash) и обратные ссылки:
даст тот же результат, что и
Но я думаю, что версия перенаправления сначала прочитает все выходные данные ls в буфер (в памяти), а затем передаст этот буфер в grep по одной строке за раз, тогда как конвейерная версия будет принимать каждую строку из ls по мере ее появления, и передайте эту строку в grep.
источник
echo yes 1>&2 2>/tmp/blah; wc -l /tmp/blah; echo yes 2>/tmp/blah 1>&2; wc -l /tmp/blah
далее, перенаправление в файл будет использовать только последнее перенаправление.echo yes >/tmp/blah >/tmp/blah2
буду только писать в/tmp/blah2
.Примечание. Ответ отражает мое собственное понимание этих механизмов на сегодняшний день, накопленное в ходе исследований и прочтения ответов коллегами на этом сайте и unix.stackexchange.com , и будет обновляться с течением времени. Не стесняйтесь задавать вопросы или предлагать улучшения в комментариях. Я также предлагаю вам попытаться увидеть, как системные вызовы работают в оболочке с помощью
strace
команды. Также, пожалуйста, не пугайтесь понятия внутренних систем или системных вызовов - вам не нужно знать или использовать их, чтобы понять, как работает оболочка, но они определенно помогают пониманию.TL; DR
|
каналы не связаны с записью на диске, поэтому не имеют номера инода дисковой файловой системы (но имеют иноды в виртуальной файловой системе pipefs в пространстве ядра), но перенаправления часто включают файлы, которые имеют записи на диске и, следовательно, имеют соответствующие инод.lseek()
«способны», поэтому команды не могут прочитать некоторые данные и затем перемотать их назад, но когда вы перенаправляете>
или,<
как правило, это файл, которыйlseek()
может быть объектом, команды могут перемещаться по своему усмотрению.dup2()
системные вызовы под капотом, чтобы предоставить копии файловых дескрипторов, где происходит фактический поток данных.exec
встроенной команды (см. это и это ), поэтому, если вы сделаете это,exec > output.txt
каждая команда будет писатьoutput.txt
с этого момента.|
Каналы применяются только для текущей команды (что означает либо простую команду, либо подобную подоболочке,seq 5 | (head -n1; head -n2)
либо составную команду .Когда перенаправление выполняется для файлов, такие вещи, как
echo "TEST" > file
иecho "TEST" >> file
оба, используютopen()
syscall для этого файла ( см. Также ) и получают от него дескриптор файла, чтобы передать егоdup2()
. Трубы|
только использоватьpipe()
иdup2()
системный вызов.Что касается выполняемых команд, каналы и перенаправление - это не более, чем файловые дескрипторы - файловые объекты, в которые они могут писать вслепую или манипулировать ими внутренне (что может привести к непредвиденным действиям;
apt
например, имеет тенденцию даже не писать в stdout). если он знает, что есть перенаправление).Введение
Чтобы понять, как эти два механизма различаются, необходимо понять их основные свойства, историю этих двух механизмов и их корни в языке программирования Си. На самом деле, также важно знать, что такое файловые дескрипторы, как
dup2()
и какpipe()
работают системные вызовыlseek()
. Оболочка предназначена для того, чтобы сделать эти механизмы абстрактными для пользователя, но копание глубже, чем абстракция, помогает понять истинную природу поведения оболочки.Происхождение перенаправлений и труб
Согласно статье Денниса Ритча « Пророческие петроглифы» , трубы возникли из внутренней записки 1964 года Малкольма Дугласа Макилроя , когда они работали над операционной системой Multics . Цитата:
Очевидно, что в то время программы могли записывать на диск, однако это было неэффективно, если выходной объем был большим. Цитировать объяснение Брайана Кернигана в видео Unix Pipeline :
Таким образом, концептуальное различие очевидно: каналы - это механизм, заставляющий программы общаться друг с другом. Перенаправления - это способ записи в файл на базовом уровне. В обоих случаях оболочка облегчает эти две вещи, но под капотом происходит много всего.
Идем глубже: системные вызовы и внутренняя работа оболочки
Начнем с понятия файлового дескриптора . Файловые дескрипторы описывают в основном открытый файл (будь то файл на диске, или в памяти, или анонимный файл), который представлен целым числом. Два стандартных потока данных (stdin, stdout, stderr) являются файловыми дескрипторами 0,1 и 2 соответственно. Откуда они ? Что ж, в командах оболочки дескрипторы файлов наследуются от их родительской оболочки. И это в целом верно для всех процессов - дочерний процесс наследует файловые дескрипторы родителя. Для демонов обычно закрывают все унаследованные файловые дескрипторы и / или перенаправляют в другие места.
Вернуться к перенаправлению. Что это на самом деле? Это механизм, который указывает оболочке подготовить файловые дескрипторы для команды (поскольку перенаправления выполняются оболочкой до запуска команды) и указывает их там, где предложил пользователь. Стандартное определение выходного перенаправления
Что
[n]
есть номер дескриптора файла. Когда вы делаетеecho "Something" > /dev/null
число 1 подразумевается там, иecho 2> /dev/null
.Под капотом это делается путем дублирования дескриптора файла через
dup2()
системный вызов. Давайте возьмемdf > /dev/null
. Оболочка создаст дочерний процессdf
, который будет запущен, но перед этим он откроется/dev/null
как дескриптор файла № 3 иdup2(3,1)
будет выпущен, что создаст копию дескриптора файла 3, а копия будет 1. Вы знаете, что у вас есть два файлаfile1.txt
иfile2.txt
, а когда вы это сделаете, уcp file1.txt file2.txt
вас будет два одинаковых файла, но вы можете управлять ими независимо? Здесь происходит то же самое. Часто вы можете видеть, что перед запускомbash
будетdup(1,10)
создан дескриптор файла копии # 1, которыйstdout
(и эта копия будет fd # 10), чтобы восстановить его позже. Важно отметить, что при рассмотрении встроенных команд(которые являются частью самой оболочки и не имеют файлов внутри/bin
или где-либо еще) или простые команды в неинтерактивной оболочке , оболочка не создает дочерний процесс.И тогда у нас есть такие вещи, как
[n]>&[m]
и[n]&<[m]
. Это дублирование файловых дескрипторов, механизм которых тот же, что иdup2()
только сейчас, в синтаксисе оболочки, который удобно доступен для пользователя.Одна из важных вещей, которые следует отметить в отношении перенаправления, заключается в том, что их порядок не фиксирован, но важен для интерпретации оболочкой того, что хочет пользователь. Сравните следующее:
Их практическое использование в сценариях оболочки может быть универсальным:
и многие другие.
Сантехника с
pipe()
иdup2()
Так как же создаются трубы? Через
pipe()
syscall , который будет принимать в качестве входных данных массив (он же список), вызываемыйpipefd
из двух элементов типаint
(целое число). Эти два целых числа являются файловыми дескрипторами.pipefd[0]
Будет прочитать конец трубы иpipefd[1]
будет конец записи. Таким образомdf | grep 'foo'
,grep
получит копиюpipefd[0]
иdf
получит копиюpipefd[1]
. Но как ? Конечно, с волшебствомdup2()
системного вызова. Например,df
в нашем примере, скажем,pipefd[1]
есть # 4, поэтому оболочка создаст дочерний элемент, сделайтеdup2(4,1)
(помните мойcp
пример?), А затемexecve()
действительно запуститеdf
. Естественно,df
будет наследовать файловый дескриптор # 1, но не будет знать, что он больше не указывает на терминал, а на самом деле fd # 4, который на самом деле является концом записи канала. Естественно, то же самое произойдет,grep 'foo'
за исключением разного числа файловых дескрипторов.Теперь интересный вопрос: можем ли мы создавать каналы, которые также перенаправляют fd # 2, а не только fd # 1? Да, на самом деле это то, что
|&
делает в Bash. Стандарт POSIX требует, чтобы командный язык оболочки поддерживалdf 2>&1 | grep 'foo'
синтаксис для этой цели, но такжеbash
делает|&
это.Важно отметить, что каналы всегда имеют дело с файловыми дескрипторами. Существует
FIFO
или именованный канал , который имеет имя файла на диске и позволяет использовать его как файл, но ведет себя как канал. Но|
типы каналов - это то, что известно как анонимный канал - у них нет имени файла, потому что на самом деле это просто два объекта, соединенных вместе. Тот факт, что мы не имеем дело с файлами, также имеет важное значение: каналы неlseek()
способны. Файлы в памяти или на диске являются статическими - программы могут использоватьlseek()
системный вызов для перехода к байту 120, затем обратно к байту 10, а затем переслать весь путь до конца. Трубы не являются статичными - они последовательные, и поэтому вы не можете перематывать данные, полученные с их помощью.lseek()
, Это то, что заставляет некоторые программы знать, читают ли они из файла или из конвейера, и, таким образом, они могут вносить необходимые корректировки для эффективной производительности; другими словами, онprog
может обнаружить, если я делаюcat file.txt | prog
илиprog < input.txt
. Настоящий пример работы - это хвост .Два других очень интересных свойства конвейеров - это то, что у них есть буфер, который в Linux составляет 4096 байт , и у них фактически есть файловая система, как определено в исходном коде Linux ! Они не просто объект для передачи данных, они сами представляют собой структуру данных! Фактически, поскольку существует файловая система pipefs, которая управляет как каналами, так и FIFO, каналы имеют номер инода в соответствующей файловой системе:
В Linux каналы являются однонаправленными, как перенаправление. В некоторых Unix-подобных реализациях существуют двунаправленные каналы. Хотя, используя магию сценариев оболочки, вы можете создавать двунаправленные каналы и в Linux .
Смотрите также:
pipe()
syscall иdup2()
.<<
,<<<
реализованы как анонимные (несвязанные) временные файлы вbash
иksh
, в то время как< <()
используются анонимные каналы;/bin/dash
использует трубы для<<
. См. В чем разница между <<, <<< и <<в bash?источник
Чтобы добавить к другим ответам, есть небольшая семантическая разница - например, каналы закрываются легче, чем перенаправления:
В первом примере, когда первый вызов
head
завершается, он закрывает канал иseq
завершается, поэтому для второго нет доступных входных данныхhead
.Во втором примере head использует первую строку, но когда он закрывает свой собственный
stdin
канал , файл остается открытым для следующего вызова.Третий пример показывает, что если мы используем,
read
чтобы избежать закрытия канала, он все еще доступен внутри подпроцесса.Таким образом, «поток» - это то, через что мы шунтируем данные (stdin и т. Д.), И он одинаков в обоих случаях, но канал соединяет потоки из двух процессов, где перенаправление соединяет потоки между процессом и файлом, так что вы Можно увидеть источник как сходства, так и различия.
PS Если вы так же любопытны и / или удивлены этими примерами, как и я, вы можете продолжить изучение,
trap
чтобы увидеть, как разрешаются процессы, например:Иногда первый процесс закрывается до того,
1
как напечатано, иногда после.Я также нашел интересным использовать
exec <&-
закрытие потока от перенаправления, чтобы приблизить поведение канала (хотя и с ошибкой):источник
read
потребляет только первую строку (это один байт для1
и новая строка ).seq
отправлено всего 10 байтов (5 номеров и 5 новых строк). Таким образом, в конвейерном буфере осталось 8 байтов, и поэтому второйhead
работает - в канальном буфере все еще есть данные. Кстати, голова выходит, только если прочитано 0 байтов, вроде какhead /dev/null
seq 5 | (head -n1; head -n1)
при первом вызове канал очищается, поэтому он все еще существует в открытом состоянии, но без данных для второго вызоваhead
? Таким образом, разница в поведении между каналом и перенаправлением заключается в том, что head вытягивает все данные из канала, но только 2 строки из дескриптора файла?strace
команде, которую я дал в первом комментарии. С помощью перенаправления файл tmp находится на диске, что делает его доступным для поиска (поскольку они используютlseek()
syscall - команды могут перемещаться по файлу от первого байта к последнему, как они хотят. Но каналы являются последовательными и не могут быть найдены. Так что единственный способ для головы это сделать. работа заключается в том, чтобы сначала прочитать все или, если файл большой, сопоставить часть его с оперативной памятью черезmmap()
вызов. Однажды я сделал это самостоятельноtail
на Python и столкнулся с точно такой же проблемой.(...)
, и подоболочка будет копировать свой собственный stdin для каждой команды внутри(...)
. Так что они технически читаются с одного и того же объекта. Сначалаhead
думает, что читает с собственного стандартного ввода. Второйhead
думает, что у него есть свой стандартный ввод. Но на самом деле их fd # 1 (stdin) является просто копией того же fd, который является концом канала для чтения. Кроме того, я опубликовал ответ, так что, возможно, это поможет прояснить ситуацию.Я столкнулся с проблемой в C сегодня. По существу, у Pipe есть и другая семантика для перенаправлений, даже когда они отправляются
stdin
. На самом деле я думаю, что с учетом различий, каналы должны идти куда-то ещеstdin
, так что,stdin
и пусть их вызовstdpipe
(чтобы сделать произвольный дифференциал) можно обрабатывать по-разному.Учти это. При передаче одного вывода программы в другой,
fstat
кажется, возвращается ноль,st_size
несмотря на то,ls -lha /proc/{PID}/fd
что он показывает, что файл существует. При перенаправлении файла это не так ( по крайней мере , на Debianwheezy
,stretch
иjessie
ванили и убунту14.04
,16.04
ваниль.Если у вас
cat /proc/{PID}/fd/0
есть перенаправление, вы сможете повторить, чтобы прочитать столько раз, сколько хотите. Если вы сделаете это с каналом, вы заметите, что при втором последовательном запуске задачи вы не получите тот же результат.источник