Для получения значения переменной в bash нужно объявить знак доллара ($) и имя переменной. Такой вариант получения переменной удобен в работе со строками, а bash ориентирован именно на это, поэтому в нем используется именно такой подход. Наглядно:
MSG="Error: $FILE not found"
Юзай двойные кавычки
При использовании одинарных кавычек все символы интерпретируются в bash буквально и → никакой замены (например, ссылки переменной его значением) не будет. Поэтому нужно использовать двойные кавычки" ".
Пробелы не нужны
В операции присваивания пробелы вокруг = не допускаются, ибо синтаксически вся конструкция присваивания должна быть одним “словом”.
Полный синтаксис
В строках, где сложно определить, где заканчивается имя переменной, лучше использовать полный синтаксис - фигурные скобки вокруг имени переменной: ${FILE}.
Подстановки/правки для возвращаемых значений переменной
Длина значения переменной
Фигурные скобки используются во многих синтаксических конструкциях, например, решетка (#) перед именем переменной${#FILE} возвращает длину значения (в символах). Наглядно:
FILE=some_fileecho "${#FILE}"# 9
Таким же образом работают и остальные синтаксические конструкции. Можно задать правила подстановки/правки, которые будут влиять на возвращаемое значение, но не на само значение переменной (есть исключение: Изменение значения).
Следующие несколько секций об этих правилах.
Short вариант команды basename или удаление пути/префикса
Как ты знаешь скрипт можно вызывать разными способами, юзая:
./scriptname из current dir
полный путь/home/autistic/scripts/scriptname
вызов скрипта из директорий в PATH считается использованием абсолютного пути
относительный путь.
$0 будет хранить путь, который и использовался для вызова скрипта. А для идентификации скрипта, к примеру, в сообщении о порядке его использования (usage message) достаточно базового имени (тот же basename) без пути к нему:
echo "usage: ${0##*/} inputfile [outputfile]"
Это пример удаления символов в начале/слева (префикса) строки. То есть, чтобы удалить префикс, нужно добавить в конструкцию ${VAR} решетку (#) и шаблон для удаления префикса. ${VAR#abc} удалит символы abc, если c них начинается значение $VAR ${VAR#*abc} удалит всё до символов abc ВКЛЮЧИТЕЛЬНО, кратчайшее совпадение (#) ${VAR##*abc} тоже самое, но выбирается самое длинное совпадение (##)
Наглядно:
Short вариант команды dirname или удаление суффикса
Подобно #, удаляющему префикс, знак % удаляет суффикс (символы справа).
И так же, двойной знаки %% (вместе с *) удаляют всё после самого длинного совпадения.
Эти выражения не полностью эквивалентны команде dirname: применительно к /file последнее выражение вернет пустую строку, а dirname - / (косую черту).
Но если нужна абсолютная схожесть с dirname в этих случаях, можно просто добавить слэш к выражению - ${0%/*}/ - и результат будет заканчиваться слэшем.
"Запоминалка" для символов
Чтобы проще запомнить, что # удаляет префикс, а % - суффикс, в книге предлагается аналогия с клавишами, где символ # (Shift+3) находится слева от % (Shift+5).
Модификаторы для преобразования в верхний/нижний регистры
С помощью ^ или ^^ можно преобразовать первый или все символы в верхний регистр, а с помощью , или ,, - в нижний регистр.
Наглядно:
# ^ и ^^TXT="silly little things"; echo "${TXT^}" # Silly little thingsTXT="silly little things"; echo "${TXT^^}" # SILLY LITTLE THINGS# , и ,,TXT="Dumb Shit"; echo "${TXT,}" # dumb ShitTXT="CLOWN ASS CAR"; echo "${TXT,,}" # clown ass car
declare [-u|-l]
А можно сразу объявить переменные в верхнем/нижнем регистрах:
declare -u var # UPPERdeclare -l var # lower
Значения этих переменных будут всегда преобразовываться в указанный в параметрах declare регистр.
Модификатор замены / (слэш)
Выполняет люблю замену в любом месте строки (как в sed, например).
Нужно указать после/ или // (для замены первого или всех совпадений, соответственно) искомый шаблон и через еще один слэш - строку замены. НЕ ТРЕБУЕТСЯ завершающий слэш.
Нужно добавить : (двоеточие) после имени переменной, указать порядковый номер символа (отсчет идет от 0) (начало подстроки), поставить еще одно : (двоеточие) и указать длину извлекаемой подстроки.
Есть и условные подстановки, выполняющиеся при определенных условиях. Их особенность - : (двоеточие), после которого идет другой спец. символ: - (минус), + (плюс)
или = (знак равенства). Они проверяют, была ли создана переменная и имеет ли она значение. Несозданной (неустановленной) считается переменная, которой еще не было присвоено значение или которая была удалена с помощью команды unset. Позиционный параметр ($1, $2, …) считается несозданным, если пользователь не передал такой параметр.
Если из этих условных подстановок убрать двоеточие→ они выполнятся, если переменная не создана; для созданных переменных вернутся их значения (даже если это пустая строка).
Дефолтные значения переменных
Один из частых применений условных подстановок - указаний дефолтных значений переменным, например, в скриптах с одним optional параметром. При использовании такой подстановки если параметр не был указан при вызове скрипта - присвоится указанное дефолтное значение.
Наглядно:
LEN="${1:-5}"some_command | head -n "$LEN"# илиLEN="${1:-$LEN}"
Выражение выше присвоит переменной LEN значение $1, либо 5, если последний не был указан.
Идиома соединения
Условная подстановка со знаком + проверяет, присвоено ли переменной какое-то значение (непустое) → если присвоено, возвращает указанное значение.
Встает вопрос: зачем возвращать какое-то другое значение, если переменная и так имеет свое??
С помощью такой подстановки можно создать, например, список значений, разделенных запятыми, без использования if для недопущения лишних запятых в начале/конце списка.
Идиома соединения наглядно (на примере создания списка значений с запятыми):
for filename in * ; do SEP="${LIST:+,}" # SEP - разделитель # Разделитель равен ",", если $LIST не пустой LIST="${LIST}${SEP}${filename}" # В список добавляются значения $filename через запятую ($SEP)done
Изменение значения
Это то самое исключение, которое может менять значение переменной, в отличие от других подстановок. Это выражение ${VAR:=value}, которая действует так же, как и ${VAR:-value}, но ПРИСВОИТ переменной указанное значение и вернет это значение, если переменная не была еще создана или имела пустое значение (в то время, как ${VAR:-value} просто возвращает указанное значение, не присваивая его).
Об этом исключении написал просто справедливости ради 🤓👆, ибо на деле используют эту идиому редко, потому что она не работает с позиционными параметрами ($1, $2, …).
Переменная $RANDOM
Из man bash
При каждом обращении к этой переменной генерируется случайное число от 0 до 32767. Присвоение значения этой переменной запускает генератор случайных чисел.
Естественно, такого диапазона чисел мало для написания всяких криптографических функций, но для добавления шума в слишком предсказуемые операции или моделирования игрового кубика сойдет.
Наглядно на примере выбора случайного элемента из списка:
declare -a some_listsome_list=(foo bar baz one two "three four")range="${#some_list[@]}"random=$(( $RANDOM % $range )) # от 0 до числа длины спискаecho "range = $range, random = $random, choice = ${some_list[$random]}"# range = 6, random = 2, choice = baz# более короткий, но не сильно читаемый вариантecho "choice = ${some_list[$(( $RANDOM % ${#some_list[@]} ))]}"# choice = three four
В источнике не рекомендуют этим кодом пользоваться (еще и без trap), ибо такой способ чреват состоянием гонки. Вообще для этого существует команда mktemp и на этом вопрос можно закрыть.
$RANDOM недоступна в dash
В некоторых дистрибутивах ссылка /bin/sh указывает на dash, где не работает переменная $RANDOM. Актуальные версии Debian/Ubuntu используют dash, ибо он позволяет быстрее загружаться за счет меньшего объема и скорости по сравнению с bash.
Пару слов о подстановке команд
Команды внутри $( ) работают в подоболочке.
Две эквивалентные строки, где вторая работает быстрее за счет использования внутренних механизмов командной оболочки:
for arg in $(cat /some/file)for arg in $(< /some/file) # FASTER
Первый вариант - по сути, cat abuse. А использование встроенных механизмов bash эффективнее сторонних инструментов.
Вложенная подстановка команд
Использование backticks при вложенной подстановке команд (да и при обычной подстановке 🤢) выглядит особенно уродливо и чревато ошибками из-за сложного синтаксиса.