Перейти к содержимому

Bash set e что это

  • автор:

bash: set -e и арифметические выражения

Допустим, у меня есть код со следующей арифметической операцией:

(( TOKEN_COUNTER++ )) 

При включенном set -e этот код совершенно правомерно может завалить скрипт, поскольку здесь (( воспринимается как бы как команда; в свою очередь, эта команда при (( 0 )) возвращает 1, и именно это сваливает скрипт.

Это один из вариантов решения:

(( TOKEN_COUNTER++ )) || : 

Но, может быть, есть что-нибудь, что можно использовать, не вдаваясь в подробности насчёт того, является ли (( какой-то там встроенной командой и возвращает ли она что-то там при определённых условиях? Хочется чего-то такого:

do_it_carefree (( TOKEN_COUNTER++ )) 

Как это сделано в выражениях вроде if, while, until — и им подобных. В них и инкремент к переменной можно сделать, и отрицательный код возврата не валит скрипт, а останавливает выполнение кода под выражением.

Я было подумал про let , но это оказалось тем же самым, что и (( :

((expression)) The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let «expression». 

Алсо, есть ещё непонятки с вариантами стрельбы по ногам с включенной обработкой нештатных ситуаций.

$ a() < return 1; >$ : $(a); echo $? 0 $ b=$(a); echo $? 1 

Может быть, вы можете что-нибудь сказать на этот счёт, что прояснило бы как-то такое поведение, чтобы не оставаться просто так с опытом «так надо делать, а так — не стоит»?

ABW ?

Безопасные bash-скрипты или set -euxo pipefail

В 2020 году когда есть такие языки как python, ruby и go, использовать bash приходится редко, но иногда без него не обойтись.

Bash не похож на высокоуровневые языки программирования, он не предоставляет привычных гарантий. К примеру, если в python обратиться к неинициализированной переменной, то скрипт тут же завершиться, не выполнив ни одной инструкции. В bash это не так, любая переменная, к которой вы обратились, но не инициализировали, будет заменена на пустую строку. Только представьте сколько можно наворотить дел, если подобная переменная была в параметрах у команды rm -rf .

К счастью, можно изменить поведение оболочки используя встроенные функции, в частности set. С ее помощью, можно значительно повысить безопасность.

set -e

Указав параметр -e скрипт немедленно завершит работу, если любая команда выйдет с ошибкой. По-умолчанию, игнорируются любые неудачи и сценарий продолжет выполнятся. Если предполагается, что команда может завершиться с ошибкой, но это не критично, можно использовать пайплайн || true .

#!/bin/bash ./non-existing-command echo "RUNNING" # output # ------ # line 3: non-existing-command: command not found # RUNNING 

С использованием -e :

#!/bin/bash set -e ./non-existing-command || true ./non-existing-command echo "RUNNING" # output # ------ # line 4: non-existing-command: command not found # line 5: non-existing-command: command not found 

set -o pipefail

Но -e не идеален. Bash возвращает только код ошибки последней команды в пайпе (конвейере). И параметр -e проверяет только его. Если нужно убедиться, что все команды в пайпах завершились успешно, нужно использовать -o pipefail .

#!/bin/bash set -e ./non-existing-command | echo "PIPE" echo "RUNNING" # output # ------ # PIPE # line 4: non-existing-command: command not found # RUNNING 

С использованием -o pipefail :

#!/bin/bash set -eo pipefail ./non-existing-command | echo "PIPE" echo "RUNNING" # output # ------ # PIPE # line 4: non-existing-command: command not found 

set -u

Наверно самый полезный параметр — -u . Благодаря ему оболочка проверяет инициализацию переменных в скрипте. Если переменной не будет, скрипт немедленно завершиться. Данный параметр достаточно умен, чтобы нормально работать с переменной по-умолчанию $ и условными операторами ( if , while , и др).

#!/bin/bash echo "$MY_VAR>" echo "RUNNING" # output # ------ # # RUNNING 

С использованием -u :

#!/bin/bash set -u echo "$MY_VAR>" echo "RUNNING" # output # ------ # line 4: MY_VAR: unbound variable 

set -x

Параметр -x очень полезен при отладке. С помощью него bash печатает в стандартный вывод все команды перед их исполнением. Стоит учитывать, что все переменные будут уже доставлены, и с этим нужно быть аккуратнее, к примеру если используете пароли.

#!/bin/bash MY_VAR="a" echo "$MY_VAR>" echo "RUNNING" # output # ------ # a # RUNNING 

С использованием -x :

#!/bin/bash set -x echo "$MY_VAR>" echo "RUNNING" # output # ------ # + MY_VAR=a # + echo a # a # + echo RUNNING # RUNNING 

Вывод

Не стоит забывать, что все эти параметры можно объединять и комбинировать между собой! Думаю, при работе с bash будет хорошим тоном начинать каждый сценарий с set -euxo pipefail .

Bash set e что это

Необязательные параметры — это дополнительные ключи (опции), которые оказывают влияние на поведение сценария и/или командной оболочки.

Команда set позволяет задавать дополнительные опции прямо внутри сценария. В том месте сценария, где необходимо, чтобы та или иная опция вступила в силу, вставьте такую конструкцию set -o option-name, или в более короткой форме — set -option-abbrev. Эти две формы записи совершенно идентичны по своему действию.

#!/bin/bash set -o verbose # Вывод команд перед их исполнением.
#!/bin/bash set -v # Имеет тот же эффект, что и выше.

Для того, чтобы отключить действие той или иной опции, следует вставить конструкцию set +o option-name, или set +option-abbrev.

#!/bin/bash set -o verbose # Вывод команд перед их исполнением. command . command set +o verbose # Запретить вывод команд перед их исполнением. command # команда не выводится. set -v # Вывод команд перед их исполнением. command . command set +v # Запретить вывод команд перед их исполнением. command exit 0

Как вариант установки опций, можно предложить указывать их в заголовке сценария (в строке sha-bang) — #!.

#!/bin/bash -x # # Далее следует текст сценария.

Так же можно указывать дополнительные ключи в командной строке, при запуске сценария. Некоторые из опций работают только если они заданы из командной строки, например -i — ключ интерактивного режима работы скрипта.

bash -v script-name

bash -o verbose script-name

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

Таблица 30-1. Ключи Bash

Назад К началу Вперед
Отладка сценариев Наверх Широко распространенные ошибки

unixforum.org

патченный БАШ / bash — стал вести себя странно с оператором «set -e»
точнее, стал выбрасывать из скрипта по непонятным причинам, не смотря на то, что оператор, на котором происходил выход из скрипта — отрабатывал без ошибок

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

#!/bin/sh clear; set -e HOST=xxx.xxx.xxx.xxx PTR=~/tmp/mnt_$ ( mkdir -p $ sudo umount -l $ > /dev/null ) sshfs root@$:/ $ -p XXXXX -o allow_other # . другие команды $ mount | grep $

сначал а установил самый последний БАШ
bash-4.3.0 patch to 4.3.30

и заметил, что перестали нормально работать slackBuild-скрипты, которые раньше нормально отрабатывали

подумал, что навый БАШ непереваривает старые оси, ладно, установил заплаты на «свой родной баш»
bash-3.2.48 patch to 3.2.57

и та же самая история, slackBuild-скрипты перестали отрабатывать. доходят до команды make — и вываливаются, несмотря на то, что сама команда отрабатывает без ошибок

в конечном итоге перестали работать около 100 пользовательских баш-скриптов
откатился на старый-дырявый баш

на десктопе такой откат — не существеннен, а вот на сервере.

что то случилось в мире опенсорса пока я отсутствовал? :о)
какие будут комментарии?

slackware 12.2
kernel 2.6.32.63-smp
kde-tde 3.5.12

bash-3.2.48
with patches: bash32-049 — bash32-057

bash-4.3
with patches: bash43-001 — bash43-030

основная проблема решена, остался один вопрос

>> видимо у меня наследование «set -e» в «Bashe» отключено
>> AFAIK зависит от дистрибутива.
как это «регулируется», ни кто не подскажет?

если я ошибаюсь, то поправьте а не критикуйте :о)
Спасибо сказали:
Bizdelnick Модератор Сообщения: 20642 Статус: nulla salus bello ОС: Debian GNU/Linux

Re: патченный bash & set -e, странное поведение

Сообщение Bizdelnick » 11.11.2014 10:21

11.11.2014 04:46

Во-первых, ! пропущен, во-вторых, если скрипт завязан на типичное только для bash поведение, то пишите

#!/bin/bash

Вы на 100% уверены, что /bin/sh у Вас — это bash?

P. S. Попозже поисследую поведение bash’а у себя.

Пишите правильно:

в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик

Спасибо сказали:
SLEDopit Модератор Сообщения: 4823 Статус: фанат консоли (= ОС: GNU/Debian, RHEL

Re: патченный bash & set -e, странное поведение

Сообщение SLEDopit » 11.11.2014 10:50

11.11.2014 04:46
а сейчас вываливается из скрипта после команды монтирования

а вы не пробовали эту команду отдельно запустить и посмотреть exit code?
ну или отключить set -e и выводить exit code в скриптах?

UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity. © Dennis Ritchie
The more you believe you don’t do mistakes, the more bugs are in your code.

Спасибо сказали:
Bizdelnick Модератор Сообщения: 20642 Статус: nulla salus bello ОС: Debian GNU/Linux

Re: патченный bash & set -e, странное поведение

Сообщение Bizdelnick » 11.11.2014 12:13

Посмотрел на скрипт более внимательно.
11.11.2014 04:46

mkdir -p $ sudo umount -l $ > /dev/null

Если в каталог $ ничего не смонтировано (в том числе если он только что создан), то umount завершится с ненулевым статусом, и в случае set -e завершится и сам скрипт. Чтобы работало, как Вы хотели, надо делать как-то так:

sudo umount -l $ 2> /dev/null || true

Пишите правильно:

в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик

Спасибо сказали:
Bizdelnick Модератор Сообщения: 20642 Статус: nulla salus bello ОС: Debian GNU/Linux

Re: патченный bash & set -e, странное поведение

Сообщение Bizdelnick » 11.11.2014 12:59

Что могло измениться в bash — это наследование сабшеллом опции -e. Если Ваш скрипт раньше работал, значит, она не наследовалась (или /bin/sh был не bash, а другим шеллом, в котором в принципе нет такой опции). Теперь наследуется (у меня тоже).

GNU bash, version 4.2.37(1)-release (x86_64-pc-linux-gnu)
bash 4.2+dfsg-0.1+deb7u3

Пишите правильно:

в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик

Спасибо сказали:
sunjob Сообщения: 319 Контактная информация:

Re: патченный bash & set -e, странное поведение

Сообщение sunjob » 13.11.2014 07:09

1.
#!/bin/sh — правильно написано, конечно же была опечатка

2.
on slackware sh —> bash

эти строки взяты в скобки, раньше я думал (а собственно так и работало) что скобки
— сохраняют текущий каталог, при выходе из скобок
— не выбрасывают из скрипта, если внутри скобок произошла «ошибка»

и все время, пока я «писал скрипты» — «это правило» так и работала (да, собственно оно и сейчас так же работает, т.е. ошибка «umount» — не приводит к выходу из скрипта, скрипт далее работает, монтирует удаленную папку итд.

ошибку и выход вызывала далее идущая команда (я ее не показал в начале)

которая не была взята в скобки (далее вооще темная история)

дело в том, что вместе с башем был обнавлен и пакет
fuse-2.8.5
—>
fuse-2.9.3

все идущие следом за вер. 2.8.5 вт.ч. и последняя ver. fuse-2.9.3 — не показывавет точку
монтирования, точнее вывод точки монтирования в команде «mount» (если удаленный каталог был смонтирован с пом. «sshfs»)

ни чего не показывала, т.е. на этом мы и вываливались из скрипта

как только не крутил ключи конфигуратора, пересобирал, все равно не вернул «нормальное поведение» для «fuse» — точка монтирования не показывалась командой «mount»

ладушки, обновил, по рекомендациям сообщений конфигуратора «fuse»
util-linux-ng-2.14.1
—>
util-linux-2.19-i486

пересобрал все зависимости — итог, все равно fuse — не кажет точку монтирования

в итоге откатился до работающих версий

далее, как выяснилось большинство скриптов перестали работать из-за такого поведения команды «fuse-mount» (т.е. данная проблема решилась)

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

появился новый вопрос:
КАК ВЕРНУТЬ ПРАВИЛЬНОЕ ПОВЕДЕНИЕ FUSE-MOUNT (т.е. что бы команда «mount» — нормально показывала смонтированный «sshfs» удаленный каталог)

спасибо за участие

если я ошибаюсь, то поправьте а не критикуйте :о)
Спасибо сказали:
Bizdelnick Модератор Сообщения: 20642 Статус: nulla salus bello ОС: Debian GNU/Linux

Re: патченный bash & set -e, странное поведение

Сообщение Bizdelnick » 13.11.2014 10:19

13.11.2014 07:09

эти строки взяты в скобки, раньше я думал (а собственно так и работало) что скобки
— сохраняют текущий каталог, при выходе из скобок
— не выбрасывают из скрипта, если внутри скобок произошла «ошибка»

Всё, что делают скобки, — это запускают то, что в них заключено, в отдельном сабшелле. Что там происходит с наследованием опции -e, из документации мне не понятно, но, как я писал выше, у меня на данный момент она наследуется.
Да, если нужно просто сохранить текущий каталог, более эффективно использовать pushd/popd.

13.11.2014 07:09

все идущие следом за вер. 2.8.5 вт.ч. и последняя ver. fuse-2.9.3 — не показывавет точку
монтирования, точнее вывод точки монтирования в команде «mount» (если удаленный каталог был смонтирован с пом. «sshfs»)

ни чего не показывала, т.е. на этом мы и вываливались из скрипта

А покажите-ка ls -l /etc/mtab .
Пишите правильно:

в консоли
вку́пе (с чем-либо)
в общем
вообще
в течение (часа)
новичок
нюанс
по умолчанию
приемлемо
проблема
пробовать
трафик

Спасибо сказали:
sunjob Сообщения: 319 Контактная информация:

Re: патченный bash & set -e, странное поведение

Сообщение sunjob » 13.11.2014 15:45

$ ls -l /etc/mtab
-rw-r—r— 1 root root 505 2014-11-13 19:41 /etc/mtab

$ cat /etc/mtab
/dev/sda1 / reiserfs rw,noatime,data=writeback 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
/dev/sda6 /home reiserfs rw,noatime,data=writeback 0 0
/dev/sda2 /mnt/hdd2 reiserfs rw,noatime,data=writeback 0 0
tmpfs /dev/shm tmpfs rw,noatime,size=8192m,mode=1777 0 0
tmpfs /tmp tmpfs rw,nosuid,nodev,noatime,mode=1777 0 0
tmpfs /var/lock tmpfs rw,noexec,nosuid,nodev,noatime,mode=1777,size=10m 0 0
root@192.168.0.251:/ /home/sun/tmp/mnt_192.168.0.251 fuse.sshfs rw,nosuid,nodev,allow_other,user=sun 0 0

это вывод с нормально работающим sshfs
т..е не понятно что должно было быть или наоборот.
поясните ваши подозрения или мысли

p.s.
долго вспоминал, что где то проскакивала нечто связанное с «mtab»
проблема решена, короче, надо было
— установить новый «util-linux-2.19»
— пересобрать «fuse-2.8.5» с опцией разрешающей работу с «mtab»
— пересобрать «sshfs-fuse-2.5»

так что проблема с поведением «fuse» решена

осталось некоторое непонятное поведение баша с неск. скриптами
разбираюсь.

Всё, что делают скобки, — это запускают то, что в них заключено, в отдельном сабшелле. Что там происходит с наследованием опции -e, из документации мне не понятно, но, как я писал выше, у меня на данный момент она наследуется.
видимо у меня наследование отключено

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *