Пользователь
0,0
рейтинг
29 апреля 2014 в 13:44

Разработка → Интересные приемы программирования на Bash из песочницы tutorial

Эти приемы были описаны во внутреннем проекте компании Google «Testing on the Toilet» (Тестируем в туалете — распространение листовок в туалетах, что бы напоминать разработчикам о тестах).
В данной статье они были пересмотрены и дополнены.


Безопасность

Я начинаю каждый скрипт со следующих строк
#!/bin/bash
set -o nounset
set -o errexit

Это защищает от двух частых ошибок
1) Попыток использовать не объявленные переменные
2) Игнорирование аварийного завершения команд
Если команда может завершиться аварийно, и нас это устраивает, можно использовать следующий код:
if ! <possible failing command> ; then
    echo "failure ignored"
fi

Нужно помнить что некоторые команды не возвращают код аварийного завершения например “mkdir -p” и “rm -f”.
Так же есть сложности с вызовом цепочек подпрограмм (command1 | command 2 | command3) из скрипта, для обхода этого ограничения можно использовать следующую конструкцию:
(./failing_command && echo A)

В этом случае оператор '&&' не позволит выполниться следующей команде, подробнее — 'http://fvue.nl/wiki/Bash:_Error_handling'

Функции

Bash позволяет использовать функции как обычные команды, это очень сильно повышает читаемость вашего кода:
Пример 1:
ExtractBashComments() {
    egrep "^#"
} 
cat myscript.sh | ExtractBashComments | wc 

comments=$(ExtractBashComments < myscript.sh)

Пример 2:
SumLines() {  # iterating over stdin - similar to awk       
    local sum=0
    local line=””
    while read line ; do
        sum=$((${sum} + ${line}))
    done
    echo ${sum}
} 
SumLines < data_one_number_per_line.txt 

Пример 3:
log() {  # classic logger 
   local prefix="[$(date +%Y/%m/%d\ %H:%M:%S)]: "
   echo "${prefix} $@" >&2
} 
log "INFO" "a message"

Попробуйте перенести весь ваш код в функции оставив только глобальные переменные/константы и вызов функции «main» в которой будет вся высокоуровневая логика.

Объявление переменных

Bash позволяет объявлять переменные нескольких типов, самые важные:
local (Для переменных используемых только внутри функций)
readonly (Переменные попытка переназначения которых вызывает ошибку)
## Если DEFAULT_VAL уже объявлена, то использовать ее значение, иначе использовать '-7'
readonly DEFAULT_VAL=${DEFAULT_VAL:-7} 

myfunc() {
   # Использование локальной переменной со значением глобальной
   local some_var=${DEFAULT_VAL}
   ...
}

Есть возможность сделать переменную типа readonly из уже объявленной:
x=5
x=6
readonly x
x=7   # failure

Стремитесь к тому что бы все ваши переменные были либо local, либо readonly, это улучшит читаемость и снизит количество ошибок.

Используйте $() вместо обратных кавычек ``

Обратные кавычки плохо читаются и в некоторых шрифтах легко могут быть перепутаны с одинарными кавычками.
Конструкция $() так же позволяет использовать вложенные вызовы без головной боли с экранированием:
# обе команды выводят: A-B-C-D
echo "A-`echo B-\`echo C-\\\`echo D\\\`\``"
echo "A-$(echo B-$(echo C-$(echo D)))"


Используйте двойные квадратные скобки [[]] вместо одинарных []

Двойные квадратные скобки позволяют избежать непреднамеренного использования путей вместо переменных:
 $ [ a < b ]
 -bash: b: No such file or directory
 $ [[ a < b ]]

В некоторых случаях упрощают синтаксис:
[ "${name}" \> "a" -o ${name} \< "m" ]

[[ "${name}" > "a" && "${name}" < "m"  ]]

А так же предоставляют дополнительную функциональность:

