Не бойтесь сценариев Bash. После прочтения этих советов будет легче

[Обновлено 2021–02-18. Коды изменены на Gist и добавлены ссылки]

Table of Contents
Introduction
1. Clean Structure
2. Install ShellCheck on Your Editor
3. Usage Function
4. Error Messages
5. Function Comments
6. How To Concatenate String Variables
7. How To Set a Debug ModeOther useful set options
8. How To Slice Strings
9. How To Make Sure Users Use a Correct Bash Version
10. How To Transform a String Case
11. Ternary Operator-Like Statement
12. How To Find the Length of a String and an Array
13. How To Unset Variables
14. How To Set a Default Value
15. How To Determine Your Bash Script Name
16. How To Make a Variable Constant
17. How To Find OSs
18. How To Determine Which HTTP Get Tool the System Has Installed
19. How To Determine Which Python the System Has Installed
20. Parameter expansion: How to Find the Script Name and Directory
21. How To Make Status Messages
22. How To Set Locale
23. Type Hinting in Bash?
24. Store Exit Status in a Variable
25. Using Trap for Unexpected Termination
26. Don’t Reinvent the Wheel
27. Subshell and Exit Status
Conclusion
Newsletter
References

Вступление

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

1. Чистая структура

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

Сначала объявите все глобальные переменные, а затем объявите все функции после глобальных переменных. Используйте локальные переменные в функциях и пишите основное тело после функций. Используйте явный код статуса выхода в ваших функциях, в операторе if и в конце скрипта.

2. Установите ShellCheck в свой редактор.

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

$ shellcheck my_awesome_script

Или вы можете установить его на VS Code.

Если вас интересует проверка списка кодов ошибок ShellCheck, проверьте эту суть.

3. Функция использования

Если ваш скрипт использует позиционные параметры, добавьте функцию, объясняющую, как и какие пользователи могут использовать эти параметры.

$0 выводит имя сценария. В приведенном выше случае функция usage будет вызываться, когда пользователь использует h или любые буквы, кроме f, d или t.

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

Вы можете использовать Bash Heredoc:

4. Сообщения об ошибках

Руководство по стилю оболочки Google рекомендует функцию для распечатки сообщений вместе с другой информацией о статусе.

В руководстве предлагается, чтобы все сообщения об ошибках отправлялись на STDERR, потому что это упрощает отделение нормального состояния от реальных проблем.

5. Комментарии к функциям

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

6. Как объединить строковые переменные

В сценарии Bash вы можете объединять строки по-разному.

Обратите внимание, что при присвоении значения переменной нет пробелов перед знаком равенства и после него. Первый использует знак += для присоединения второго значения к первому. Во втором используются двойные кавычки с пробелом между переменными. В последнем случае используется фигурная скобка, поскольку за переменной foo сразу следует символ.

7. Как установить режим отладки

Используйте set -x. Устанавливается опция -x или xtrace. Это отображает развернутые команды и переменные данной строки кода перед запуском кода.

Результатом является подробная трассировка выполнения скрипта.

+ foo=Bash
+ '[' Bash == Bash ']'
+ echo Bash
Bash

Легко увидеть, как скрипт запускается и присваивает значения.

Другие полезные параметры набора

set -e немедленно завершает работу, если команда завершается с ненулевым статусом.

set -u при подстановке рассматривает неустановленные переменные как ошибку.

set -C запрещает перезапись существующих обычных файлов.

Таким образом, вы можете использовать set -Ceu в начале вашего скрипта.

Узнайте больше о set использовании help set.

8. Как нарезать струны

Вы можете использовать cut -cstart-end, чтобы разрезать строку.

Первый разрезает строку от первой буквы до третьей. Второй - от второго до четвертого. Последний нарезает пятую букву.

Расширение параметра выполняет извлечение подстроки ${s:off-set-index:length}:

#!/usr/bin/env bash
s="abcdefg"
echo ${s:0:3}
echo ${s:1:3}
echo ${s:4:1}
# output
# abc
# bcd
# e

Смещение отсчитывается от нуля.

9. Как убедиться, что пользователи используют правильную версию Bash

Следующий сценарий гарантирует, что пользователи будут использовать Bash версии 4.0 или выше.

if ((BASH_VERSINFO[0] < 4)); then
    printf '%s\n' "Error: This requires Bash v4.0 or higher. You have version $BASH_VERSION." 1>&2
    exit 2
fi

10. Как преобразовать регистр строки

Когда вы читаете вводимые пользователем данные, вводимые данные могут быть в нижнем или верхнем регистре. Для Bash ниже версии 4 вы можете изменить его на верхний регистр, используя команду tr с [:lower:] и [:upper:].

Обратите внимание, что мы установили параметр -x для отладки.

Строка 4: чтение пользовательского ввода.

Строка 5: каналы, |, позволяют использовать выходные данные одной команды в качестве входных данных другой команды. Мы cut первую букву, затем преобразовываем ее из нижнего регистра в верхний. Вы можете поменять местами строчные и прописные буквы, чтобы преобразовать их с верхнего регистра в нижний.

В Bash 4+ вы можете использовать операторы модификации case.

Используя эти операторы модификации регистра, вы можете создавать функции.

11. Утверждение, подобное тернарному оператору.

В Bash нет тернарного оператора, но вы можете сделать то же самое для простых назначений переменных.

Мы можем написать if оператор:

if [ $foo -ge $bar ]; then
  baz="Smile!"
else
  baz="Sleep!"
fi

Используя вместе && и ||, мы можем создать оператор, аналогичный тернарному оператору:

[ $foo -ge $bar ] && baz="Smile!" || baz="Sleep!"

12. Как найти длину строки и массива

