Какой из них лучше: использовать ; или && для выполнения нескольких команд в одной строке?

В учебных пособиях и инструкциях я часто вижу комбинированные команды. Например,

sudo apt-get update && sudo apt-get install pyrenamer

По-видимому, существует четыре возможных соединителя: &, &&, || и ;. Несмотря на то, что & коннектор мне понятен (он отправляет процесс в фоновый режим и оставляет терминал доступным), непонятно, в чем разница между && и ;. И я не знал о || до комментария Кайи.

Следующие вопросы касаются разницы между двумя соединителями, но в основном это делается в комментариях:

Итак, вот ряд связанных с этим вопросов:

  1. В чем разница между ; и &&?
  2. Когда вы должны их использовать соответственно? Было бы неплохо увидеть некоторые варианты использования: если я хочу запустить команду, а затем после ее завершения выключить компьютер, какой разъем мне следует выбрать?
  3. Каковы их преимущества и опасности? Я Делаю Басак упоминания в комментарии к этот ответ что команда, подобная cd /somewhere_else; rm -Rf * может иметь разрушительные последствия, например, если первый элемент в цепочке команд выходит из строя.
  4. Если это уместно, то откуда они берутся?

Мошеннический лист:

A; B    # Run A and then B, regardless of success of AA && B  # Run B if and only if A succeededA || B  # Run B if and only if A failedA &     # Run A in background.

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

Ваш пример с && может быть эквивалентно перефразировано как

if sudo apt-get update ; then    sudo apt-get install pyrenamerfi

С помощью ; будет выполнять команды независимо от того, будет ли первая команда успешной или нет.

С помощью && будет выполняться вторая команда только тогда, когда первая команда выполнена успешно (статус 0).

Оба они используются с разных точек зрения. Например, для более длительного процесса, скажем, для установки вам нужно скомпилировать и установить его. вам следует make && make install. Таким образом, установка будет выполняться только в том случае, если make успешный.

Поэтому для зависимых команд вы должны использовать &&.

Wring bash или команды с независимыми командами, используйте ;.

Поэтому, если вы хотите выключить компьютер даже при сбое первого задания, используйте ; , но если хотите при полном успехе первого задания инициировать завершение работы, используйте &&.

a ; b будет выполняться b независимо от статуса выхода a. a && b будет выполняться b только в том случае, если a удалось выполнить.

Это необходимо и достаточно для ответа на первые 3 вопроса. В частности, 2 является слишком широким, и на него нельзя дать "один" окончательный ответ - лучше всего принимать решение в каждом конкретном случае.

Что касается 4-го вопроса: Это синтаксис Bash.

В использовании ни того, ни другого нет никакой внутренней опасности. Опять же, приведенного выше определения достаточно. Это подразумевает, что вы напишете && когда b имеет непреднамеренные последствия, если a не удается. Имхо, нет необходимости в дополнительных правилах или объяснениях.

Ответ Джека это очень хорошее эмпирическое правило.

A; B    # Run A and then B, regardless of success of AA && B  # Run B if and only if A succeededA || B  # Run B if and only if A failedA &     # Run A in background.

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

Примеры:

  • Объединить выходные данные двух команд:

    (ls foo; ls bar) > single-result.txt
  • Перейдите в каталог и выполните команду оттуда, не изменяя текущий каталог оболочки:

    (cd target-directory && jar -xf ../my-jar)

Честь и хвала Чит-лист Джека за то, что четко и лаконично объяснил, как это работает. Но все эти вещи имеют более общую причину для того, чтобы быть выбранными. Вам не нужно просто запоминать эти факты, когда вы делаете что-то в оболочке. Для получения более подробной информации вам также может быть интересно узнать, что:

  • ; обычно используется в качестве разделителя операторов в таких языках, как C, C++, Perl, Java, C#, awk и множестве языков, которые были производными от них. Во всех этих случаях он выполняет одну и ту же функцию: делает одно, затем следующее. Это как точка в конце предложения.
  • & используется в C для побитового И так далее && был использован для логического И. Логически упорядочивание вещей в C включает в себя функцию, называемую сокращением. Короткое замыкание означает, что для И, если первое значение равно false, нет причин проверять второе значение. Один ложный элемент в И означает, что все это будет ложным. В оболочке это означает, что она не перейдет ко второй команде, если первая завершится неудачей. Или, если первое сработало, он перейдет ко второму, чтобы посмотреть, "может ли это быть ложным".
  • | используется в C для побитового ИЛИ около того || используется для логического ИЛИ. Логические ИЛИ также имеют функцию сокращения. В ИЛИ это означает, что вы ищете первую истинную вещь. Так что, если первое не удастся, он попробует второе.

Другие говорили об этом хорошо. С практической точки зрения я часто нахожу, что если вы хотите связать команды в цепочку, то вам также хотелось бы знать, где что-то выходит из строя. Для этого вам нужно понять группировку в командах bash с использованием фигурных скобок.

Вместо a && b вы захотите написать:

a || { echo "a failed" && false; } && b || echo "b failed"

Существует еще один соединитель, с которым вы, возможно, не сталкивались: || - это то же самое, что и &&, за исключением того, что он выполняет вторую команду только в том случае, если первая завершилась с ненулевым (неудачным) статусом.

Также обратите внимание, что запуск вашего скрипта с помощью set -e остановит сценарий при сбое, как если бы все команды были связаны с &&.

Никто не ответил на вопрос 4… Я подозреваю, что поведение && и || было вдохновлено языком программирования C. В случае (x && y), если x вычисляется как false, все выражение должно быть false, чтобы компилятор мог оптимизировать вычисление y, если это было дорого. Современные стандарты C и C++ на самом деле * требуют * такой оптимизации, поэтому программы могут с уверенностью предположить, что y не будет вычисляться, если x равно false. Например, (ptr && ptr->days > 31) не приведет к сбою, даже если ptr равен null. Также в C операторы заканчиваются на ; независимо от того, есть ли другой оператор в той же строке или нет.