Новые операторы:
  • || Логическое ИЛИ (logical or) — только с двойными скобками.
  • && Логическое И (logical and) — только с двойными скобками.
  • < Сравнение строковых переменных (string comparison) — с двойными скобками экранирование не нужно.
  • == Сравнение строковых переменных с подстановкой (string matching with globbing) — только с двойными скобками.
  • =~ Сравнение строковых переменных используя регулярные выражения (string matching with regular expressions) — только с двойными скобками.


Дополненные/измененные операторы:
  • -lt Цифровое сравнение (numerical comparison)
  • -n Строковая переменная не пуста (string is non-empty)
  • -z Строковая переменная пуста (string is empty)
  • -eq Цифровое равенство (numerical equality)
  • -ne Цифровое не равенство (numerical inequality)


Примеры:
t="abc123"
[[ "$t" == abc* ]]         # true (globbing)
[[ "$t" == "abc*" ]]       # false (literal matching)
[[ "$t" =~ [abc]+[123]+ ]] # true (regular expression)
[[ "$t" =~ "abc*" ]]       # false (literal matching)

Начиная с версии bash 3.2 регулярные выражения или выражения с подстановкой не должны заключаться в кавычки, если ваше выражение содержит пробелы, вы можете поместить его в пеерменную:
r="a b+"
[[ "a bbb" =~ $r ]]        # true

Сравнене строковых переменных с подстановкой так же доступно в операторе case:
case $t in
abc*)  <action> ;;
esac


Работа со строковыми переменными:

В bash встроено несколько (недооцененных) возможностей работы со строковыми переменными:
Базовые:
f="path1/path2/file.ext"  
len="${#f}" # = 20 (длина строковой переменной) 
# выделение участка из переменной: ${<переменная>:<начало_участка>} или ${<переменная>:<начало_участка>:<размер_участка>}
slice1="${f:6}" # = "path2/file.ext"
slice2="${f:6:5}" # = "path2"
slice3="${f: -8}" # = "file.ext" (обратите внимание на пробел перед знаком '-')
pos=6
len=5
slice4="${f:${pos}:${len}}" # = "path2"

Замена с подстановкой:
f="path1/path2/file.ext"  
single_subst="${f/path?/x}"   # = "x/path2/file.ext" (змена первого совпадения)
global_subst="${f//path?/x}"  # = "x/x/file.ext" (замена всех совпадений)

