Что эквивалентно && при написании 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!'

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


Спасибо. Это ближе всего к тому, что я надеялся сделать. И это позволяет очень легко преобразовать обратно в один лайнер, если это необходимо.
Майк Б

Майк, так как ты беспокоишься о чистоте кода, общепринятым условием является отступ 2-N разделов цепочки под первым.
Jblaine

@jblaine Я не уверен, что понимаю, что ты имеешь в виду. Можете ли вы уточнить?
Майк Б

Майк, Stackexchange не позволит мне правильно отформатировать мой ответ, так что все, что я имел в виду: отступ
jblaine

@jblaine Хорошее замечание, отредактировано для отступа строк.
Фолькер Сигел

38

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

#!/bin/bash -e

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


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.


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"; }

@ 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"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.