Труба от B до D? - A && B || C | D

14

Есть ли способ переписать структуру команд A && B || C | Dтак, чтобы B или C передавались по D?

С текущей командой запускаются только B или C и D.

Например:

введите описание изображения здесь

Филип Киркбрайд
источник

Ответы:

31

Да, в bash вы можете использовать круглые скобки:

(A && B || C) | D

Таким образом, вывод A && B || Cбудет передан в D.

utopiabound
источник
6
Или A && (B || C) | D, если вы не хотите, чтобы B, C или D запускались при сбое A
zwol
2
Вы также можете использовать парены в sh:)
EKons
Неясно, было ли использование скобок вместо фигурных скобок преднамеренным решением (если да, каковы преимущества?) Или просто упущением. Могли бы вы объяснить?
Том Фенек,
@TomFenech Parens заставляет выражение работать в под-оболочке, которая является (отдельным) процессом, отличным от POV остальной части сценария. Поэтому, независимо от вывода выражения внутри паренов, оно будет передано вместе. (Так как Aнаходится внутри подоболочки, это также включает в себя A.)
jpaugh
1
@jpaugh Я думаю, что ваша точка зрения на изоляцию хорошая, но результат будет таким же, как при использовании фигурных скобок, не так ли?
Том Фенек,
14

Вы можете написать это как

if A; then B; else C; fi | D

Вы говорите, что хотите бежать Bили C, но A && B || Cне достигаете этого. Если Aуспешно, но Bработает и не удается, он будет выполнен C.

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

{ A && B || C; } | D

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

Примечание 2: обе формы предполагают, что не Aпроизводит вывод, что верно в вашем примере, но не обязательно так в общем. Этого можно избежать

A; if [ "$?" -eq 0 ]; then B; else C; fi | D
HVD
источник
Вы уверены, что { … }не создаст подоболочку из-за трубы? Я наблюдаю следующее поведение: pgrep bashи, pgrep bash | catи if true; then pgrep bash; fiи { pgrep bash; }есть одна строка вывода; ( pgrep bash; )и ( pgrep bash; ) | catи { pgrep bash; } | catи if true; then pgrep bash; fi | catимеют две строки вывода.
wchargin
@wchargin ... | ...вызывает создание подоболочки, это неизбежно. ( ... )По крайней мере теоретически, это приводит к созданию дополнительной подоболочки, которая { ...; }избегает, но это то, что я имел в виду, «может или не может быть оптимизировано»: возможно, что в данном конкретном случае оболочка понимает, что это не имеет значения, эффект был бы таким же.
17
5

Ответ акцептора верен, но он не охватывает потенциальный вариант использования, чтобы не выводить в Aкачестве входных данных D. Для этого вам понадобится перенаправление потока в Aзависимости от ваших потребностей.

  • Если вы хотите отказаться от вывода в Aлюбом случае:

    { A >/dev/null && B || C; } | D
  • Если вы хотите увидеть вывод Aна терминале:

    { A >/dev/tty && B || C; } | D
  • Если вам нужен вывод в Aкачестве ввода последующей команды, Eвам понадобится дополнительная группа команд и перенаправление потока:

    { { A >&3 && B || C; } | D; } 3>&1 | E

Если все это выглядит слишком загадочно для вас (как и для меня), я рекомендую вам использовать специальную переменную оболочки для статуса выхода Aи работать с этим:

A
if [ $? -eq 0 ]; then
  B
else
  C
fi |
D

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

A; { [ $? -eq 0 ] && B || C; } | D

(См. Также последнюю часть ответа hvd, которую я не заметил, когда писал свой оригинальный ответ.)

Дэвид Фёрстер
источник
Мой ответ действительно охватывает это. Посмотрите, что я вставил в «Примечание 2:», где я просто Aвышел из конвейера.
HVd
@hvd: Вы правы, и спасибо, что указали на эту часть вашего ответа мне! Я изменил свое требование и дал вам кредит соответственно.
Дэвид Фёрстер