процесс замещения и трубы

<p>мне было интересно, как понять следующее:</p><blockquote> <p>передача стандартного вывода команды в стандартную команду другого является мощной техникой. но что, если вам нужно передать стандартный вывод нескольких команд? это где процесс замены приходит.</p> </blockquote><p>другими словами, может ли процесс замещения делать то, что может делать канал?</p><p>что может заменить процесс, а труба - нет?</p>

<p>хороший способ уловить разницу между ними - немного поэкспериментировать в командной строке. несмотря на визуальное сходство в использовании <code><</code> символ, он делает что-то очень отличное от перенаправления или канала.</p><p>давайте использовать команду <code>date</code> для тестирования.</p><pre><code>$ date | cat thu jul 21 12:39:18 eest 2011 это бессмысленный пример, но он показывает, что cat принял вывод date на stdin и выплюнул его обратно. те же результаты могут быть достигнуты путем замены процесса:$ cat <(date) thu jul 21 12:40:53 eest 2011 </code></pre><p>однако то, что произошло за кадром, было другим. вместо того чтобы получить поток stdin, <code>cat</code> фактически было передано имя файла, который необходимо открыть и прочитать. вы можете увидеть этот шаг, используя <code>echo</code> вместо <code>cat</code>.</p><pre><code>$ echo <(date) /proc/self/fd/11 когда cat получил имя файла, он прочитал содержимое файла для нас. с другой стороны, echo просто показал нам имя файла, который был передан. эта разница становится более очевидной, если вы добавите больше замен:$ cat <(date) <(date) <(date) thu jul 21 12:44:45 eest 2011 thu jul 21 12:44:45 eest 2011 thu jul 21 12:44:45 eest 2011 $ echo <(date) <(date) <(date) /proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13 можно объединить подстановку процесса (который генерирует файл) и перенаправление ввода (который подключает файл к stdin):$ cat < <(date) thu jul 21 12:46:22 eest 2011 </code></pre><p>это выглядит примерно так же, но на этот раз кошке был передан поток stdin вместо имени файла. вы можете увидеть это, попробовав это с помощью echo:</p><pre><code>$ echo < <(date) <blank> поскольку echo не читает stdin и аргумент не передается, мы ничего не получаем.каналы и входные данные перенаправляют содержимое в поток stdin. подстановка процессов запускает команды, сохраняет их вывод в специальный временный файл и затем передает это имя файла вместо команды. любая команда, которую вы используете, обрабатывает ее как имя файла. обратите внимание, что созданный файл - это не обычный файл, а именованный канал, который удаляется автоматически, когда он больше не нужен.

вот три вещи, которые вы можете сделать с заменой процесса, которые невозможны в противном случае.несколько входов процессаdiff <(cd /foo/bar/; ls) <(cd /foo/baz; ls) там просто нет способа сделать это с трубами.сохранение stdinскажем, у вас есть следующее:curl -o - http://example.com/script.sh #/bin/bash read line echo "you said ${line}!" </code></pre><p>и вы хотите запустить его напрямую. следующее терпит неудачу с треском. bash уже использует stdin для чтения сценария, поэтому другой ввод невозможен.</p><pre><code>curl -o - http://example.com/script.sh | bash </code></pre><p>но этот способ работает отлично.</p><pre><code>bash <(curl -o - http://example.com/script.sh) </code></pre><h3>замена исходящего процесса</h3><p>также обратите внимание, что процесс подстановки работает и в другом направлении. таким образом, вы можете сделать что-то вроде этого:</p><pre><code>(ls /proc/*/exe >/dev/null) 2> >(sed -n \ '/permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt ) </code></pre><p>это немного запутанный пример, но он отправляет <em>stdout</em> в <code>/dev/null</code>, в то время как конвейер <em>stderr</em> к сценарию sed, чтобы извлечь имена файлов, для которых была отображена ошибка "отказано в доступе", а затем отправил результаты в файл.</p><p>обратите внимание, что первая команда и перенаправление <em>stdout</em> находятся в скобках (<em>subshell</em>), так что только результат команды th отправляется <code>/dev/null</code> и это не портит остальную часть строки.</p>

