В чем разница между & ldquo; перенаправлением & rdquo; и & ldquo; Pipe & rdquo ;?

Этот вопрос может показаться немного глупым, но я не вижу разницы между перенаправлением и каналами.

Перенаправление используется для перенаправления stdout / stdin / stderr, например, ls > log.txt.

Каналы используются для выдачи выходных данных команды в качестве входных данных для другой команды, например, ls | grep file.txt.

Но почему есть два оператора для одной и той же вещи?

Почему бы просто не написать ls > grep для прохождения вывода, не является ли это также своего рода перенаправлением? Чего мне не хватает?

212
задан 1 April 2015 в 12:38

8 ответов

Канал используется для передачи вывода другой программе или утилите.

Перенаправление используется для передачи вывода или файлу или потоку.

Пример: thing1 > thing2 по сравнению с thing1 | thing2

thing1 > thing2

  1. Ваша оболочка запустит названную программу thing1
  2. Все это thing1 выводы будут помещены в названный файл thing2. (Отметьте - если thing2 существует, это будет перезаписано),

Если Вы хотите передать вывод из программы thing1 к названной программе thing2, Вы могли сделать следующее:

thing1 > temp_file && thing2 < temp_file

который был бы

  1. запустите названную программу thing1
  2. сохраните вывод в названный файл temp_file
  3. запустите названную программу thing2, притворство, что человек на клавиатуре ввел содержание temp_file как вход.

Однако это неуклюже, таким образом, они сделали каналы как более простой способ сделать это. thing1 | thing2 делает то же самое как thing1 > temp_file && thing2 < temp_file

РЕДАКТИРОВАНИЕ для предоставления больше подробной информации к вопросу в комментарии:

Если > попробованный, чтобы быть и "передают программе" и, "пишут в файл", это могло вызвать проблемы в обоих направлениях.

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

Второй пример: Как Florian Diesch указал, что, если существует другая команда в другом месте в системе с тем же именем (который находится в выполнить пути). Если бы Вы намеревались сделать файл с тем именем в Вашей текущей папке, то Вы застряли бы.

В-третьих: если бы Вы вводите команду с опечаткой, она не предупредила бы Вас, что команда не существует. Прямо сейчас, если Вы вводите ls | gerp log.txt это скажет Вам bash: gerp: command not found. Если > предназначенный оба, это просто создало бы новый файл для Вас (затем предупреждают, что это не знает, что сделать с log.txt).

230
ответ дан 1 April 2015 в 12:38

Из Руководства по системному администрированию Unix и Linux:

Перенаправление

Оболочка интерпретирует символы <,> и >> как инструкции для перенаправления команды ввод или вывод в или из файла .

Трубы

Для подключения STDOUT одной команды к STDIN другой используйте | символ, широко известный как трубка.

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

0
ответ дан 1 April 2015 в 12:38

Между ними есть большая синтаксическая разница:

  1. Перенаправление - это аргумент программы
  2. Канал разделяет две команды

Вы может думать о перенаправлениях так: 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.

0
ответ дан 1 April 2015 в 12:38
1113 У меня сегодня проблема с этим в Си. По существу, у Pipe есть и другая семантика для перенаправлений, даже когда они отправляются в 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 с перенаправление, которое вы сможете повторить, чтобы читать столько раз, сколько хотите. Если вы сделаете это с конвейером, вы заметите, что при втором последовательном запуске задачи вы не получите тот же вывод.

0
ответ дан 1 April 2015 в 12:38

Чтобы добавить к другим ответам, также есть тонкое семантическое различие - например, каналы закрываются легче, чем перенаправления:

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`
0
ответ дан 1 April 2015 в 12:38

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

0
ответ дан 1 April 2015 в 12:38

Между двумя операторами существует существенная разница:

  1. ls > log.txt -> Эта команда отправляет вывод в файл log.txt.

  2. ls | grep file.txt -> Эта команда отправляет выходные данные команды ls в grep посредством использования pipe (|), а команда grep ищет файл file.txt во входных данных, предоставленных для это по предыдущей команде.

Если бы вам пришлось выполнить ту же задачу, используя первый сценарий, то это было бы:

ls > log.txt; grep 'file.txt' log.txt

Таким образом, канал (с |) используется для отправки вывода другим команда, тогда как перенаправление (с >) используется для перенаправления вывода в некоторый файл.

0
ответ дан 1 April 2015 в 12:38

Примечание: Ответ отражает мое собственное понимание этих механизмов, актуальных, накопленных по исследованию и чтению ответов коллегами на этом сайте и unix.stackexchange.com, и будет обновлен со временем. Задайте вопросы или предложите улучшения комментариев. Я также предлагаю, чтобы Вы попытались видеть, как syscalls работают в оболочке с strace команда. Также не запугивайтесь понятием внутренностей или syscalls - Вы не должны знать или смочь использовать их, чтобы понять, как оболочка делает вещи, но они определенно помогают пониманию.

TL; DR

  • | каналы не связаны с записью на диске, поэтому не имейте 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. Кавычка:

Помещать мои самые сильные проблемы в ореховую скорлупу:

  1. У нас должны быть некоторые способы соединиться, программы как садовый шланг - завинчивают в другом сегменте, когда это становится, когда это становится необходимым для массажных данных в другом отношении. Это - способ IO также.

То, что очевидно, - то, что в то время, когда программы были способны к записи в диск, однако который был неэффективен, если произведенный было большим. Заключить объяснение Brian Kernighan в кавычки в Конвейерном видео Unix:

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

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

Движение глубже: syscalls и внутренние работы оболочки

Мы запускаем с понятия дескриптора файла. Дескрипторы файлов описывают в основном открытый файл (является ли это файлом на диске, или в памяти или анонимном файле), который представлен целым числом. Два стандартных потока данных (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 также.

См. также:

3
ответ дан 1 April 2015 в 12:38

Другие вопросы по тегам:

Похожие вопросы: