Почему объединение управляет на одной строке в сценарии Bash?

Я плохо знаком со сценариями Bash и Linux. На работе я видел сценарии Bash с конструкциями, подобными этому:

mkdir build && cd build && touch blank.txt

Или:

mkdir build; cd build; touch blank.txt

Или даже экзотическое:

COMMAND="mkdir build && cd build && touch blank.txt"
eval ${COMMAND}

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

mkdir build
cd build
touch blank.txt

Там технические преимущества - к зубрежке все на одной строке?

35
задан 21 October 2019 в 18:44

8 ответов

mkdir build && cd build && touch blank.txt

В Bash (и некоторые другие оболочки) && логическое и, что означает - если предыдущая команда возвратит true, то следующая команда будет выполняться. Там также логично или ||. Например, можно объединить эти две опции в одном операторе:

mkdir /tmp/abc/123 && echo 'the dir is created' || echo "the dir isn't created"

Отметьте, конструкция cmd_1 && cmd_2 || comd_3 не замена if; then; else оператор, потому что, неважно, какая из предыдущих команд возвращает false, cmd_3 будет выполняться. Таким образом, необходимо быть осторожны относительно обстоятельств, при которых Вы используете его. Вот пример:

$ true && false || echo success
success
$ false && true || echo success
success
$ false && false || echo success
success

Как правило, эмпирическое правило, обычно, когда я использую cd команда в рамках сценария, я помещаю тест, успешно ли изменение каталога: cd somewhere/ || exit. Более надлежащий тест предложен @dessert: if ! cd dir; then exit 1; fi. Но во всех случаях как защита отказа сценария лучше использовать set -e опция, поскольку это показывают в ответе @mrks'.


mkdir build; cd build; touch blank.txt

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


Отметьте когда ; или && и || используются это не обязательно для записи команд в на строке - это проиллюстрировано в рамках ответа @allo.

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


COMMAND="mkdir build && cd build && touch blank.txt"
eval ${COMMAND}

Здесь одна или несколько команд (и их аргументы) сгруппированы как значение переменной, и затем эта переменная (аргумент) преобразовывается в команду справкой eval. Таким образом Вы сможете изменить команду, которая будет выполняться многократно в рамках некоторого сценария путем изменения его только в одном месте.

Скажем, необходимо изменить путь в который файл blank.txt создается, например, можно изменить соответствующую строку способом как это:

COMMAND="mkdir build && cd build && echo 'test' > blank.txt"

Фактическое evalпреимущество перед использованием alias когда существуют перенаправления, каналы, логические операторы, и т.д. используются. В большинстве случаев можно использовать functions вместо eval когда alias не применимо. Также в случае, если переменная содержит только единственную команду, т.е. CMD="cat", нам не нужно eval, потому что разделенный на мир Bash расширится "$CMD" "$file1" "$file2" правильно.

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

54
ответ дан 23 November 2019 в 00:22

Ответы до сих пор обращаются к тому, что происходит/как, это работает, но я думаю, что Вы спрашивали "почему"

Основная "Причина" сделать (для меня) вместо того, чтобы ввести их на трех строках случается так, что иногда команды занимают время (Иногда минуты), и Вы не хотите болтаться в течение всего времени.

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

Опережающий ввод является альтернативой, но это сомнительно, Вы не смогли видеть то, что Вы вводите (и поэтому делаете ошибки), или программа могла бы съесть опережающий ввод (Спасибо, чаши Грааля!). Затем Вы возвращаетесь из ланча и узнаете, что у Вас все еще есть две длинноватых задачи начаться из-за опечатки.

Другая альтернатива пишет маленький сценарий или псевдоним. Не плохая идея, но не позволяет столько же гибкости, как я могу:

stop && build && test && deploy && start 

или я могу просто:

stop && start

Или любое количество других комбинаций, которые быстро загрязнили бы сценарий наборами флагов.

Даже если Вы действительно запишете сценарий, то Вы, вероятно, интегрируете его с другими сценариями с &&.

13
ответ дан 23 November 2019 в 00:22

Объединение команд на Linux может быть очень полезным.

Хороший пример может перезапускать интерфейс удаленной сети через ssh (при изменении конфигурации сети или чего-то...).

ifdown eth0 && ifup eth0

Это может препятствовать тому, чтобы Вы шли физически в сервер, для перевода в рабочее состояние интерфейса Вы где первоначально ssh'ing к. (Вы не сможете, выполняются ifup eth0 если ifdown eth0 выполняется один.)

10
ответ дан 23 November 2019 в 00:22

Что делают команды?

Для понимания, почему, мы также должны понять то, что делается. Сценарии последовательны. В Вашем последнем примере:

mkdir build
cd build
touch blank.txt

cd build будет выполняться независимо от mkdir build следование или нет. Конечно, если mkdir fails, Вы будете видеть ошибку от cd.

mkdir build; cd build; touch blank.txt последовательный список. Это может думаться как по существу то же как несколько строк сценария. Грамматика Shell будет рассматривать это немного по-другому, но результатом будет точно то же как выше. Снова, команды выполняются независимо от того, успешно выполнилось ли предыдущее.

Наконец, существует

mkdir build && cd build && touch blank.txt

который является И списки - последовательность одного или нескольких конвейеров (или команды) разделен && оператор. Команды в таком списке выполняются с левой ассоциативностью. Это означает, оболочка будет продолжать брать две команды/конвейера, разделенные &&, выполните тот слева сначала и выполните тот справа, только если оставленный, за которым следуют (возвратил нулевой статус выхода).

В этом примере, что произойдет?

  • оболочка выполняется mkdir build сначала.
  • выше команды, за которой следуют (возвратил код выхода 0), cd build будет работать
  • Снова, давайте посмотрим на левую ассоциативность. ... && touch blank.txt. Действительно наполнял слева от && успешно выполниться? Если да, выполненный touch blank.txt.

Почему использование последовательные списки по сравнению с И список?

Последовательный список mkdir build; cd build; touch blank.txt имеет смысл, когда мы соглашаемся с одним из сбоя команд. Предположим mkdir build уже существует. Мы все еще хотим cd в build/ каталог и touch blank.txt. Конечно, недостаток здесь - то, что существует возможность непреднамеренного результата. Что, если build имеет не выполняют набор битов, и вот почему cd build сбои? touch blank.txt произойдет в нашем текущем рабочем каталоге вместо намеченного build/.

Теперь рассмотрите mkdir build && cd build && touch blank.txt. Это несколько более логично, хотя имеет его собственные ловушки. Если build уже существует, вероятно, кто-то уже сделал touch blank.txt также, таким образом, мы не можем хотеть делать touch blank.txt снова, поскольку это изменит метку времени доступа на нем, которая является чем-то, что не могло бы быть желательно.

Заключение

Размещение команд на той же строке зависит от цели команд и чего Вы пытаетесь достигнуть. Последовательные списки могут упростить вывод, поскольку оболочка не вновь отобразит подсказку, пока команды не закончились. И список допускает условное выполнение команд и может предотвратить выполнение ненужных команд. Нижняя строка - то, что пользователь должен знать различия и что выбрать как правильный инструмент для задачи.

7
ответ дан 23 November 2019 в 00:22

Другая причина могла бы быть в том, как сценарий оказался: Для технических пользователей весьма обычно создать очень длинные интерактивные командные строки, неоднократно расширяясь и тестируя их, пока они не работают, как желаемый, затем вставляя результат в текстовый файл и хлопая a #!/bin/bash заголовок по нему. Иногда несколько сегментов сценария развиты тем методом.

Долго конструкции строки, которые начинаются for i in, grep | cut | sed | xargs цепочки, интенсивное использование && и ||, $(cat something) конструкции часто показательны из таких источников.

5
ответ дан 23 November 2019 в 00:22

Почему нам иногда нравится использовать его

Когда мы пишем, что ответы здесь в Спрашивают Ubuntu, полезно поместить их в одну строку, которую люди могут вырезать и вставить в их легче терминал. Например:

$ CurrDir=$PWD ; cd /bin ; ls -la dd ; cd "$CurrDir"

-rwxr-xr-x 1 root root 72632 Mar  2  2017 dd

Как отмечалось ранее, Вы могли использовать && для того же эффекта, но если были ошибки выше Вас, мог бы закончиться на неправильном каталоге. В этом случае ; предпочтительно для && это не тестируется на успех:

rick@alien:~/askubuntu$ CurrDir=$PWD && cd /bin && llllssss -la dd && cd "$CurrDir"

llllssss: command not found

rick@alien:/bin$ 

Почему мы должны использовать его

Существуют некоторые команды, где необходимо представить несколько строк в виде строки вместе. Один случай является сокращенным if- команды -fi который должен быть одной строкой. Например:

$ [[ 2 -eq 2 ]] && { echo 2 equals 2 ; echo setting three to 3 ; three=3 ; }

2 equals 2
setting three to 3
4
ответ дан 23 November 2019 в 00:22

Там технические преимущества - к зубрежке все на одной строке?

Это - только вопрос стиля. Нет технических преимуществ.

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

foo &&
bar

делает то же как foo && bar. Все еще необходимо быть осторожными, как

foo
&& bar

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

Когда Вы все еще хотите, чтобы Ваш сценарий был более читаемым, можно выйти из символа новой строки как это

foo \
&& bar

Удостоверьтесь, что нет никакого пробела после \ символ, как это должно прибыть непосредственно перед символом новой строки.

3
ответ дан 23 November 2019 в 00:22

Многие ответы здесь говорят о безопасности, например, "Что происходит если cd сбои"?

При объединении команд такой как cd foo && rm * решает ту проблему, существует намного лучший способ сделать это:

Возьмите эту структуру папок:

.
./foo
./foo/bar
./foo2
./foo2/bar2
./test.sh
./do_not_delete_me

и следующий сценарий:

find .
cd notexisting
rm *
cd ../foo2
rm *
cd ..
find .

Как notexisting не существует, rm * будет работать в . и удалите do_not_delete_me.

Однако, если Вы запускаете сценарий с set -e, это прервется с первой неудавшейся командой:

./test.sh: line 4: cd: notexisting: No such file or directory

Как упомянуто в комментариях, имеет смысл включать его в хижину: #!/bin/bash -e.

2
ответ дан 23 November 2019 в 00:22

Другие вопросы по тегам:

Похожие вопросы: