В чем разница между «Перенаправление» и «Труба»?

205

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

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

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

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

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

Джон Трипвуд
источник

Ответы:

224

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

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

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

РЕДАКТИРОВАТЬ, чтобы предоставить более подробную информацию к вопросу в комментарии:

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

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

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

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

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

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

Флориан Диш
источник
Это будет проблемой, только если вы пишете barв каталог, который является частью вашей $PATHпеременной env. Если вы находитесь в чем-то вроде / bin, то это может быть проблемой. Но даже в этом случае barдолжен быть установлен исполняемый набор разрешений, чтобы оболочка проверяла не только поиск исполняемого файла, barно и фактически могла его выполнить. И если проблема заключается в перезаписи существующих файлов, nocloberопция оболочки должна предотвращать перезапись существующих файлов при перенаправлениях.
Сергей Колодяжный
13

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

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

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

трубы

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

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

Мистер Безотносительно
источник
12

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

  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

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

Анкит
источник
3

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

  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.

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

grep blah <<<`ls`

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

ls | grep blah

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

user319857
источник
1
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.
Муру
2
Перенаправление фактически не является аргументом для программы. Программа не будет знать и не заботиться о том, куда направляется ее вывод (или ввод поступает). Это просто способ рассказать bash, как все устроить перед запуском программы.
Алоис Махдал
3

Примечание. Ответ отражает мое собственное понимание этих механизмов на сегодняшний день, накопленное в ходе исследований и прочтения ответов коллегами на этом сайте и 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 . Цитата:

Чтобы выразить мои самые сильные опасения в двух словах:

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

Очевидно, что в то время программы могли записывать на диск, однако это было неэффективно, если выходной объем был большим. Цитировать объяснение Брайана Кернигана в видео Unix Pipeline :

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

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

Идем глубже: системные вызовы и внутренняя работа оболочки

Начнем с понятия файлового дескриптора . Файловые дескрипторы описывают в основном открытый файл (будь то файл на диске, или в памяти, или анонимный файл), который представлен целым числом. Два стандартных потока данных (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()системного вызова. Например, 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, каналы имеют номер инода в соответствующей файловой системе:

# 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 .

Смотрите также:

Сергей Колодяжный
источник
2

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

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

PS Если вы так же любопытны и / или удивлены этими примерами, как и я, вы можете продолжить изучение, 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`
Джулиан де Бхал
источник
«когда первый вызов head заканчивается, он закрывает канал». Это на самом деле неточно по двум причинам. Одна (head -n1; head -n1) является подоболочкой с двумя командами, каждая из которых наследует конец чтения канала как дескриптор 0, и, таким образом, в подполочке И каждой команде этот дескриптор файла открыт. Вторая причина: вы можете видеть это с помощью strace -f bash -c 'seq 5 | (голова -n1; голова -n1) '. Поэтому первая глава закрывает только свою копию файлового дескриптора
Сергей Колодяжный,
Третий пример также неточен, поскольку 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, который является концом канала для чтения. Кроме того, я опубликовал ответ, так что, возможно, это поможет прояснить ситуацию.
Сергей Колодяжный,
1

Я столкнулся с проблемой в C сегодня. По существу, у Pipe есть и другая семантика для перенаправлений, даже когда они отправляются stdin. На самом деле я думаю, что с учетом различий, каналы должны идти куда-то еще stdin, так что, stdinи пусть их вызов stdpipe(чтобы сделать произвольный дифференциал) можно обрабатывать по-разному.

Учти это. При передаче одного вывода программы в другой, fstatкажется, возвращается ноль, st_sizeнесмотря на то, ls -lha /proc/{PID}/fdчто он показывает, что файл существует. При перенаправлении файла это не так ( по крайней мере , на Debian wheezy, stretchи jessieванили и убунту 14.04, 16.04ваниль.

Если у вас cat /proc/{PID}/fd/0есть перенаправление, вы сможете повторить, чтобы прочитать столько раз, сколько хотите. Если вы сделаете это с каналом, вы заметите, что при втором последовательном запуске задачи вы не получите тот же результат.

MrMesees
источник