?

Log in

No account? Create an account
nyaload

Журнал Пушыстого

Журнал Пушыстого

Previous Entry Share Flag Next Entry
потому что в кузнице не было гвоздя
nyaload
_winnie
Не было гвоздя — подкова пропала.
Не было подковы — лошадь захромала.
Лошадь захромала — командир убит.
Конница разбита — армия бежит.
Враг ступает в город, пленных не щадя,
От того, что в кузнице не было гвоздя


В свои bash-скрипты я вставляю
#!/usr/bin/env bash
set -euo pipefail



Опция -e останавливает скрипт если процесс вернул не 0 (и пишет в stderr на какой строке ошибка).

Это предотвращает беду, если в списке команд одна из фейлится:
svn up
build
copy some files
delete secret files
deploy build to external server

Опция -u останавливает скрипт, если используется неопределённая переменная. Это предотвращает беду например в таких случаях:

tar -czf download.tarball.tar.gz "$PROJECT_DIR/bin"

Если почему-то PROJECT_DIR не определена, то пакуется и отправляется пользователям системная /bin, вместо скомпилированых файлов проекта. И есть менеее забавные фейлы, превращение rm -rf "$1/$2" в rm -rf "/" со стиранием всего.

В комбинации с предыдущей опцией - опечатки в переменных окружения перестают быть непредсказуемым каскадно-гвоздевым фейерверком.
опция -o pipefail фейлит выполнение пайпа, если один из подкомпонентов выполняется с ошибкой. Например,
cat файл_который_не_существует | iconv -f cp1251 -t UTF-8 > результирующий файл.


Ожидаемые ошибки я игнорирую явно.
Если мне похрен на результат команды, вставляю || true после неё
cmd || true #'||' запускает вторую команду, если первая вернула не ноль. '||' можно читать "а иначе".

Если я удаляю папку, которая может не существовать, я явно проверяю что она есть перед удалением:
test -d dir_to_delete && rm -r dir_to_delete.

grep с пустым выводом возвращает код 1, и код ошибки 2 если есть реальная ошибка. Игнорирую коды меньше 2 явно:
cmd1 | (grep c || test $? -lt 2) | cmd2. # $? - код возврата,  test A -lt B - сравнение


Я не знаю, как удобным образом проверить ошибки в cmd2 в таком коде:
cmd1 $(cmd2)
. Подскажите?

Я не эксперт по "портабельному sh", поэтому если используете #!/bin/sh который ссылка на ksh/dash/bash/некий лже-POSIX, то надо смотреть в манах/гуглах какие есть опции.

В bat-файлах программировать надёжно сложно, и я не хочу внимательно вникать в cmd.exe, боюсь за свою психику.
Тем не менее, если я загоняю в bat-файл простой список команд, я в конце каждой команды ставлю || goto error или || exit /b 1 (или || pause если скрипт интерактивный, запускается всегда мышкой).

build || pause
copy some files || pause
delete secret files || pause
deploy build to external server || pause


Данная техника позволила в скрипте апдейта арта для дизайнеров найти тупые и хитрые ошибки в первые два месяца проекта (иначе бы мы с ними жили два года).

Ничего сложного на bat-файлах я стараюсь не писать, они в пять коварней чем C++, bash, assembler и perl вместе взятые.

Если не убеждаться в правильности работы каждой команды из цепочки,










  • 1
> удаляю папку, которая может не существовать я явно проверяю что она есть перед удалением:
> test -d dir_to_delete && rm -r dir_to_delete.

rm -rf завершается удачей при отсутствии файла.

> Я не знаю, как удобным образом проверить ошибки в cmd2 в таком коде:
> cmd1 $(cmd2)

Не знаю, считается ли это за "удобный образ":

VAR=$(cmd2) || { echo ERROR ; exit 1 ; }
cmd2 $VAR

>rm -rf завершается удачей при отсутствии файла.
Ага. Ну, там ещё тонкости, что rm -rf завершается удачей если файл есть, но был например заблокирован, или пользователь опечатался в его имени, или нет прав для удаления, или, или, или, ...
И при этом ситуакция с оставшимся файлом в сборке почему-то является недопустимой.
Вместо rm так же может стоять что-нибудь ещё.

>Не знаю, считается ли это за "удобный образ":
>VAR=$(cmd2) || { echo ERROR ; exit 1 ; }
>cmd2 $VAR

Не, не считается :) Хочется использовать доступные инструкции. А не страдать что они есть, но их нельзя использовать из-за обработки ошибок. Которая при этом раздувает скрипт в несколько раз. Настолько, что за обработкой ошибки не видна настоящая опечатка - использование cmd2 вмето cmd1 при копипасте :)

VAR=$(cmd2) || { echo ERROR ; exit 1 ; }
cmd2 $VAR

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

Edited at 2010-12-19 12:20 pm (UTC)

> rm -rf завершается удачей если файл есть, но был например заблокирован
Это - portability issue, "заблокированы" файлы бывают только в одной
малопопулярной ОС. Думаю, исправят - если зафайлить соответствующий баг в
msys или откуда там rm на винде взялся.

> или пользователь опечатался в его имени,
Что, собственно, и есть "отсутствие файла".
Не может же rm знать какой файл пользователь имел в виду?
(если б знала, то тогда зачем ей вообще аргументы :-)

> или нет прав для удаления
Неправда ваша.
При отсутвии прав rm даже с -f жалуется как положено и выставляет $? в единичку.
$ rm -f / ; echo $?
rm: cannot remove `/': Is a directory
1

> Хочется использовать доступные инструкции.
Собственно, command expansion используется.
Только что таким образом, который не перетирает только что $? второй командой.
Это цена компактного кода.
Ровно как в каком-нибудь ruby/python: если пишем в одну строчечку
someObject.reverse.filter.reverse.map.map.invert.discuss.collapse.delete() - то
разобраться кто из них на самом деле выкинул exception будет нелегко.

> А не страдать что они есть, но их нельзя использовать из-за обработки ошибок.
> Которая при этом раздувает скрипт в несколько раз.
Вам шашечки или ехать? В смысле, ошибки обрабатывать или скрипт маленьким держать?
Обработка ошибок всегда увеличивает размер программы.
"В несколько раз" - это только на синтетических примерах, когда
программа занимает одну строку, а с обработкой - две.
Или у вас в кажой строке command expansion?

> Настолько, что за обработкой ошибки не видна настоящая опечатка -
> использование cmd2 вмето cmd1 при копипасте
Вот если бы там были не придуманные cmd1 и cmd2, а реальные ls и rm -
опечатку было бы видно. А так - какая разница? "Если замысел художника ясен, то
зачем доводить картину до конца?".

> но не в обычных ежедневных скриптах.
Ой, в обычных ежедневных обработки ошибок нету вообще.
Потом обычный ежедневный становится кронжобом, потом mission-critical,
потом всё бумкается - и вот потом там появляется обработка вот именно
этой ошибки.

Правильный пример про недостаточные права для удаления:
$ rm -f /bin/sh ; echo $?
rm: cannot remove `/bin/sh': Permission denied
1

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

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

Мне иногда проще записать
rm -rf $dst; chflags -R 0 $dst ; rm -rf $dst

первая команда может пройти с ошибкой.

  • 1