Разделение переменных:
f="path1/path2/file.ext" 
readonly DIR_SEP="/"
array=(${f//${DIR_SEP}/ })
second_dir="${array[1]}"     # = path2

Удаление с подстановкой:
# Удаление с начала строки, до первого совпадения
f="path1/path2/file.ext" 
extension="${f#*.}"  # = "ext" 

# Удаление с начала строки, до последнего совпадения
f="path1/path2/file.ext" 
filename="${f##*/}"  # = "file.ext" 

# Удаление с конца строки, до первого совпадения
f="path1/path2/file.ext" 
dirname="${f%/*}"    # = "path1/path2" 

# Удаление с конца строки, до последнего совпадения
f="path1/path2/file.ext" 
root="${f%%/*}"      # = "path1"


Избавляемся от временных файлов

Некоторые команды ожидают на вход имя файла, с ними нам поможет оператор '<()', он принимает на вход команду и преобразует в нечто что можно использовать как имя файла:
# скачать два URLa и передать их в diff
diff <(wget -O - url1) <(wget -O - url2)

Использование маркера для передачи многострочных переменных:
# MARKER — любое слово.
command  << MARKER
...
${var}
$(cmd)
...
MARKER

Если нужно избежать подстановки, то маркер можно взять в кавычки:
# конструкция вернет '$var' а не значение переменной
var="text"
cat << 'MARKER'
...
$var
...
MARKER


Встроенные переменные

  • $0 Имя скрипта (name of the script)
  • $1 $2… $n Параметры переданные скрипту/фнукции (positional parameters to script/function)
  • $$ PID скрипта (PID of the script)
  • $! PID последней команды выполненной в фоне(PID of the last command executed (and run in the background))
  • $? Статус возвращенный последней командой (exit status of the last command (${PIPESTATUS} for pipelined commands))
  • $# Количество параметров переданных скрипту/функции (number of parameters to script/function)
  • $@ Все параметры переданные скрипту/функции, представленные в виде слов (sees arguments as separate word)
  • $* Все параметры переданные скрипту/функции, представленные в виде одного слова (sees arguments as single word)
  • Как правило:
  • $* Редко является полезной
  • $@ Корректно обрабатывает пустые параметры и параметры с пробелами
  • $@ При использовании обычно заключается в двойные кавычки — "$@"

Пример:
for i in "$@"; do echo '$@ param:' $i; done
for i in "$*"; do echo '$! param:' $i; done

вывод:
bash ./parameters.sh arg1 arg2
$@ param: arg1
$@ param: arg2
$! param: arg1 arg2


Отладка

Проверка синтаксиса (экономит время если скрипт выполняется дольше 15 секунд):
bash -n myscript.sh

Трассировка:
bash -v myscripts.sh

Трассировка с раскрытием сложных команд:
bash -x myscript.sh

Параметры -v и -x можно задать в коде, это может быть полезно если ваш скрипт работает на одной машине а журналирование ведется на другой:
set -o verbose
set -o xtrace

Признаки того, что вы не должны использовать shell скрипты:

  • Ваш скрипт содержит более нескольких сотен строк.
  • Вам нужны структуры данных сложнее обычных массивов.
  • Вас задолбало заниматься непотребствами с кавычками и экранированием.
  • Вам необходимо обрабатывать/изменять много строковых переменных.
  • У вас нет необходимости вызывать сторонние програмы и нет необходимости в пайпах.
  • Для вас важна скорость/производительность.

Если ваш проект соответствует пунктам из этого списка, рассмотрите для него языки языки Python или Ruby.
Ссылки:
Advanced Bash-Scripting Guide: tldp.org/LDP/abs/html
Bash Reference Manual: www.gnu.org/software/bash/manual/bashref.html
Оригинал статьи: robertmuth.blogspot.ru/2012/08/better-bash-scripting-in-15-minutes.html
Андрей @ayurtaykin
карма
29,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (67)

  • +7
    Кстати, [ это утилита из coreutils ( см. which [ и man test), а [[ это конструкция самого интерпретатора bash.

    $ [ a < b ]
    равносильно
    $ test a < b
    отсюда и ошибка b: No such file or directory
    • +7
      Оказался неправ. Мне на почту написали:
      Не совсем так, а чаще всего не так:
      ubuntu@stage:~$ type [
      [ is a shell builtin
      ubuntu@stage:~$ type [[
      [[ is a shell keyword
      ubuntu@stage:~$ type test
      test is a shell builtin
      ubuntu@stage:~$ which test
      /usr/bin/test
      ubuntu@stage:~$ which [
      /usr/bin/[
      

      Чаще всего при вызове [ ] вызывается shell builtin
      • +2
        Ключевое отличие: shell builtin (который от внешней команды отличается разве что скоростью) vs. shell keyword (который значительно меняет синтаксис и семантику).
  • +3
    set -o errexit не работает внутри функций, поэтому по-осторожнее с заворачиванием всего и вся в функции при использовании этого.

    Из перечисленного очень много башизмов, которые будут работать только с #!/bin/bash в шебанге, но не будут с #!/bin/sh.

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

    В остальном интересно, есть кое-что новое. Спасибо!
    • +1
      Ну и set -o nounset и set -o errexit не работает на некоторых юниксах. Вместо этого можно использовать set -u и set -e.
  • +1
    Отличный сборник, не мешало бы ещё добавить массивы: www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_10_02.html
    • +1
      Упс, не заметил, что перевод. Тем не менее комментарий выше актуален ;)
  • 0
    Люблю баш, спасибо.
  • +9
    Учитывая как много людей думают что bash == sh и оный есть на любой системе, статью неплохо было бы начать с ремарки о том что большая часть указанных возможностей являются bash-специфичными расширениями над POSIX shell, в народе называется башизмами и избегается в кросс-платформенных скриптах. Самые часто встречающиеся башизмы — собственно #!/bin/bash вместо #!/bin/sh и, пожалуй, [[ foo == bar ]] вместо [ foo = bar ].

    Что же до собственно статьи, в первой секции обязательно нужно добавить про set -o pipefail и про особенности генерации статусов завершения конвееров. Если в двух словах

    $ (false | true) && echo true || echo false
    true
    $ set -o pipefail
    $ (false | true) && echo true || echo false
    false
    
    • 0
      плюсую. и bash-only скрипты лучше именовать .bash, а не .sh
      • +8
        ИМХО все скрипты лучше именовать name (без разширения). В этом случае вы можете переписать скрипт с баша на перл, а перла на руби, а с руби на питон и никому ничего не говорить (оставив совместимость входных параметров и формата вывода), а пользователи ничего не заметят.
        • +2
          А в случае с расширением вы этого как будто сделать не сможете ;)
          • +4
            сможет, но тогда имя файла будет «врать» о своём содержимом.
            • 0
              Случай вообще довольно вычурный. Кстати не сказано что bash-евские скрипты удобно разбивать на отдельные файлы и подключать друг к другу — bash это позволяет. На моей практике они как внешний исполняемый файл и не вызываются друг из друга поэтому и переписывать на другом языке врятли получится.
              • 0
                что это вдруг «не вызываются»?

                Пример
                borz@debian:~/t$ ls -lFa
                -rw-r--r-- 1 borz borz 73 Apr 29 15:58 ext.bash
                -rw-r--r-- 1 borz borz 69 Apr 29 15:58 test.bash

                borz@debian:~/t$ cat ext.bash
                test_fn(){
                echo «run test_fn function!»
                }

                echo «run exclude bash file»

                borz@debian:~/t$ cat test.bash
                #!/bin/bash

                source ./ext.bash

                echo «run main script file»

                test_fn

                borz@debian:~/t$ bash test.bash
                run exclude bash file
                run main script file
                run test_fn function!

                borz@debian:~/t$ chmod +x test.bash

                borz@debian:~/t$ ./test.bash
                run exclude bash file
                run main script file
                run test_fn function!
                • 0
                  Я сказал не это. Вызвать можно что угодно, но удобнее подключать и использовать, скажем, как библиотеки функций. Вызвать как сторонний скрипт можно, но при этом теряются все преимущества. Конечно бывают и исключения когда это действительно надо.
                  • 0
                    эм… а разве в примере скрипт ext.bash не выполняет роли коллектора функций? в частности, содержит в себе функцию test_fn, которую использует скрипт test.bash
                    • 0
                      Я что-то запутался. Есть вызов типа `` он же $(), есть подключение. Я изначально сказал что второе удобнее. В чём заключается ваше мнение?
                      • 0
                        покажите ваш пример с «подключением», потому как в моём примере я подключаю скрипт ext.bash к скрипту test.bash
                        если же дёргать скрипт через $(), то это не подключение, а выполнение и не важно что там внутри указано — скрипт или бинарник.
                        • 0
                          Я вижу что вы именно подключаете, я это и имел ввиду под подключением. Вы неверно поняли мой первый комментарий мне кажется. Кстати у «source» есть синоним — точка. Я его всегда и использовал.
                          • 0
                            после точки можно пробел забыть поставить.

                            на тему «неверно поняли», то я отвечал на «На моей практике они как внешний исполняемый файл и не вызываются друг из друга поэтому и переписывать на другом языке врятли получится».
                            • 0
                              Всё верно. Они и у вас подключаются как макрос а не вызываются как внешний исполняемый файл. Вы можете подключить и вызывать функции из скрипта на языке, отличного от bash?
                              • 0
                                зачем «мне», при выполнении некого файла знать что у него в потрохах? я должен знать какие параметры подать ему на вход и что он выдаст мне на выходе.
                                именно про это и говорилось в комментарии.
                                • 0
                                  ОК, значит мы обсуждали разные вещи =)
                          • +5
                            source, кстати, тоже башизм. Точка везде работает.
                            • 0
                              И это кстати по сути выполнение содержимого указанного файла в контексте текущего экземпляра оболочки, а не включение файла.
                              • 0
                                Поэтому и написано «как макрос» =)
                                • 0
                                  Это не макрос. Это inline
                                  • 0
                                    Я имел ввиду определение «макрос — символьное имя, заменяемое при обработке на последовательность программных инструкций».
        • 0
          ну, в топике скрипты запускаются как «bash скрипт» — тут переписывание на иной от Bash язык не получится. вот если бы скрипт исполняемый был, тогда согласен — лучше без «расширения».
    • +2
      «Обыкновенный башизм» — очень распространенное явление. Вылавливай потом все эти $() при некорректном объявлении шелла "#!/bin/sh", за который необходимо расстреливать два раза.
      • +3
        $() работает и в обычном шелле, а вот многое другое из перечисленного (<(), [[ $a == $b ]] первое что в голову пришло) — уже нет
        • 0
          верно, виноват
    • 0
      О да, как дошёл до [[ ]] vs [ ] так и думал что комментариях придет кто-то обвинит в башизме.
      Но не ожидал за #!/bin/bash в шебанге.
      Как вам такое поведения истинно posix echo:
      $ echo -n text -n text

      STANDARDS CONFORMANCE
      echo: SVID2, SVID3, XPG2, XPG3, XPG4, POSIX.2

  • +2
    Параметры -v и -x можно задать в коде, это может быть полезно если ваш скрипт работает на одной машине а журналирование ведется на другой:

    Либо явно указать опции -v -x в шебанге.

    Для вас важна скорость/производительность.

    Ну порой вполне приличной скорости можно добиться и с шелл скриптами. Просто не нужно пытаться обрабатывать построчно большие файлы силами шелла. Есть же замечательные sed и awk. Если нужно сделать быстро, то всё это вполне приемлимо.

    ps. По поводу bash'измов, кстати, будет полезно почитать эту вики.
  • 0
    Спасибо! Многого не знал, интересно.
  • +1
    В баше очень много возможностей и хитрых приёмов и эту статью можно дополнять бесконечно. Например я работал над проектом с кросс-платформенными скриптами на bash и bash с этим прекрасно справлялся. Причём одной из платформ была moxa с мегаурезанной версией linux — mulinux, другой мейнфрейм от HP RTB, ещё были сервера на windows, red hat, solaris. Очень было интересно и до сих пор тот проект вызывает приятные воспоминания =)
  • +1
    В плане портабельности обратные кавычки ` и $( не всегда взаимозаменяемы.
    • 0
      Когда например?
      • 0
        Всех подробностей сейчас уже не вспомню (дело пару лет назад было).
        Но заменил я в одном из наших билд-скриптов кавычки на скобочки.
        Под башем — да, ура, работает.

        Через день пошли багрепорты. Где-то то ли на макоси, то ли на солярке, то ли ещё на какой-то экзотике сборка сломалась из-за этой замены.
        • 0
          На макоси работает точно. На солярке не знаю, никогда ее не видел.
      • +2
        Вы не хотите этого знать.



        bash до версии 2.0 и другие столь же древние шеллы не понимают $(. Не придавайте этому значения — если вам придётся работать с UNIX'ами, где есть настолько старый shell, вы об этом так или иначе вскоре узнаете сами. POSIX рекомендует синтаксис $(, бэктик ` провозглашён legacy — так что не мучайтесь понапрасну.

        Ещё csh не умеет $(, но там вообще ни о какой совместимости речи не идёт.
  • 0
    Нужно помнить что некоторые команды не возвращают код аварийного завершения например “mkdir -p” и “rm -f”.

    mkdir -p возвращает ошибку, если директорию ему создать нужно (она не существует), но прав на это не хватает.
  • +1
    Don't forget about trap!
  • +1
    А еще есть отличный вариант перенаправления — <<<, называется «here document»
    Работает следующим образом:
    $ grep "q" <<< "qweqweqwe"
    qweqweqwe
    

    То есть то, что после "<<<" — это как бы файл, то есть все, что после трех знаков «меньше» — это некоторый абстрактный файл без имени. Штука полезная иногда, но мало кто ей пользуется.
    • 0
      А я думал, это специфика zsh. Оказывается, bash просто не работает с таким перенаправлением без указания команды (конструкция SomeCommand <(<<< "some string") является вполне рабочей в zsh, но не в bash).
  • 0
    Насколько я помню `` выполняются внутри терущего процесса а конструкция $() запускает новый процесс шелла. Это может замедлить некоторые скрипты.
    • 0
      Это для группировок (...) vs {...}, а не подстановок.

      Впрочем, я и для них ничего такого не смог воспроизвести.
  • –3
    Ну ладно там выполнить пару команд — ок, bash. Но когда начинаются вот все эти функции, условия, парсинг строк, циклы и т.д. — почему бы просто не написать скрипт на Python, в котором это всё проще, понятнее, работать будет с той же скоростью, куча библиотек, а знания Python пригодятся и в других целях?
    • 0
      Если ваш скрипт активно использует стандартные утилиты типа awk и прочего то что-то удобнее чем bash сложно найти.
    • 0
      Вы даже не представляете как много «библиотек» в bash.
      • 0
        Вполне возможно. А как обстоят дела с отладкой bash-скриптов? Можно их пройти по шагам? Поставить брейкпоинт? По ходу отладки изменить переменную? Увидеть что-то типа колл-стека? А как с многопоточностью? А вообще с компоновкой кода с использованием каких-либо паттернов?

        Не то чтобы я троллил, мне правда интересно, где та граница, которая отделяет целесообразность написания bash-скрипта от использования полноценного скриптового языка?
        • +1
          внезапно: bashdb.sourceforge.net/
          • 0
            спасибо, отличная штука, я смотрю
        • 0
          Цитата из статьи выше:
          Признаки того, что вы не должны использовать shell скрипты:

          Ваш скрипт содержит более нескольких сотен строк.
          Вам нужны структуры данных сложнее обычных массивов.
          Вас задолбало заниматься непотребствами с кавычками и экранированием.
          Вам необходимо обрабатывать/изменять много строковых переменных.
          У вас нет необходимости вызывать сторонние програмы и нет необходимости в пайпах.
          Для вас важна скорость/производительность.
  • 0
    Всем, кого интересуют не очевидные методы работы в bash советую книгу «Unix. Программное окружение». Каждый листинг в ней — произведение искусства.
  • +1
    Вообще, я с каждым новым крупным (несколько сотен строк) скриптом убеждаюсь снова и снова: в программировании на шелле самое главное — читаемость и при этом неперегруженность кода.

    Т.е. все эти readonly, крошечные функции на полторы строки на каждый чих, выделение библиотек в отдельные файлы, абстрактные функции и т.д. — они чаще всего на деле мешают, и при этом не делают код ни надежнее, ни намного понятнее.

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

    Проще говоря, в шелле чаще всего уместно многие вещи прибивать гвоздями на месте, уместно писать лапшу, строить негибкий код. Часто вполне правильно будет покопипастить какие-то небольшие куски кода с минимальными изменениями туда-сюда.
    К нему в большей степени применимы принципы unix way и «чем меньше фич — тем лучше».
    А вот уже от этого отталкиваясь можно вводить и абстракции, и дупликацию уменьшать пытаться, программу структурировать. Самое главное — ничего этого не делать в ущерб простоте. А если нужно сложную и красивую структуру сделать — вон на Python или Ruby лучше написать, они для этого и предназначены.
  • –1
    в программировании на шелле самое главное — читаемость и при этом неперегруженность кода.

    В программировании на шелле самое главное — вовремя остановиться и понять, что лучше шелл уже не использовать
    • 0
      Когда вы уйдете из проекта, кто будет поддерживать ваши скрипты на питоне/руби, которые например скачивают файл и делают на него симлинк из другого места, или например фильтруют по нужному шаблону строки из файла и что-то с ними делают, или даже шлют какой-то HTTP запрос и останавливают после этого некий сервис? Как они поступят, когда нужно будет эти скрипты перенести на новый сервер, или добавить в середину скрипта еще какую-то операцию?
      Даже если нужно все эти операции сделать за один проход, это не всегда повод уходить с шелла — часто нужно бывает запускать эти операции вручную по-отдельности. На шелле мы вместо одного большого скрипта просто сделаем три маленьких и враппер, который их вызывает. В случае других языков чаще всего придется выдумывать какие-то дополнительные ключи, параметры командной строки, добавлять дополнительную логику для их обработки, писать к этому хозяйству доки (как установить, как использовать).
      Как вчерашний студент, который придет после вас, будет с этим всем работать?

      Если какую-то сложную логику использовать, или большие длинные сценарии писать, или в вашей компании нет выделенного админа и их задачи выполняют между делом по мере надобности программисты — тогда можно посмотреть и на более правильные языки, согласен.
      • 0
        Возможно я сужу со своей колокольни, т.к. мне приходилось копаться и править кучу разных скриптов — bash, perl, php, немного python. Возможно, поэтому мне проще выбрать инструмент под задачу, чем использовать везде шелл.
        Как вчерашний студент, который придет после вас, будет с этим всем работать?
        Как вчерашний студент будет разбираться с шелл-скриптами, которые в 80% случаев работают одним способом, а в 20% — по-другому. Подумаешь, там в начале стоял какой-то set чего-то там, это же не может так влиять. Вы же не будете комментировать каждый чих в скрипте? А если будете, то тут вообще пофиг, что это — шелл или не шелл, комментарий же есть :)
        Да и был я на месте такого вот студента, ничего, не сломался :) И не вижу каких-то проблем.
        В случае других языков чаще всего придется выдумывать какие-то дополнительные ключи, параметры командной строки, добавлять дополнительную логику для их обработки, писать к этому хозяйству доки (как установить, как использовать).
        Т.е. шелл-скрипты вы не документируете? Ключи/параметры не используете, все hardcoded? И какая разница вызвать функцию в шелл-скрипте или php?
        в вашей компании нет выделенного админа и их задачи выполняют между делом по мере надобности программисты
        Имхо, если в компании есть выделенные программисты и админ, то какие-то сложные задачи как раз нужно отдавать программистам, т.к. это их хлеб — писать программы.
        • 0
          Как вчерашний студент будет разбираться с шелл-скриптами, которые в 80% случаев работают одним способом, а в 20% — по-другому.

          В каком смысле?

          set я использую только -e, т.к. остальные плохо соответствуют принципу «least surprise».
          Все неясные моменты, естественно нужно комментировать.

          Т.е. шелл-скрипты вы не документируете? Ключи/параметры не используете, все hardcoded? И какая разница вызвать функцию в шелл-скрипте или php?

          Разница в том, что на шелле у нас 3, допустим, маленьких скрипта, которые принимают 1-2 параметра и документируют сами себя через имена параметров (запускаем без параметров и получаем usage), а в случае других скриптовых языков будет один большой скрипт с десятком параметров, которые нужно выдумать, описать, реализовать их обработку.

          Имхо, если в компании есть выделенные программисты и админ, то какие-то сложные задачи как раз нужно отдавать программистам, т.к. это их хлеб — писать программы.

          Если бизнес компании не связан с системным программированием (а чаще всего это так), то задача управления системой, каким бы оно ни было сложным (или простым) — задача специально выделенного на это человека. Если этим занимается кто попало — результат получается грустный.
          • 0
            В каком смысле?
            Это я как раз про неприметные set'ы :)
            задача управления системой, каким бы оно ни было сложным (или простым) — задача специально выделенного на это человека.
            Управление — это одно, а создание инструмента для управления — совершенно другое. Вы же используете grep, а не пишете его сами. Имхо, админ не обязан быть программистом. Если задача довольно сложная, то, опять же имхо, лучше подготовить для программиста адекватное техзадание, чем пытаться залезть туда, где ты не очень силен.
            а в случае других скриптовых языков будет один большой скрипт с десятком параметров, которые нужно выдумать, описать, реализовать их обработку.
            Какая разница, на чем писать эти скрипты — на шелле или на чем-то другом? Вы что, не можете в этих 3 скриптах один написать на шелле, другой на php, третий на перле? Почему обязательно если шелл — то 3 простых скрипта, а если другой язык — одна большая программа?
            И кстати, вы тут говорите про «маленькие скрипты», в какой-то мере подтверждая мое «нужно вовремя остановиться» :)

            Моя первоначальная посылка — каждой задаче свой инструмент. Я же не говорю «не используйте шелл вообще». Я говорю — если у вас получается нагромождение обходных путей, то возможно пора уже посмотреть на другой инструмент.
            • 0
              Управление — это одно, а создание инструмента для управления — совершенно другое. Вы же используете grep, а не пишете его сами. Имхо, админ не обязан быть программистом. Если задача довольно сложная, то, опять же имхо, лучше подготовить для программиста адекватное техзадание, чем пытаться залезть туда, где ты не очень силен.

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

              Какая разница, на чем писать эти скрипты — на шелле или на чем-то другом? Вы что, не можете в этих 3 скриптах один написать на шелле, другой на php, третий на перле?

              Как все это добро потом поддерживать?

              Почему обязательно если шелл — то 3 простых скрипта, а если другой язык — одна большая программа?

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

              И кстати, вы тут говорите про «маленькие скрипты», в какой-то мере подтверждая мое «нужно вовремя остановиться» :)

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


              Я изначальную фразу понял как «шелл лучше не использовать», а не как «вовремя остановиться». Видимо, отсюда и выросло недопонимание.
          • 0
            Разница в том, что на шелле у нас 3, допустим, маленьких скрипта, которые принимают 1-2 параметра и документируют сами себя через имена параметров (запускаем без параметров и получаем usage), а в случае других скриптовых языков будет один большой скрипт с десятком параметров, которые нужно выдумать, описать, реализовать их обработку.
            Я обычно использую --help для usage (если неохота писать полноценную документацию: тогда --help будет и не для usage) и принцип garbage in — garbage out для запуска без параметров (т.е. если нет --help то скрипт может отработать как угодно, если ему требуются параметры (и как надо, если не требуются)).

            Причина: --help более универсален. Если вы запустите большинство программ с --help вам покажут справку. Если нет — то как повезёт: cp --help и dd --help оба кажут справку, тогда как только cp показывает справку без --help. То же самое относится к редакторам, интерпретаторам (кроме стандартных sed и awk), различным GUI программам — все они показывают справку с --help и запускаются без аргументов. Есть и исключения, но их мало: намного меньше, чем программ с --help, но без справки при отсутствии параметров.
            • 0
              Я придерживаюсь описанного мной подхода по одной простой причине: нам в любом случае нужно проверять наличие всех нужных переданных параметров, так почему бы и не показывать сразу справку в этом случае?

              И т.к. скрипты обычно делают что-то относительно сложное и потенциально деструктивное, я настороженно отношусь, чтобы они при запуске вообще без параметров делали что-то вообще. Хотя это от области применения, конечно, зависит.

              Попробую еще добавлять проверку первого параметра на равенство -h или --help, посмотрю насколько это красиво будет. Спасибо за совет!
  • 0
    не туда :(
  • +3
    Если ваш проект соответствует пунктам из этого списка, рассмотрите для него языки языки Python или Ruby.

    Perl?

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