${#string} возвращает длину строки и аналогично ${#array[@]} возвращает длину массива.

13. Как сбросить переменные

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

В начале сценария:

#!/usr/bin/env bash
unset var1 var2 var3
var1=some_value
...

14. Как установить значение по умолчанию

${foo-$DEFAULT} и ${foo=$DEFAULT} оцениваются как $DEFAULT, если foo не установлен.

${foo:-$DEFAULT} и ${foo:=$DEFAULT} оценивается как $DEFAULT, если foo не установлен или пуст.

${foo+$OTHER} и ${foo:+OTHER} оцениваются как $OTHER, если foo установлен, в противном случае - как пустая строка.

15. Как определить имя сценария Bash

Добавив все свои сценарии Bash в каталог ~/bin и добавив его путь к файлу конфигурации вашего терминала, ~/.bashrc или ~/.zshrc, вы можете запускать их из любого каталога. Но перед тем, как вы зададите имя сценария, лучше не использовать имя сценария где-либо еще. Для проверки используйте команду type или which.

$ type my_script
my_script not found
$ which which
which: shell built-in command

Имя сценария с my_script отсутствует, но which уже занято.

16. Как сделать переменную постоянной

readonly делает переменные и функции доступными только для чтения.

Строка 3: установить переменную, доступную только для чтения.
Строка 5: Попытка перезаписать переменную, доступную только для чтения. Но это возвращает ошибку: «строка 5: MAX: переменная только для чтения».

Выход:

10
/Users/shinokada/bin/ex6: line 5: MAX: readonly variable
10

Вы можете вывести readonly переменные:

17. Как найти ОС

В разных ОС есть разные команды. Например, Linux использует readlink, а macOS использует greadlink.

Следующий пример довольно тривиален, но он находит ОС пользователя и применяет правильную команду.

Выход:

Your script path is /Users/shinokada/bin
Your OS is mac

18. Как определить, какой инструмент HTTP Get установлен в системе

Ваш сценарий Bash может использовать инструмент HTTP GET. В разных системах используются разные инструменты. Это может быть curl, wget, http, fetch или что-то еще.

&>/dev/null перенаправляет стандартный поток вывода и стандартный поток ошибок на /dev/null. Это то же самое, что и >/dev/null 2>&1.

command -v отображает путь к исполняемому файлу или определение псевдонима конкретной команды.

19. Как определить, какой Python установлен в системе

Точно так же, если ваш скрипт использует Python, вы можете найти системный Python:

20. Расширение параметров: как найти имя сценария и каталог

Используйте ${parameter##*/}, чтобы получить имя файла, и используйте ${parameter%/*}, чтобы получить путь к каталогу.

На этой странице Tech.io объясняется, как использовать ${parameter%word}, ${parameter%%word}, ${parameter#word}, ${parameter##word}.

Вы также можете использовать basename "$0", чтобы получить имя файла.

Вы можете найти свой каталог сценариев Bash:

#!/usr/bin/env bash
c=$0
echo "${c%/*}"

Выходы:

/Users/shinokada/bin

21. Как сделать статусные сообщения

Вы можете сообщить пользователям, что делает ваш скрипт.

Если вы хотите выводить сообщения о состоянии, вы должны запустить его с имени программы. Используйте $(basename "$0"), чтобы получить имя программы. Сообщение должно быть написано о стандартной ошибке с использованием echo ... 1>&2.

#!/usr/bin/env bash
progname=$(basename "$0")
...
echo "$progname: Running this and that" 1>&2

22. Как установить языковой стандарт

Вы можете найти свою локальную среду:

Не все программисты используют один и тот же языковой стандарт. Программисты из Франции могут использовать fr_CH.UTF-8; компьютерные фанаты из Англии могут использовать en_GB.UTF-8. Чтобы убедиться, что пользователи используют правильный языковой стандарт, вы можете использовать LC_ALL для установки языкового стандарта:

LC_ALL="en_US.UTF-8"

23. Введите подсказку в Bash?

В сценарии Bash нет подсказок типа, но вы можете использовать declare для создания индексированных массивов переменных, ассоциативных массивов или целых чисел. Вы можете найти declare параметры, используя help declare.

Вы можете использовать + вместо , чтобы отключить атрибут.

Мы объявили var1 как целое число в строке 3. Если вы попытаетесь преобразовать его в строку, вы получите ошибку (строка 5). Но вы можете изменить его на другое целое число (строка 7).

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

24. Сохранить статус выхода в переменной

Вместо использования $? в if операторах:

mkdir /tmp/tmp_dir
if [ $? = 0 ]; then
  # do something
fi

Сохраните статус выхода в переменной:

Следующее сохраняет статус выхода для проверки того, существует ли файл и является ли он каталогом.

test -d /tmp/tmp_dir
test_es=$?
if [[ ${test_es} -ne 0 ]]; then  
    echo "No dir found. Exiting script!" >&2
    exit 1
fi

25. Использование ловушки для неожиданного завершения

Пользователи могут завершить ваш скрипт, используя Ctrl-c во время его работы. Если ваш сценарий изменяет каталог или файл, вам необходимо вернуть его в исходное состояние. Команда trap предназначена для этой ситуации.

Когда пользователь использует Ctrl-c, он генерирует сигнал SIGINT. Когда пользователь завершает процесс, он генерирует сигнал SIGTERM. Вы можете перечислить все сигналы, используя:

# Linux, from terminal
$ trap --list
# macOS, from a script
trap --list

Стандартные сигналы можно найти на man7.org.

Команда trap имеет форму trap "do something" signal_to_trap. Если ваш код очистки длинный, вы можете создать функцию.

Давайте перехватим сигнал Ctrl-c:

tempfile=/tmp/myfile
cleanup(){
    rm -f $tempfile
}
trap cleanup SIGINT

Подробнее о команде ловушки.

26. Не изобретайте колесо заново

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

27. Дополнительная оболочка и статус выхода

Можете ли вы найти разницу между следующими кодами?

Первый возвращается:

/Users/myname/bin/ex5: line 34: no_func1: command not found
there is nothing
1

И второй возвращается:

/Users/myname/bin/ex5: line 34: no_func2: command not found
there is nothing

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

Поскольку фигурные скобки не создают подоболочку, exit завершает основной процесс оболочки, поэтому он никогда не достигает точки, в которой он мог бы выполняться echo $?.

Заключение

Это 27 советов по созданию сценариев Bash, которые вы можете использовать в своем следующем проекте сценариев Bash. Многие из них легко использовать, и они делают ваш сценарий Bash более профессиональным.

Удачного кодирования!

Новостная рассылка

Получите полный доступ ко всем статьям на Medium, став участником.

использованная литература