Вопрос В чем разница между «перенаправлением» и «трубой»?


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

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

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

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

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


174
2017-08-07 13:22


происхождения




ответы:


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

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

Пример: 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

EDIT, чтобы предоставить более подробную информацию на вопрос в комментарии:

Если > пытался «перейти к программе» и «написать в файл», это может вызвать проблемы в обоих направлениях.

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

Второй пример: Как отметил Флориан Дёш, если есть другая команда в другом месте системы с тем же именем (то есть в пути выполнения). Если вы планируете создать файл с этим именем в текущей папке, вы застряли.

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


195
2017-08-07 13:30



Спасибо. Ты упомянул thing1 > temp_file && thing2 < temp_file для облегчения работы с трубами. Но почему бы не повторить использование > оператора для этого, например. thing1 > thing2 для команд thing1 а также thing2 ? Почему дополнительный оператор | ? - John Threepwood
«Возьмите вывод и напишите его в файл» - это другое действие, чем «Возьмите вывод и передайте его в другую программу». Я отредактирую больше мыслей в свой ответ ... - David Oneill
@JohnThreepwood У них разные значения. Что делать, если я хочу перенаправить что-то в файл с именем less, например? thing | less а также thing > less совершенно разные, поскольку они делают разные вещи. То, что вы предлагаете, создало бы двусмысленность. - Darkhogg
Справедливо ли говорить, что «thing1> temp_file» является просто синтаксическим сахаром для «thing1 | tee temp_file»? Поскольку я узнал о тройнике, я почти никогда не использую перенаправления. - Sridhar-Sarnobat
@ Шридхар-Сарнобат нет, tee команда делает что-то другое. teeзаписывает вывод на экран (stdout) а также файл. Перенаправление только файл. - David Oneill


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


19
2017-08-07 13:40



Это будет проблемой только в том случае, если вы пишете bar в каталоге, который является частью вашего $PATH переменная env. Если у вас что-то вроде / bin, тогда ot может быть проблемой. Но даже тогда, bar должен иметь установленный набор разрешений, так что оболочка проверяет не только на поиск исполняемого файла bar но на самом деле может его выполнить. И если проблема связана с перезаписи существующего файла, noclober shell должен предотвратить переписывание существующих файлов в перенаправления. - Sergiy Kolodyazhnyy


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

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

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

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

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

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


11
2017-08-07 13:32





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

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

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

трубы

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

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


9
2018-02-16 00:40





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

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

Вы можете думать о таких переадресациях: cat [<infile] [>outfile], Это означает, что порядок не имеет значения: cat <infile >outfile такой же как cat >outfile <infile, Вы даже можете смешивать перенаправления с другими аргументами: cat >outfile <infile -b а также cat <infile -b >outfile оба прекрасно. Кроме того, вы можете объединять более одного ввода или вывода (входы будут считываться последовательно, и весь вывод будет записан в каждый выходной файл): cat >outfile1 >outfile2 <infile1 <infile2, Целевой или источник перенаправления может быть либо именем файла, либо именем потока (например, & 1, по крайней мере, в bash).

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

[command1] | [command2]

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

Вы также можете комбинировать трубопроводы и перенаправление. Например:

cat <infile >outfile | cat <infile2 >outfile2

Первый cat будут читать строки из infile, затем одновременно записывать каждую строку в outfile и отправлять ее на второй cat,

В секунду cat, стандартный ввод сначала считывается из трубы (содержимое infile), затем считывается из infile2, записывая каждую строку в outfile2. После запуска outfile будет копией infile, а outfile2 будет содержать infile, за которым следует infile2.

Наконец, вы действительно делаете что-то действительно похожее на ваш пример, используя перенаправление «здесь строка» (только семейство bash) и обратные ссылки:

grep blah <<<`ls`

даст тот же результат, что и

ls | grep blah

Но я думаю, что версия перенаправления сначала будет считывать весь вывод ls в буфер (в памяти), а затем кормить этот буфер для grep по одной строке за раз, тогда как версия с каналами будет принимать каждую строку от ls по мере ее появления, и передать эту строку в grep.


3
2017-08-23 22:24



Nitpick: порядок имеет значение при перенаправлении, если вы перенаправляете один fd на другой: 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, - muru
Перенаправление не является фактически аргументом для программы. Программа не знает и не заботится о том, куда идет ее выход (или поступает ввод). Это просто способ сообщить bash, как организовать вещи перед запуском программы. - Alois Mahdal


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

TL; DR

  • | трубы не связаны с записью на диск, поэтому не имеют индекс количество дисковой файловой системы (но есть inode в pipefs виртуальная файловая система в пространстве ядра), но перенаправления часто связаны с файлами, которые имеют записи на диске и поэтому имеют соответствующий индексный дескриптор.
  • трубы не 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() Системный вызов.

Введение