<p>полагаю, вы говорите о <code>bash</code> или какой-то другой расширенной оболочке, потому что оболочка posix не имеет <em>подстановка процесса</em>.</p><p><code>bash</code> справочные страницы отчетов:</p><blockquote> <p><b>замена процесса</b><br> подстановка процессов поддерживается в системах, которые поддерживают именованные каналы (fifo) или метод/dev/fd для именования открытых файлов. он принимает форму <(список) или> (список). список процессов запускается с его входом или выходом, подключенным к fifo или некоторому файлу в/dev/fd. имя этого файла передается в качестве аргумента текущей команде в результате расширение. если используется форма> (список), запись в файл обеспечит ввод для списка. если используется форма <(список), файл, переданный в качестве аргумента, следует прочитать, чтобы получить выходные данные списка.<br> <br> когда доступно, подстановка процесса выполняется одновременно с расширением параметров и переменных, подстановкой команд и арифметическим расширением.</p> </blockquote><p>другими словами, и с практической точки зрения, вы можете использовать выражение, подобное следующему</p><pre><code><(commands) </code></pre><p>в качестве имени файла для других команд, для которых в качестве параметра требуется файл. или вы можете использовать перенаправление для такого файла:</p><pre><code>while read line; do something; done < <(commands) </code></pre><p>возвращаясь к вашему вопросу, мне кажется, что процесс замещения и каналы имеют мало общего.</p><p>если вы хотите последовательно передать вывод нескольких команд, вы можете использовать одну из следующих форм:</p><pre><code>(command1; command2) | command3 { command1; command2; } | command3 </code></pre><p>но вы также можете использовать перенаправление при замене процесса</p><pre><code>command3 < <(command1; command2) </code></pre><p>наконец, если <code>command3</code> принимает параметр файла (в замене stdin)</p><pre><code>command3 <(command1; command2) </code></pre>

<p>если команда принимает список файлов в качестве аргументов и обрабатывает эти файлы как ввод (или вывод, но не часто), каждый из этих файлов может быть именованным каналом или псевдофайлом/dev/fd, прозрачно предоставляемым подстановкой процесса:</p><pre><code>$ sort -m <(command1) <(command2) <(command3) это будет "передавать" выходные данные трех команд для сортировки, так как сортировка может принимать список входных файлов в командной строке.

следует отметить, что подстановка процесса не ограничивается формой <(command), которая использует вывод command в виде файла. он может иметь вид >(command), который также подает файл в качестве входных данных для command. это также упоминается в цитате из руководства bash в ответе @ enzotib.для приведенного выше примера _date | cat_ команда, использующая подстановку процесса в форме >(command) для достижения того же эффекта, будет иметь вид:_date > >(cat) _обратите внимание, что _>_ перед >(cat) необходимо. это снова может быть ясно проиллюстрировано echo, как в ответе @ caleb._$ echo >(cat) /dev/fd/63 </code>\_</pre><p>таким образом, без дополнительного \_<code>></code>\_, <code>date >(cat)</code> будет таким же, как \_<code>date /dev/fd/63</code>\_, который будет печатать сообщение в stderr.</p><p>предположим, у вас есть программа, которая принимает в качестве параметров только имена файлов и не обрабатывает <code>stdin</code> или <code>stdout</code>. я буду использовать упрощенный скрипт \_<code>psub.sh</code>\_, чтобы проиллюстрировать это. содержание \_<code>psub.sh</code>\_</p><pre>\_<code>#!/bin/bash [ -e "$1" -a -e "$2" ] && awk '{print $1}' "$1" > "$2" _по сути, он проверяет, что оба его аргумента являются файлами (не обязательно обычными файлами), и если это так, запишите первое поле каждой строки _"$1"</code>\_ в \_<code>"$2"_, используя awk. затем команда, которая объединяет все, что упомянуто до сих пор,_./psub.sh <(printf "a anc cnb b") >(sort) _это напечатает_a b c _и эквивалентно_printf "a anc cnb b" | awk '{print $1}' | sort </code>\_</pre><p>но следующее не будет работать, и мы должны использовать здесь процесс подстановки,</p><pre>\_<code>printf "a a\nc c\nb b" | ./psub.sh | sort </code>\_</pre><p>или его эквивалентная форма</p><pre>\_<code>printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort </code>\_</pre><p>если \_<code>./psub.sh</code>\_ также читает <code>stdin</code> помимо того, что упомянуто выше, такой эквивалентной формы не существует, и в этом случае мы не можем использовать ничего вместо подстановки процесса (конечно, вы также можете использовать именованный канал или временный файл, но это уже другая история).</p>