Что эквивалентно && при написании bash-скрипта?

31

Я заранее прошу прощения, если это дублирующий вопрос. Я приложил усилия для поиска / проверки, прежде чем спрашивать здесь.

Мне удобно писать однострочники, как это:

foocommand && foocommand2 && foocommand3

Идея в том, что я хочу, чтобы последующие команды выполнялись только в том случае, если предыдущая была «успешной».

Я пишу довольно длинный скрипт, и этот однострочный текст неосуществим, потому что он выглядит как огромный блок запутанного кода для всех остальных.

Я хочу разместить команды и написать комментарии между ними в скрипте. Как я могу сделать это и при этом иметь эквивалент && там?

Майк Б
источник
4
Это медведи повторить: &&не означает , что последующая команда будет работать , если предыдущий один был успешным. Это означает , что команда будет работать , если коллективный результат из всех предыдущих команд в списке команд успеха. Вы можете знать это, но будущие читатели могут неправильно понять.
Кодзиро
1
Также просто в качестве примечания, это поведение, которое вы описываете, происходит ||и &&(в отличие от |или &) называется коротким замыканием. Поведение, используемое с последними операторами, называется энергичной оценкой.
AndyPerfect
7
@kojiro: я не вижу различий. a && b && cбудет работать только , bесли aудастся, так что «это работает только , cесли bудастся» и «она работает только cесли aи bкак добиться успеха» эквивалентны утверждения: bне может быть успешным , если aне удалось.
Руах
1
@ruakh a || b && cболее показателен.
Кодзиро
8
@ruakh нет, true || false && echo hiбудет выводить hi. Спецификация гласит : операторы "&&" и "||" должен иметь равный приоритет и должен оцениваться с левой ассоциативностью.
Кодзиро

Ответы:

23

Вы можете сделать это так:

#!/bin/sh
ls -lh &&
    # This is a comment
    echo 'Wicked, it works!'

Надеюсь, я правильно понял, что вы спросили.

schaiba
источник
Спасибо. Это ближе всего к тому, что я надеялся сделать. И это позволяет очень легко преобразовать обратно в один лайнер, если это необходимо.
Майк Б
Майк, так как ты беспокоишься о чистоте кода, общепринятым условием является отступ 2-N разделов цепочки под первым.
Jblaine
@jblaine Я не уверен, что понимаю, что ты имеешь в виду. Можете ли вы уточнить?
Майк Б
Майк, Stackexchange не позволит мне правильно отформатировать мой ответ, так что все, что я имел в виду: отступ
jblaine
@jblaine Хорошее замечание, отредактировано для отступа строк.
Фолькер Сигел
38

Вы можете изменить линию Шебанга на

#!/bin/bash -e

После любой ошибки скрипт остановится.

choroba
источник
1
Интересный. Я буду иметь это в виду. Спасибо.
Майк Б
спасает меня от набора set -e, когда мне это нужно
Silverrocker
@ Silverrocker Это тоже POSIX (за исключением bashчасти конечно). Оболочка POSIX принимает множество параметров, которые применимы к set. Или наоборот? setспособ установки параметров командной строки оболочки в скрипте
Kaz
7
К сожалению, существует ряд стандартных утилит, которые не следуют обычным соглашениям о состоянии выхода, поэтому -eмогут плохо себя вести. Например, вы, вероятно, не хотите, чтобы ваш скрипт завершал работу каждый раз, когда у вас diffдва неидентичных файла. Конечно, вы можете обойти это, добавляя || true(или, возможно || [ $? = 1 ]) к таким командам, но ОП упоминает «всех остальных», которым придется читать этот сценарий, и говорит, что «все остальные», вероятно, не будут привыкать к необходимости справиться с -e.
Руах
4
Я использовал -eее, пока один администратор моей компании не продолжал запускать мои скрипты bash myscript.sh, поэтому он игнорировал -e. Теперь все мои скрипты set -eво второй строке.
Карлос Кампдеррос
19

Если вам не нравится set -eидея, возможно, вы можете инвертировать логику.

foocommand || exit 1
foocommand2 || exit 2
foocommand3 || exit 3

Полезнее заменить exitна что-нибудь, чтобы напечатать полезное сообщение об ошибке, а затем выйти. Внутри функции, конечно, вы хотите returnвместо exit.

tripleee
источник
11
Или foocommand || exitдля выхода с foocommandвыходным статусом, если не ноль.
Стефан Шазелас
Как это работает? Это как в C - если левый аргумент оценивается как TRUE, больше аргументов не проверяется?
Vorac
Да все верно. Это распространенная идиома в Лиспе (и, полагаю, в функциональных языках в целом) и в Perl.
tripleee
9

Вы можете использовать if else fiблоки вместо этого.

if foocommand; then

  # some comments

  if foocommand2; then

    # more comments

    foocommand3
  fi
fi

Это немного более читабельно.

Кроме того, вы можете просто использовать, \чтобы разбить ваш большой 1 лайнер на несколько строк

foocommand && \
  # some comment
  foocommand2 && \
    # more comment
    foocommand3

Но, конечно, это может сбить с толку неподготовленного глаза.

Мартин Канаваль
источник
4
Я не думаю, что \там необходимо.
Сэмюэль Эдвин Уорд
Вы правы. Сила привычки.
Мартин Канаваль
5

Вы можете рассмотреть возможность использования вложенных ifоператоров. Другой вариант заключается в использовании кудрявых для группировки как{ true && echo hi; } || echo huh

Обновление 1:

Вот пример без новых строк / комментариев:

{ { false && echo "inner true"; } && { echo "inner true" && true; } || { echo "inner false" && false; } || echo "outter false"; }
livingstaccato
источник
@ Stephane Спасибо за добавление точки с запятой. Я использовал свой телефон, чтобы ответить, поэтому я не дважды проверял свои ответы. :)
livingstaccato
Интересная идея. Да, я думал о вложенных операторах if, но если я объединю много вещей в цепочку, это может стать грязным.
Майк Б
4

Разделите каждую последовательность команд на функции:

big_block_1() {
  # ...
}
big_block_2() {
  # ...
}
big_block_3() {
  # ...
}

big_block_1 && big_block_2 && big_block_3
Фуад сауд
источник
0

Мне всегда нравится писать функцию «сбой», которая позволяет мне указать, что я собирался сделать, когда произошел сбой, а затем использовать ||шаблон. Как следующее:

function fail() {
  echo "Failure: ${@}"
  exit 1
}

foocommand || fail "couldn't foo"
Дейв
источник