Этот вопрос может показаться немного глупым, но я не вижу разницы между перенаправлением и каналами.
Перенаправление используется для перенаправления stdout / stdin / stderr, например, ls > log.txt
.
Каналы используются для выдачи выходных данных команды в качестве входных данных для другой команды, например, ls | grep file.txt
.
Но почему есть два оператора для одной и той же вещи?
Почему бы просто не написать ls > grep
для прохождения вывода, не является ли это также своего рода перенаправлением? Чего мне не хватает?
Канал используется для передачи вывода другой программе или утилите.
Перенаправление используется для передачи вывода или файлу или потоку.
Пример: 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
РЕДАКТИРОВАНИЕ для предоставления больше подробной информации к вопросу в комментарии:
Если >
попробованный, чтобы быть и "передают программе" и, "пишут в файл", это могло вызвать проблемы в обоих направлениях.
Первый пример: Вы пытаетесь записать в файл. Там уже существует файл с тем именем, которое Вы хотите перезаписать. Однако файл является исполняемым файлом. По-видимому, это попыталось бы выполнить этот файл, передав вход. Необходимо было бы сделать что-то, любят, пишут вывод в новое имя файла, затем переименовывают файл.
Второй пример: Как Florian Diesch указал, что, если существует другая команда в другом месте в системе с тем же именем (который находится в выполнить пути). Если бы Вы намеревались сделать файл с тем именем в Вашей текущей папке, то Вы застряли бы.
В-третьих: если бы Вы вводите команду с опечаткой, она не предупредила бы Вас, что команда не существует. Прямо сейчас, если Вы вводите ls | gerp log.txt
это скажет Вам bash: gerp: command not found
. Если >
предназначенный оба, это просто создало бы новый файл для Вас (затем предупреждают, что это не знает, что сделать с log.txt
).
Из Руководства по системному администрированию Unix и Linux:
Перенаправление
Оболочка интерпретирует символы <,> и >> как инструкции для перенаправления команды ввод или вывод в или из файла .
Трубы
Для подключения STDOUT одной команды к STDIN другой используйте | символ, широко известный как трубка.
blockquote>Итак, моя интерпретация такова: если это команда для команды, используйте канал. Если вы выводите в файл или из файла, используйте перенаправление.
Между ними есть большая синтаксическая разница:
Вы может думать о перенаправлениях так: cat [<infile] [>outfile]
. Это означает, что порядок не имеет значения: cat <infile >outfile
совпадает с cat >outfile <infile
. Вы можете даже смешать перенаправления с другими аргументами: cat >outfile <infile -b
и cat <infile -b >outfile
оба прекрасно подходят. Также вы можете связать вместе несколько входов или выходов (входы будут считываться последовательно, а весь вывод записывается в каждый выходной файл): cat >outfile1 >outfile2 <infile1 <infile2
. Целью или источником перенаправления может быть либо имя файла, либо имя потока (например, & amp; 1, по крайней мере, в bash).
Но каналы полностью отделяют одну команду от другой, вы не можете смешивать их с аргументами:
[command1] | [command2]
Канал берет все записанное в стандартный вывод из команды command1 и отправляет его на стандартный ввод команда2.
Вы также можете комбинировать трубопровод и перенаправление. Например:
cat <infile >outfile | cat <infile2 >outfile2
Первый cat
будет читать строки из infile, затем одновременно записывать каждую строку в outfile и отправлять ее второй cat
.
Во втором cat
стандартный ввод сначала читает из канала (содержимое infile), затем читает из infile2, записывая каждую строку в outfile2. После запуска outfile будет копией infile, а outfile2 будет содержать infile, за которым следует infile2.
Наконец, вы действительно делаете что-то действительно похожее на ваш пример, используя перенаправление «here string» (только семейство bash) и обратные ссылки:
grep blah <<<`ls`
даст тот же результат, что и
ls | grep blah
[ 1125] Но я думаю, что версия перенаправления сначала прочитает все выходные данные ls в буфер (в памяти), а затем передаст этот буфер в grep по одной строке за раз, тогда как конвейерная версия будет принимать каждую строку из ls как и передайте эту строку grep.
stdin
. На самом деле, я думаю, что с учетом различий, каналы должны идти куда-то, кроме stdin
, так что stdin
и назовем его stdpipe
(чтобы сделать произвольный дифференциал) можно обрабатывать по-разному. 1114 Учтите это. При передаче одной программы в другую fstat
, кажется, возвращает ноль как st_size
, несмотря на то, что ls -lha /proc/{PID}/fd
показывает, что файл существует. При перенаправлении файла это не так (по крайней мере, в Debian wheezy
, stretch
и jessie
vanilla и ubuntu 14.04
, 16.04
vanilla.
Если вы cat /proc/{PID}/fd/0
с перенаправление, которое вы сможете повторить, чтобы читать столько раз, сколько хотите. Если вы сделаете это с конвейером, вы заметите, что при втором последовательном запуске задачи вы не получите тот же вывод.
Чтобы добавить к другим ответам, также есть тонкое семантическое различие - например, каналы закрываются легче, чем перенаправления:
seq 5 | (head -n1; head -n1) # just 1
seq 5 > tmp5; (head -n1; head -n1) < tmp5 # 1 and 2
seq 5 | (read LINE; echo $LINE; head -n1) # 1 and 2
В первом примере, когда первый вызов head
завершается, он закрывает канал, и seq
завершается, поэтому для второго нет доступных входных данных. head
.
Во втором примере head использует первую строку, но когда он закрывает свой собственный канал stdin
, файл остается открытым для следующего вызова.
Третий пример показывает, что если мы используем read
, чтобы избежать закрытия канала, он все еще доступен внутри подпроцесса.
Таким образом, «поток» - это то, через что мы шунтируем данные (stdin и т. Д.), И он одинаков в обоих случаях, но канал соединяет потоки из двух процессов, где перенаправление соединяет потоки между процессом и файлом Таким образом, вы можете увидеть источник как сходств, так и различий.
P.S. Если вы так же любопытны и / или удивлены этими примерами, как и я, вы можете углубиться в изучение, используя trap
, чтобы увидеть, как разрешаются процессы, например:
(trap 'echo seq EXITed >&2' EXIT; seq 5) | (trap 'echo all done' EXIT; (trap 'echo first head exited' EXIT; head -n1)
echo '.'
(trap 'echo second head exited' EXIT; head -n1))
Иногда первый процесс закрывается до печати 1
, иногда после.
Мне также было интересно использовать exec <&-
, чтобы закрыть поток от перенаправления, чтобы приблизить поведение канала (хотя и с ошибкой):
seq 5 > tmp5
(trap 'echo all done' EXIT
(trap 'echo first head exited' EXIT; head -n1)
echo '.'
exec <&-
(trap 'echo second head exited' EXIT; head -n1)) < tmp5`
Если значение foo > bar
будет зависеть от того, существует ли команда с именем bar
, которая сделает использование перенаправления намного сложнее и более подвержено ошибкам: каждый раз, когда я хочу перенаправить в файл, мне сначала нужно было проверить, есть команда, названная как мой файл назначения.
Между двумя операторами существует существенная разница:
ls > log.txt
-> Эта команда отправляет вывод в файл log.txt.
ls | grep file.txt
-> Эта команда отправляет выходные данные команды ls в grep посредством использования pipe (|
), а команда grep ищет файл file.txt во входных данных, предоставленных для это по предыдущей команде.
Если бы вам пришлось выполнить ту же задачу, используя первый сценарий, то это было бы:
ls > log.txt; grep 'file.txt' log.txt
Таким образом, канал (с |
) используется для отправки вывода другим команда, тогда как перенаправление (с >
) используется для перенаправления вывода в некоторый файл.
Примечание: Ответ отражает мое собственное понимание этих механизмов, актуальных, накопленных по исследованию и чтению ответов коллегами на этом сайте и unix.stackexchange.com, и будет обновлен со временем. Задайте вопросы или предложите улучшения комментариев. Я также предлагаю, чтобы Вы попытались видеть, как syscalls работают в оболочке с strace
команда. Также не запугивайтесь понятием внутренностей или syscalls - Вы не должны знать или смочь использовать их, чтобы понять, как оболочка делает вещи, но они определенно помогают пониманию.
|
каналы не связаны с записью на диске, поэтому не имейте inode количества дисковой файловой системы (но действительно имейте inode в pipefs виртуальной файловой системе в пространстве ядра), но перенаправления часто включают файлы, которые действительно имеют дисковые записи и поэтому имеют соответствующий inode.lseek()
'способный, таким образом, команды не могут считать некоторые данные и затем перемотаться назад, но когда Вы перенаправляете с >
или <
обычно это - файл, который является lseek()
способный объект, таким образом, команды могут перейти однако, им нравится.dup2()
syscalls под капотом для обеспечения копий дескрипторов файлов, где фактический поток данных происходит.exec
встроенная команда (см. это и это), поэтому если Вы делаете exec > output.txt
каждая команда запишет в output.txt
с тех пор. |
каналы применяются только для текущей команды (что означает или простую команду или подоболочку как seq 5 | (head -n1; head -n2)
или составные команды.Когда перенаправление сделано на файлах, вещах как echo "TEST" > file
и echo "TEST" >> file
оба использования open()
syscall на том файле (см. также) и заставляют дескриптор файла от него передавать его dup2()
. Каналы |
только используйте pipe()
и dup2()
syscall.
До выполняемых команд каналы и перенаправление являются не больше, чем дескрипторами файлов - подобные файлу объекты, в которые они могут записать вслепую, или управлять ими внутренне (который может произвести неожиданные поведения; apt
например, имеет тенденцию даже не писать в stdout, если он знает, что существует перенаправление).
Чтобы понять, как эти два механизма отличаются, необходимо понять их существенные свойства, историю позади этих двух и их корни на языке программирования C. На самом деле, знание, что дескрипторы файлов, и как dup2()
и pipe()
работа системных вызовов важна, а также lseek()
. Shell предназначен как способ сделать эти механизмы кратким обзором пользователю, но вырыть глубже, чем абстракция помогает понять истинный характер поведения оболочки.
В соответствии со статьей Prophetic Petroglyphs Dennis Ritche, каналы произошли из внутренней заметки 1964 года Malcolm Douglas McIlroy, в то время, когда они работали над операционной системой Multics. Кавычка:
Помещать мои самые сильные проблемы в ореховую скорлупу:
- У нас должны быть некоторые способы соединиться, программы как садовый шланг - завинчивают в другом сегменте, когда это становится, когда это становится необходимым для массажных данных в другом отношении. Это - способ IO также.
То, что очевидно, - то, что в то время, когда программы были способны к записи в диск, однако который был неэффективен, если произведенный было большим. Заключить объяснение Brian Kernighan в кавычки в Конвейерном видео Unix:
Во-первых, Вы не должны писать одну большую крупную программу - у Вас есть существующие меньшие программы, которые могут уже внести свой вклад задания... Другой - это, возможно, что объем данных, который Вы обрабатываете, не соответствовал бы при хранении его в файле..., потому что помнят, мы вернулись в дни, когда диски на этих вещах имели, если Вы были удачливы, Мегабайт или два из данных... Таким образом, конвейер никогда не должен был инстанцировать целого вывода.
Таким образом концептуальная разница очевидна: каналы являются механизмом того, чтобы заставлять программы говорить друг с другом. Перенаправления - являются способом записать в файл на базовом уровне. В обоих случаях оболочка делает эти две вещи легкими, но под капотом, существуют все из продолжения.
Мы запускаем с понятия дескриптора файла. Дескрипторы файлов описывают в основном открытый файл (является ли это файлом на диске, или в памяти или анонимном файле), который представлен целым числом. Два стандартных потока данных (stdin, stdout, stderr) являются дескрипторами файлов 0,1, и 2 соответственно. Куда они происходят из? Ну, в оболочке управляет, чтобы дескрипторы файлов были наследованы от своего родителя - оболочка. И это верно в целом для всех процессов - дочерний процесс наследовал дескрипторы файлов родителя. Для демонов распространено закрыть все наследованные дескрипторы файлов и/или перенаправление к другим местам.
Назад к перенаправлению. Что это действительно? Это - механизм, который говорит оболочке готовить дескрипторы файлов к команде (потому что перенаправления сделаны оболочкой перед выполнениями команды), и укажите на них где предложенный пользователь. Стандартное определение перенаправления вывода
[n]>word
Это [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()
только теперь это находится в синтаксисе оболочки, удобно доступном пользователю.
Одна из важных вещей отметить о перенаправлении - то, что их порядок не фиксируется, но значительный к тому, как оболочка интерпретирует то, что хочет пользователь. Сравните следующее:
# Make copy of where fd 2 points , then redirect fd 2
$ ls -l /proc/self/fd/ 3>&2 2> /dev/null
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
lrwx------ 1 runner user 64 Sep 13 00:08 3 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/29/fd
# redirect fd #2 first, then clone it
$ ls -l /proc/self/fd/ 2> /dev/null 3>&2
total 0
lrwx------ 1 user user 64 Sep 13 00:08 0 -> /dev/pts/0
lrwx------ 1 user user 64 Sep 13 00:08 1 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:08 2 -> /dev/null
l-wx------ 1 user user 64 Sep 13 00:08 3 -> /dev/null
lr-x------ 1 user user 64 Sep 13 00:08 4 -> /proc/31/fd
Практическое применение их в сценариях оболочки может быть универсальным:
и многие другой.
pipe()
и dup2()
Таким образом, как каналы становятся созданными? Через pipe()
syscall, который возьмет в качестве входа массив (иначе список) названный pipefd
из двух объектов типа int
(целое число). Те два целых числа являются дескрипторами файлов. pipefd[0]
будет конец чтения канала и pipefd[1]
будет конец записи. Таким образом в df | grep 'foo'
, grep
получит копию pipefd[0]
и df
получит копию pipefd[1]
. Но как? Конечно, с волшебством dup2()
syscall. Для df
в нашем примере, скажем, pipefd[1]
имеет № 4, таким образом, оболочка сделает ребенка, сделает dup2(4,1)
(помните мой cp
пример?), и затем делают execve()
на самом деле работать df
. Естественно, df
наследует дескриптор файла № 1, но будет не знать, что он больше не указывает на терминал, но на самом деле fd № 4, который является на самом деле концом записи канала. Естественно, то же самое произойдет с grep 'foo'
кроме с различными числами дескрипторов файлов.
Теперь, интересный вопрос: мы могли сделать каналы, которые перенаправляют fd № 2 также, не только fd № 1? Да, на самом деле это что |&
делает в ударе. Стандарт POSIX требует, чтобы командный язык оболочки поддерживал df 2>&1 | grep 'foo'
синтаксис с этой целью, но bash
делает |&
также.
То, что важно для примечания, - то, который всегда передает по каналу соглашение с дескрипторами файлов. Там существует FIFO
или именованный канал, который имеет имя файла на диске и позволяет нам, Вы используете его в качестве файла, но ведет себя как канал. Но |
типы каналов - то, что известно как неименованный канал - у них нет имени файла, потому что они - действительно всего два объекта, соединенные вместе. То, что мы не имеем дело с файлами также, делает важную импликацию: каналы не lseek()
'способный. Файлы, или в памяти или на диске, статичны - программы могут использовать lseek()
syscall для перехода к байту 120, затем обратно к байту 10, затем передают полностью в конец. Каналы не статичны - они последовательны, и поэтому Вы не можете перемотать данные, с которыми Вы добираетесь от них lseek()
. Это - то, что информирует некоторые программы, если они читают из файла или из канала, и таким образом они могут внести необходимые корректировки для эффективной производительности; другими словами, a prog
может обнаружить, если я делаю cat file.txt | prog
или prog < input.txt
. Реальным примером работы этого является хвост.
Другие два очень интересных свойства каналов - то, что у них есть буфер, который на Linux составляет 4 096 байтов, и у них на самом деле есть файловая система, как определено в исходном коде Linux! Они не просто объект для того, чтобы раздать данные, они - datastructure сами! На самом деле, потому что там существует pipefs файловая система, которая управляет обоими каналами и FIFOs, каналы имеют inode число в своей соответствующей файловой системе:
# Stdout of ls is wired to pipe
$ ls -l /proc/self/fd/ | cat
lrwx------ 1 user user 64 Sep 13 00:02 0 -> /dev/pts/0
l-wx------ 1 user user 64 Sep 13 00:02 1 -> pipe:[15655630]
lrwx------ 1 user user 64 Sep 13 00:02 2 -> /dev/pts/0
lr-x------ 1 user user 64 Sep 13 00:02 3 -> /proc/22/fd
# stdin of ls is wired to pipe
$ true | ls -l /proc/self/fd/0
lr-x------ 1 user user 64 Sep 13 03:58 /proc/self/fd/0 -> 'pipe:[54741]'
На Linux каналы однонаправлены, точно так же, как перенаправление. На некоторых подобных Unix реализациях - существуют двунаправленные каналы. Хотя с волшебством сценариев оболочки, можно сделать двунаправленные каналы на Linux также.
pipe()
syscall и dup2()
.<<
,<<<
реализованы как анонимные (несвязанные) временные файлы в bash
и ksh
, в то время как < <()
неименованные каналы использования; /bin/dash
использование передает по каналу для <<
. Посмотрите то, Что различие между <<, <<<и <<в ударе?