Труба | Перенаправление <> Приоритет
Я хочу прояснить, когда труба | или перенаправление < > имеет приоритет в команде?
Это моя мысль, но нужно подтвердить, как это работает.
Пример 1:
sort < names | head
The pipe runs first: names|head then it sorts what is returned from names|head
Пример 2:
ls | sort > out.txt
This one seems straight forward by testing, ls|sort then redirects to out.txt
Пример 3:
Fill in the blank? Can you have both a < and a > with a | ???
Ответы
Ответ 1
В терминах синтаксической группировки >
и <
имеют более высокий приоритет; то есть эти две команды эквивалентны:
sort < names | head
( sort < names ) | head
как и эти два:
ls | sort > out.txt
ls | ( sort > out.txt )
Но в терминах последовательного упорядочения сначала выполняется |
; поэтому, эта команда:
cat in.txt > out1.txt | cat > out2.txt
будет заполняться out1.txt
, а не out2.txt
, потому что > out1.txt
выполняется после |
и, следовательно, заменяет его (поэтому выход не выводится на cat > out2.txt
).
Аналогично, эта команда:
cat < in1.txt | cat < in2.txt
будет печатать in2.txt
, а не in1.txt
, потому что < in2.txt
выполняется после |
и, следовательно, заменяет его (поэтому никакой ввод не поступает из cat < in1.txt
).
Ответ 2
От man bash
(как и другие кавычки):
SHELL GRAMMAR
Simple Commands
A simple command is a sequence of optional variable assignments followed by
blank-separated words and redirections, and terminated by a control
operator. The first word specifies the command to be executed, and is
passed as argument zero. The remaining words are passed as arguments
to the invoked command.
The return value of a simple command is its exit status, or 128+n if
the command is terminated by signal n.
Pipelines
A pipeline is a sequence of one or more commands separated by one of
the control operators | or |&. The format for a pipeline is:
[time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]
Другими словами, вы можете иметь любое количество переназначений для (простой) команды; вы также можете использовать это как часть конвейера. Или, по-другому, перенаправление связывается более жестко, чем труба.
Есть несколько способов получить работу над этим (хотя они редко либо необходимы, либо эстетичны):
1.
Вы можете создать "составную команду" и перенаправить в нее:
Compound Commands
A compound command is one of the following:
(list) list is executed in a subshell environment (see
COMMAND EXECUTION ENVIRONMENT below). Variable
assignments and builtin commands that affect the
shell environment do not remain in effect after the
command completes. The return status is the exit status of list.
{ list; }
list is simply executed in the current shell environment. list
must be terminated with a newline or semicolon. This is known as a
group command. The return status is the exit status of list. Note
that unlike the metacharacters ( and ), { and } are reserved words
and must occur where a reserved word is permitted to be recognized.
Since they do not cause a word break, they must be separated from
list by whitespace or another shell metacharacter.
Итак:
$ echo foo > input
$ { cat | sed 's/^/I saw a line: /'; } < input
I saw a line: foo
2.
Вы можете перенаправить на канал, используя "замену процесса":
Process Substitution
Process substitution is supported on systems that support named pipes
(FIFOs) or the /dev/fd method of naming open files. It takes the form of
<(list) or >(list). The process list is run with its input or output
connected to a FIFO or some file in /dev/fd. The name of this file is
passed as an argument to the current command as the result of the
expansion. If the >(list) form is used, writing to the file will provide
input for list. If the <(list) form is used, the file passed as an argument
should be read to obtain the output of list.
Итак:
[email protected]$ cat > >(sed 's/^/I saw a line: /') < <(echo foo; echo bar)
I saw a line: foo
[email protected]$ I saw a line: bar
(Почему приглашение появляется до завершения вывода, и что делать с ним остаются в виде упражнений).
Ответ 3
Это немного неортодоксальный, но совершенно законный, разместить <
в любом месте, где угодно, поэтому я предпочитаю это, поскольку он лучше иллюстрирует поток данных слева направо:
<input.txt sort | head >output.txt
Единственный раз, когда вы не можете этого сделать, - это встроенные команды структуры управления (for
, if
, while
).
# Unfortunately, NOT LEGAL
<input.txt while read line; do ...; done
Обратите внимание, что все это эквивалентные команды, но чтобы избежать путаницы, вы должны использовать только первый или последний:
<input.txt grep -l foobar
grep <input.txt -l foobar
grep -l <input.txt foobar
grep -l foobar <input.txt
Поскольку имя файла всегда должно появляться сразу после оператора перенаправления, я предпочитаю оставить свободное место между <
и именем файла.
Ответ 4
Исправления:
Пример 1:
sort < names | head
В этом случае сначала выполняется перенаправление ввода (сортируются имена), затем результат этого сообщения направляется в голову.
В общем, вы можете читать слева направо. Стандартная идиома работает следующим образом:
- Использование перенаправления ввода "<" сообщает программе чтение из файла вместо stdin
- Использование перенаправления вывода " > " сообщает программе вывод в файл вместо stdout
- Использование pipe "program_a | program_b" принимает все, что обычно выводится программой program_a в stdout, и передает его прямо в program_b, как если бы оно было прочитано из stdin.
Ответ 5
Это в значительной степени то, что я понимаю после некоторого чтения (в том числе ruakh)
Прежде всего, если вы переадресовываете несколько раз, все перенаправления выполняются, но только последнее перенаправление вступит в силу (если ни одно из предыдущих переназначений не вызывает ошибку)
-
например. cat < in1.txt < in2.txt
эквивалентен cat < in2.txt
, если in1.txt
не существует, и в этом случае эта команда не будет выполнена (поскольку сначала выполняется < in1.txt
)
-
Аналогично, с cat in.txt > out1.txt > out2.txt
, только out2.txt
будет содержать содержимое out2.txt
, но поскольку сначала выполнялось > out1.txt
, out1.txt
было бы создано, если оно не существует.
Какая труба соединяет stdout
предыдущей команды с stdin
следующей команды, и это соединение происходит перед любыми другими перенаправлениями (из Bash вручную).
Итак, вы можете думать о
cat in1.txt > out1.txt | cat > out2.txt
как
cat in1.txt > pipe > out1.txt; cat < pipe > out2.txt
И применяя правило множественного перенаправления, упомянутое выше, мы можем упростить это для
cat in1.txt > out1.txt; cat < pipe > out2.txt
Результат. Содержимое in1.txt
копируется в out1.txt
, так как ничего не было записано в pipe
Используя другой пример ruakh,
cat < in1.txt | cat > in2.txt
примерно эквивалентен
cat > pipe < in1.txt; cat < pipe < in2.txt
который эффективно
cat > pipe < in1.txt; cat < in2.txt
Результат. На этот раз что-то записывается в pipe
, но поскольку второй cat
читает от in2.txt
вместо pipe
, печатается только содержимое in2.txt
из.