Чтобы понять, как эти два механизма отличаются друг от друга, необходимо понять их основные свойства, историю, стоящую за ними, и их корни в языке программирования C. Фактически, зная, какие файловые дескрипторы есть, и как dup2() а также pipe() работа системных вызовов имеет важное значение, а также lseek(), Shell предназначена для того, чтобы сделать эти механизмы абстрактными для пользователя, но копание глубже абстракции помогает понять истинную природу поведения оболочки.

Происхождение перенаправления и трубопроводов

Согласно статье Денниса Ритче Пророческие петроглифы, трубы возникли из Внутренняя памятка 1964 года от Малькольм Дуглас Макилрой, в то время, когда они работали над Многофункциональная операционная система, Цитата:

Чтобы выразить свою сильную озабоченность в двух словах:

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

Очевидно, что в то время, когда программы были способны записывать на диск, однако это было неэффективно, если выход был большим. Процитировать объяснение Брайана Кернигана в Трубопровод 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() Системный вызов, который будет принимать в качестве входного массива (aka list), называемый 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() syscall, чтобы перейти к байту 120, затем вернуться к байту 10, а затем переместиться до конца. Трубы не статичны - они последовательны, и поэтому вы не можете перемотать данные, которые вы получаете от них lseek(), Это то, что делает некоторые программы осведомленными, если они читают из файла или из канала, и поэтому они могут внести необходимые корректировки для эффективной работы; другими словами, prog может определить, буду ли я cat file.txt | prog или prog < input.txt, Настоящий пример работы хвост,

Два других очень интересных свойства труб - это то, что у них есть буфер, который по Linux - 4096 байт, и на самом деле у них есть файловая система, определенная в исходном коде Linux ! Они не просто объект для передачи данных, они сами являются файловой структурой! Фактически, поскольку существует файловая система pipefs, которая управляет как трубами, так и FIFO, трубы имеют индекс число в соответствующей файловой системе:

# 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
2017-09-12 09:26





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

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,

Во втором примере голова потребляет первую строку, но когда она закрывает свою собственную stdin  труба, файл остается открытым для следующего вызова.

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

Таким образом, «поток» - это то, с чем мы шунтируем данные через (stdin и т. Д.) И одинаково в обоих случаях, но канал соединяет потоки из двух процессов, где перенаправление соединяет потоки между процессом и файлом, поэтому вы может видеть источник как сходства, так и различий.

Постскриптум Если вы так же любопытны и / или удивлены этими примерами, как и я, вы можете продолжить копать дальше 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`

2
2018-06-05 00:54



«когда первый вызов главы заканчивается, он закрывает трубу». Это фактически неточно по двум причинам. Один из них (head -n1; head -n1) является подоболочкой с двумя командами, каждая из которых наследует конец чтения в качестве дескриптора 0 и, следовательно, подглазу. И каждая команда имеет этот дескриптор файла. Вторая причина, вы можете видеть, что с strace -f bash -c 'seq 5 | (head -n1, head -n1) '. Итак, первая глава закрывает только свою копию файлового дескриптора - Sergiy Kolodyazhnyy
Третий пример также является неточным, поскольку read потребляет только первую строку (это один байт для 1 и новой строки). seq отправлено всего 10 байт (5 номеров и 5 строк новой строки). Таким образом, в буфере канала осталось 8 байт, и именно поэтому второй head работает - все еще доступны данные в буфере труб. Btw, head выходы, только если есть 0 байт, прочитанных, как в head /dev/null - Sergiy Kolodyazhnyy
Спасибо за разъяснения. Правильно ли понимаю, что в seq 5 | (head -n1; head -n1) первый вызов опустошает трубу, поэтому он все еще существует в открытом состоянии, но без данных для второго вызова head? Таким образом, разница в поведении между трубой и перенаправлением заключается в том, что голова вытаскивает все данные из трубы, но только 2 строки из дескриптора файла? - Julian de Bhal
Правильно. И это то, что можно увидеть с помощью strace команда, которую я дал в первом комментарии. При перенаправлении файл tmp находится на диске, что делает его доступным для поиска (потому что они используют lseek() syscall - команды могут перемещаться по файлу с первого байта до последнего, но они хотят. Но трубы являются последовательными и не доступны для поиска. Таким образом, единственный способ для руководителя выполнить свою работу - сначала прочитать все, или файл большой - скопируйте часть из них в ОЗУ через mmap() вызов. Я когда-то сам tail в Python, и столкнулся с точно такой же проблемой. - Sergiy Kolodyazhnyy
Также важно помнить, что конец считывания канала (дескриптор файла) сначала присваивается подоболочке (...), и подоболочка сделает копию своего собственного stdin каждой команде внутри (...), Поэтому они технически читаются с одного и того же объекта. Первый headдумает, что он читает из своего собственного stdin. второй head думает, что у него есть собственный stdin. Но на самом деле их fd # 1 (stdin) является просто копией того же fd, который является прочитанным концом трубы. Кроме того, я отправил ответ, так что, возможно, это поможет прояснить ситуацию. - Sergiy Kolodyazhnyy