Bash (Bourne-Again SHell) — это командный интерпретатор в UNIX-подобных операционных системах, который позволяет автоматизировать задачи на уровне командной строки. Bash-скрипты — это файлы, содержащие последовательность команд, которые могут быть выполнены с помощью интерпретатора Bash.
Bash-скрипты могут быть использованы для автоматизации повторяющихся задач. Например, если вам нужно каждый день создавать отчет и отправлять его по электронной почте, вы можете написать скрипт, который будет выполнять эти действия автоматически. Это позволит сэкономить много времени и уменьшить вероятность ошибок.
В этой статье мы рассмотрим основные концепции и инструменты для написания Bash-скриптов в Linux.
Bash-скрипты могут быть написаны в любом текстовом редакторе и должны иметь права на выполнение. Рассмотрим некоторые из наиболее популярных редакторов:
- Nano — это простой текстовый редактор, который поставляется вместе с большинством дистрибутивов Linux. Он имеет простой и интуитивно понятный интерфейс, а также некоторые дополнительные полезные функции, такие как подсветка синтаксиса.
- Vim — это один из самых популярных текстовых редакторов для Linux, хотя он может показаться сложным для начинающих. Vim имеет множество функций, которые помогают ускорить процесс написания кода, такие как подсветка синтаксиса, автодополнение и макросы.
- Emacs — это еще один популярный текстовый редактор для Linux. Он также имеет множество функций, которые могут значительно упростить процесс написания кода. Одна из его главных особенностей — это возможность запуска интерпретатора Bash внутри редактора, что позволяет тестировать скрипты без выхода из редактора.
В начале каждого скрипта должна быть строка, называемая шебанг (shebang), которая сообщает операционной системе, какой интерпретатор использовать для выполнения скрипта. Шебанг должен начинаться с символа решетки (#
) и следующим за ним символ восклицания (!
), после которых указывается путь к интерпретатору. Для использования интерпретатора Bash, шебанг будет выглядеть следующим образом:
#!/bin/bash
Также во время написания скрипта можно оставлять комментарии, которые начинаются с символа решетки и продолжаются до конца строки. Комментарии не будут выполнены интерпретатором и используются для описания функциональности скрипта. Например:
# Это комментарий
Ниже напишем свой первый скрипт. Допустим, мы хотим создать скрипт в Linux, который приветствует пользователя и выводит текущую дату и время на экран. Для этого создадим файл greeting.sh
в любой директории нашего компьютера и добавим следующий код:
#!/bin/bash
echo "Hello, $USER!"
echo "Today is $(date)"
Первая строка указывает на то, что это скрипт на Bash. Следующая строка echo "Hello, $USER!"
выводит приветствие с именем текущего пользователя. $USER
является системной переменной, которая содержит имя текущего пользователя. А третья строка echo "Today is $(date)"
выводит текущую дату и время. $(date)
используется для вызова команды date
, которая возвращает текущую дату и время в формате, указанном в настройках системы.
Создавая скрипт на Bash, важно убедиться, что файл можно будет исполнять. Для этого необходимо изменить права доступа к файлу. Об этом, а также о запуске написанного скрипта мы поговорим в следующей главе.
Запуск скриптов
Для запуска скрипта в Linux необходимо дать ему права на выполнение. Чтобы сделать файл исполняемым, можно воспользоваться командой chmod
(от англ. change mode — изменить режим). С помощью этой команды вы можете изменять права доступа к файлам и директориям в Linux.
Команда chmod
имеет следующий синтаксис:
chmod [опции] права_доступа файл
где права_доступа
— это специальный код, который задает права доступа к файлу или директории, а файл
— это путь к файлу или директории, права доступа которой нужно изменить.
Для того чтобы сделать файл исполняемым, нужно добавить к его правам доступа бит исполнения (x
).
Например, сделаем файл greeting.sh
исполняемым, для этого используем следующую команду:
chmod +x greeting.sh
Эта команда добавит права на выполнение для текущего пользователя. Теперь мы можем запустить Bash-скрипт в Linux, вызвав его из терминала:
./greeting.sh
Результат выполнения скрипта продемонстрирован на картинке ниже.
Параметры командной строки
Параметры командной строки позволяют передавать аргументы в скрипты Linux при его запуске. Параметры командной строки могут быть доступны в скрипте как $1, $2, $3 и т.д., где $1 — это первый параметр, $2 — второй параметр и т.д.
Перепишем скрипт из предыдущей главы для приветствия пользователя:
#!/bin/bash
echo "Hello, $1!”
А затем запустим скрипт, передав ему аргумент $USER
:
./greeting.sh $USER
Результат запуска представлен на картинке ниже.
Кроме того, возможно использовать специальные параметры командной строки:
$0
— имя скрипта (то есть, имя файла, который был запущен)$#
— количество переданных параметров$*
или$@
— список всех переданных параметров (в виде одной строки или массива соответственно)$?
— код возврата последней выполненной команды
Например, чтобы вывести на экран количество переданных параметров, можно использовать следующий код:
#!/bin/bash
echo "Hello, " $1 “!”
echo "Количество переданных параметров: $#"
Результат запуска скрипта представлен на картинке ниже.
Переменные
Переменные в Bash используются для хранения данных, таких как строки и числа. Они могут быть определены явно, путем присвоения значения, или неявно, путем автоматического присвоения значения при выполнении определенных операций. Для создания переменной в Bash необходимо присвоить ей значение, используя знак равенства (=
). Например:
company="Timeweb"
Обратите внимание, что не должно быть пробелов между именем переменной, знаком равенства и значением.
Значение переменной можно получить, указав ее имя после команды echo
и знака $
. Например:
echo $company
Также возможно присвоить значение переменной через ввод с клавиатуры с помощью команды read
. Например, следующий скрипт запрашивает у пользователя имя и сохраняет его в переменной:
#!/bin/bash
echo "What is your name?"
read name
echo "Hello, $name!"
Результат работы данного скрипта представлен на картинке ниже.
В Bash доступны несколько специальных переменных, которые автоматически определяются и заполняются системой. Например, переменная $HOME
содержит путь к домашней директории пользователя, а $PWD
содержит путь к текущей рабочей директории.
Кроме этого, доступны переменные окружения, которые определяются системой и могут использоваться в скриптах. Например, $PATH
содержит список директорий, в которых Bash ищет исполняемые файлы.
Переменные также могут использоваться для передачи значений между различными командами и скриптами. Например, для передачи значения переменной из одного скрипта в другой, необходимо использовать команду export
:
export название_переменной
Условные операторы
Условные операторы — это конструкции, которые позволяют выполнить определенный набор действий в зависимости от истинности или ложности какого-то условия. В Bash-скриптах условия записываются в скобках и передаются в команду if
.
Синтаксис оператора if
выглядит следующим образом:
if [ условие ]
then
команды, которые нужно выполнить, если условие верно
fi
Здесь в квадратных скобках указывается условие, которое необходимо проверить. Если условие истинно, то будут выполнены команды, указанные между then
и fi
.
Например, напишем скрипт в Linux chetnechet.sh
, который будет проверять, является ли число, введенное пользователем, четным или нечетным:
#!/bin/bash
echo "Введите число: "
read n
if (( $n % 2 == 0 ))
then
echo "Число $n четное"
else
echo "Число $n нечетное"
fi
В этом примере мы использовали оператор %
, который вычисляет остаток от деления на 2. Если остаток равен 0, то число четное, иначе — нечетное.
Результат работы скрипта представлен на картинке ниже.
Кроме того, существует несколько операторов сравнения, которые можно использовать в условных конструкциях:
-eq
– равно;-ne
– не равно;-gt
– больше;-lt
– меньше;-ge
– больше или равно;-le
– меньше или равно.
Например, чтобы проверить, является ли переменная $a
больше переменной $b
, можно написать следующее:
if [ $a -gt $b ]
then
echo "$a больше $b"
fi
Важно помнить, что в условных конструкциях необходимо использовать пробелы вокруг операторов сравнения. Если пробелов нет, то Bash-скрипт будет считать это одной большой строкой, а не операцией сравнения.
Кроме if
, в Bash-скриптах также используется конструкция case
. Эта конструкция позволяет проверять значение переменной на соответствие нескольким вариантам. Но об этом мы поговорим в следующей главе.
Конструкция Case
Конструкция case
в bash-скриптах позволяет упростить написание условных операторов для сравнения переменных с несколькими возможными значениями.
Синтаксис конструкции case
выглядит следующим образом:
case variable in
pattern1)
command1
;;
pattern2)
command2
;;
pattern3)
command3
;;
*)
default command
;;
esac
Где variable
— это переменная, которую нужно проверить, pattern1
, pattern2
, pattern3
— это возможные значения, которые нужно проверить, и command1
, command2
, command3
— это команды, которые нужно выполнить в зависимости от значения переменной.
Символ *
в конце списка значений используется как обработчик по умолчанию, если ни одно из значений не соответствует переменной.
Для примера, давайте рассмотрим скрипт, который проверяет день недели и выполняет соответствующее действие:
#!/bin/bashday=$(date +%u)
case $day in
1)
echo "Сегодня понедельник"
;;
2)
echo "Сегодня вторник"
;;
3)
echo "Сегодня среда"
;;
4)
echo "Сегодня четверг"
;;
5)
echo "Сегодня пятница"
;;
6)
echo "Сегодня суббота"
;;
7)
echo "Сегодня воскресенье"
;;
*)
echo "Некорректное значение дня недели"
;;
esac
В этом примере мы используем переменную day
, которую мы определяем с помощью команды date +%u
. В данном случае, %u
используется для получения числового значения дня недели от 1 (понедельник) до 7 (воскресенье). Затем мы сравниваем эту переменную с днями недели, используя конструкцию case
. Если ее значение соответствует определенному значению дня недели, то мы выводим соответствующее сообщение. Если значение не соответствует перечисленным дням недели, мы выводим сообщение об ошибке.
Результат работы скрипта продемонстрирован на картинке ниже.
Циклы
Циклы в Bash используются для выполнения повторяющихся действий. Существуют два типа циклов: цикл for
и цикл while
.
Цикл for
используется для выполнения команд для каждого элемента из списка.
Синтаксис цикла for
выглядит следующим образом:
for переменная in список
do
команды
done
Здесь переменная принимает значение элемента из списка, после чего для каждого из них выполняются команды, указанные между do
и done
.
Пример:
#!/bin/bashfor i in {1..10}; do
echo "Number: $i"
done
В этом примере i
принимает значения от 1 до 10, и для каждого из них будет выполнена команда echo "Number: $i"
. Результат выполнения этого цикла будет выглядеть следующим образом:
Следующий цикл while
используется для выполнения команд до тех пор, пока условие не станет ложным. Синтаксис цикла while
выглядит следующим образом:
while [ условие ]
do
команды
done
Здесь в квадратных скобках указывается условие, которое проверяется перед каждой итерацией цикла. Команды, указанные между do
и done
, будут выполнены до тех пор, пока условие остается истинным.
Пример:
#!/bin/bashcount=1
while [ $count -le 10 ]; do
echo "Count: $count"
count=$((count+1))
done
В этом примере count
увеличивается на 1 после каждой итерации цикла. Когда значение count
достигает 10, цикл завершается. Результат выполнения этого цикла будет выглядеть следующим образом:
Функции
Функции в Bash используются для группировки команд в логически связанные блоки. Функции могут быть вызваны из скрипта с помощью имени функции.
Синтаксис функции выглядит следующим образом:
название_функции () {
команды_и_выражения
}
Название функции должно начинаться с буквы или символа подчеркивания и может содержать только буквы, цифры и символы подчеркивания. За названием функции следует список аргументов в скобках. Команды и выражения, которые должны быть выполнены при вызове функции, должны быть внутри фигурных скобок.
Вот пример функции, которая выводит текущее время и дату:
#!/bin/bashprint_date () {
echo "Сегодняшняя дата: $(date)"
}
print_date #Вызов функции
Результат работы скрипта представлен на картинке ниже.
Функции также могут иметь аргументы, которые передаются в качестве параметров внутри скобок при их вызове. Вот пример функции, которая принимает два аргумента и выводит их сумму:
#!/bin/bashsum_numbers () {
result=$(( $1 + $2 ))
echo "Сумма чисел $1 и $2 равна $result"
}
sum_numbers 10 20 #Вызов функции
В этом примере $1
и $2
— это переменные, которые содержат значения первого и второго аргументов соответственно. sum_numbers 10 20
вызовет функцию sum_numbers
с аргументами 10
и 20
и выведет следующий результат:
Функции также могут возвращать значения при помощи ключевого слова return
. Перепишем прошлый пример, используя новые знания:
#!/bin/bashsum_numbers () {
result=$(( $1 + $2 ))
return $result
}
sum_numbers 12 24 #Вызов функции
echo "Сумма чисел равна $?" #Вывод
Здесь результат сохраняется в переменную result
и возвращается из функции с помощью команды return
.
Переменная $?
содержит код возврата функции, то есть в данном случае — результат вычисления суммы.
Результат работы скрипта представлен на картинке ниже.
Есть и другой способ взаимодействия с результатом вызова функции, помимо return
. Немного перепишем код прошлого скрипта:
#!/bin/bashsum_numbers () {
result=$(( $1 + $2 ))
echo $result
}
sum=$(sum_numbers 9 11)
echo "Сумма чисел равна $sum" #Вывод
Здесь, вместо использования $?
и return
, мы сохраняем результат вызова функции в переменную sum
и затем выводим ее значение на экран. Результат продемонстрирован на картинке ниже.
Работа с файлами и директориями
Bash-скрипты могут использоваться для выполнения различных операций с файлами и директориями в Linux. Например, чтобы проверить существование файла, можно использовать команду:
test -e filename
Если файл существует, то команда вернет значение 0, в противном случае — ненулевое значение. Для работы с директориями в Bash-скриптах можно использовать команды cd
, mkdir
, rmdir
, ls
и т.д.
Отладка скриптов
Отладка Bash-скриптов может оказаться трудной задачей, поскольку проблемы могут быть вызваны различными факторами, такими как ошибки синтаксиса, неправильное использование переменных или функций и т.д. Для отладки Bash-скриптов можно использовать такие инструменты, как set -x
, set -v
и set -e
.
Команда set -x
позволяет выводить на экран команды перед их выполнением, команда set -v
выводит на экран значения переменных перед их использованием, а set -e
прерывает выполнение скрипта в случае ошибки.
Заключение
Bash-скрипты — это инструмент для автоматизации задач в UNIX-подобных операционных системах. В этой статье мы рассмотрели основные концепции и инструменты для написания Bash-скриптов, такие как синтаксис, переменные, условные операторы, циклы, функции и запуск скриптов. Надеемся, что эта инструкция поможет вам стать более продуктивным и опытным пользователем Linux.
Бесплатная книга-сайт на русском, полный гайд
Advanced Bash-Scripting Guide
Введение
BASH — Bourne-Again SHell (что может переводится как «перерожденный шел», или «Снова шел Борна(создатель sh)»), самый популярный командный интерпретатор в юниксоподобных системах, в особенности в GNU/Linux. Ниже приведу ряд встроенных команд, которые мы будем использовать для создания своих скриптов.
break
выход из цикла for, while или until
continue
выполнение следующей итерации цикла for, while или until
echo
вывод аргументов, разделенных пробелами, на стандартное устройство вывода
exit
выход из оболочки
export
отмечает аргументы как переменные для передачи в дочерние процессы в среде
hash
запоминает полные имена путей команд, указанных в качестве аргументов, чтобы не искать их при следующем обращении
kill
посылает сигнал завершения процессу
pwd
выводит текущий рабочий каталог
read
читает строку из ввода оболочки и использует ее для присвоения значений указанным переменным.\
return
заставляет функцию оболочки выйти с указанным значением
shift
перемещает позиционные параметры налево
test
вычисляет условное выражение
times
выводит имя пользователя и системное время, использованное оболочкой и ее потомками
trap
указывает команды, которые должны выполняться при получении оболочкой сигнала
unset
вызывает уничтожение переменных оболочки
wait
ждет выхода из дочернего процесса и сообщает выходное состояние.
И конечно же кроме встроенных команд мы будем использовать целую кучу внешних, отдельных команд-программ, с которыми мы познакомимся уже в процессе
Что необходимо знать с самого начала
- Любой bash-скрипт должен начинаться со строки:
#!/bin/bash
в этой строке после #! указывается путь к bash-интерпретатору, поэтому если он у вас установлен в другом месте(где, вы можете узнать набрав whereis bash
) поменяйте её на ваш путь.
- Коментарии начинаются с символа # (кроме первой строки).
- В bash переменные не имеют типа(о них речь пойдет ниже)
Переменные и параметры скрипта
Приведу как пример небольшой пример, который мы разберем:
#!/bin/bash
#указываем где у нас хранится bash-интерпретатор
#присваиваем переменной parametr1 значение первого параметра скрипта
parametr1=$1
#присваиваем переменной script_name значение имени скрипта
script_name=$0
# команда echo выводит определенную строку, обращение к переменным осуществляется через $имя_переменной.
echo "Вы запустили скрипт с именем $script_name и параметром $parametr1"
# здесь мы видим другие кавычки, разница в том, что в одинарных кавычках не происходит подстановки переменных.
echo 'Вы запустили скрипт с именем $script_name и параметром $parametr1'
#Выход с кодом 0 (удачное завершение работы скрипта)
exit 0
Результат выполнения скрипта:
ite@ite-desktop:~$ ./test.sh qwerty
Вы запустили скрипт с именем ./test.sh и параметром qwerty
Вы запустили скрипт с именем $script_name и параметром $parametr1
После того как мы познакомились как использовать переменные и передавать скрипту параметры, время познакомиться с зарезервированными переменными:
$DIRSTACK
— содержимое вершины стека каталогов
$EDITOR
— текстовый редактор по умолчанию
$EUID
— Эффективный UID. Если вы использовали программу su для выполнения команд от другого пользователя, то эта переменная содержит UID этого пользователя, в то время как…
$UID
— …содержит реальный идентификатор, который устанавливается только при логине.
$FUNCNAME
— имя текущей функции в скрипте.
$GROUPS
— массив групп к которым принадлежит текущий пользователь
$HOME
— домашний каталог пользователя
$HOSTNAME
— ваш hostname
$HOSTTYPE
— архитектура машины.
$LC_CTYPE
— внутренняя переменная, котороя определяет кодировку символов
$OLDPWD
— прежний рабочий каталог
$OSTYPE
— тип ОС
$PATH
— путь поиска программ
$PPID
— идентификатор родительского процесса
$SECONDS
— время работы скрипта(в сек.)
$#
— общее количество параметров переданных скрипту
$*
— все аргументы переданыне скрипту(выводятся в строку)
$@
— тоже самое, что и предыдущий, но параметры выводятся в столбик
$!
— PID последнего запущенного в фоне процесса
$$
— PID самого скрипта
Условия
Условные операторы, думаю, знакомы практически каждому, кто хоть раз пытался на чем-то писать программы. В bash условия пишутся след. образом (как обычно на примере):
#!/bin/bash
#в переменную source засовываем первый параметр скрипта
source=$1
#в переменную dest засовываем второй параметр скрипта
dest=$2
# в ковычках указываем имена переменных для сравнения. -eq — логическое сравнение обозначающие «равны»
if [[ "$source" -eq "$dest" ]]
# если они действительно равны, то
then
#выводим сообщение об ошибке, т.к. $source и $dest у нас равны
echo "Применик $dest и источник $source один и тот же файл!"
# выходим с ошибкой (1 — код ошибки)
exit 1
# если же они не равны
else
# то выполняем команду cp: копируем источник в приемник
cp $source $dest
echo "Удачное копирование!"
fi
#обозначаем окончание условия.
Результат выполнения скрипта:
ite@ite-desktop:~$ ./primer2.sh 1 1
Применик 1 и источник 1 один и тот же файл!
ite@ite-desktop:~$ ./primer2.sh 1 2
Удачное копирование!
Структура if-then-else
используется следующим образом:
if
<команда или набор команд возвращающих код возврата(0 или 1)>
then
<если выражение после if истино, то выполняется этот блок>
else
<если выражение после if ложно, тот этот>
В качестве команд возвращающих код возврата могут выступать структуры [[ , [ , test, (( ))
или любая другая(или несколько) linux-команда.
test
— используется для логического сравнения. после выражения, неоьбходима закрывающая скобка «]»
[
— синоним команды test
[[
— расширенная версия «[» (начиная с версии 2.02)(как в примере), внутри которой могут быть использованы || (или), & (и). Долна иметь закрывающуб скобку «]]»
(( ))
— математическое сравнение.
для построения многоярусных условий вида:
if ...
then ....
else
if ....
then....
else ....
для краткости и читаемости кода, можно использовать структуру:
if ..
then ...
elif ...
then ...
elif ...
Условия. Множественный выбор
Если необходимо сравнивать какоую-то одну переменную с большим количеством параметров, то целесообразней использовать оператор case.
#!/bin/bash
echo "Выберите редатор для запуска:"
echo "1 Запуск программы nano"
echo "2 Запуск программы vi"
echo "3 Запуск программы emacs"
echo "4 Выход"
#здесь мы читаем в переменную $doing со стандартного ввода
read doing
case $doing in
1)
/usr/bin/nano
# если $doing содержит 1, то запустить nano
;;
2)
/usr/bin/vi
# если $doing содержит 2, то запустить vi
;;
3)
/usr/bin/emacs
# если $doing содержит 3, то запустить emacs
;;
4)
exit 0
;;
*)
#если введено с клавиатуры то, что в case не описывается, выполнять следующее:
echo "Введено неправильное действие"
esac
#окончание оператора case.
Результат работы:
ite@ite-desktop:~$ ./menu2.sh
Выберите редатор для запуска:
1 Запуск программы nano
2 Запуск программы vi
3 Запуск программы emacs
4 Выход
После выбор цифры и нажатия Enter запуститься тот редактор, который вы выбрали(если конечно все пути указаны правильно, и у вас установлены эти редакторы )
Прведу список логических операторв, которые используются для конструкции if-then-else-fi:
-z
# строка пуста
-n
# строка не пуста
=, (==)
# строки равны
!=
# строки неравны
-eq
# равно
-ne
# неравно
-lt,(< )
# меньше
-le,(<=)
# меньше или равно
-gt,(>)
#больше
-ge,(>=)
#больше или равно
!
#отрицание логического выражения
-a,(&&)
#логическое «И»
-o,(||)
# логическое «ИЛИ»
С основами языка и условиями мы разобрались, чтобы не перегружать статью, разобью её на несколько частей(допустим на 3). Во второй части разберем операторы цикла и выполнение математических операций.
Основы BASH. Часть 2
Циклы. Цикл for-in.
Оператор for-in
предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной.
Синтаксис следующий:
for переменная in список_значений
do
команды
done
Рассмотрим небольшой пример:
#!/bin/bash
for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно
do
echo "Console number is $i" >> /dev/pts/$i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i"
done #цикл окончен
exit 0
После выполнения примера в первых 5 виртуальных консолях(терминалах) появится строка с её номером. В переменную $i
поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной
Циклы. Цикл while.
Цикл while
сложнее цикла for-in
и используется для повторения команд, пока какое-то выражение истинно( код возврата = 0).
Синтаксис оператора следующий:
while выражение или команда возвращающая код возврата
do
команды
done
Пример работы цикла рассмотрим на следующем примере:
#!/bin/bash
again=yes #присваиваем значение "yes" переменной again
while [ "$again" = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes"
do
echo "Please enter a name:"
read name
echo "The name you entered is $name"
echo "Do you wish to continue?"
read again
done
echo "Bye-Bye"
А теперь результат работы скрипта:
ite@ite-desktop:~$ ./bash2_primer1.sh
Please enter a name:
ite
The name you entered is ite
Do you wish to continue?
yes
Please enter a name:
mihail
The name you entered is mihail
Do you wish to continue?
no
Bye-Bye
Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между do
и done
можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл.
Теперь об условии истинности. После while
, как и в условном операторе if-then-else
можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор [
аналог команды test
, которая проверяет истинность условия, которое ей передали.
Рассмотрим еще один пример, я взял его из книги Advanced Bash Scripting. Уж очень он мне понравился :), но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов UNTIL-DO. Эта практически полный аналог цикла WHILE-DO, только выполняется пока какое-то выражение ложно.
Вот пример:
#!/bin/bash
echo "Введите числитель: "
read dividend
echo "Введите знаменатель: "
read divisor
dnd=$dividend #мы будем изменять переменные dividend и divisor,
#сохраним их знания в других переменных, т.к. они нам
#понадобятся
dvs=$divisor
remainder=1
until [ "$remainder" -eq 0 ]
do
let "remainder = dividend % divisor"
dividend=$divisor
divisor=$remainder
done
echo "НОД чисел $dnd и $dvs = $dividend"
Результат выполнения скрипта:
ite@ite-desktop:~$ ./bash2_primer3.sh
Введите числитель:
100
Введите знаменатель:
90
НОД чисел 100 и 90 = 10
Математические операции
Команда let
.
Команда let
производит арифметические операции над числами и переменными.
Рассмотрим небольшой пример, в котором мы производим некоторые вычисления над введенными числами:
#!/bin/bash
echo "Введите a: "
read a
echo "Введите b: "
read b
let "c = a + b" #сложение
echo "a+b= $c"
let "c = a / b" #деление
echo "a/b= $c"
let "c <<= 2" #сдвигает c на 2 разряда влево
echo "c после сдвига на 2 разряда: $c"
let "c = a % b" # находит остаток от деления a на b
echo "$a / $b. остаток: $c "
Результат выполнения:
ite@ite-desktop:~$ ./bash2_primer2.sh
Введите a:
123
Введите b:
12
a+b= 135
a/b= 10
c после сдвига на 2 разряда: 40
123 / 12. остаток: 3
Ну вот, как видите ничего сложного, список математических операций стандартный:
+
— сложение
—
— вычитание
*
— умножение
/
— деление
**
— возведение в степень
%
— модуль(деление по модулю), остаток от деления
let
позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных. Например:a = a+b
эквивалентноa +=b
и т.д
Работа с внешними программами при написании shell-скриптов
Для начала немного полезной теории.
Перенаправление потоков.
В bash (как и многих других оболочках) есть встроенные файловые дескрипторы: 0 (stdin)
, 1 (stdout)
, 2 (stderr)
.
stdout
— Стандартный вывод. Сюда попадает все что выводят программы
stdin
— Стандартный ввод. Это все что набирает юзер в консоли
stderr
— Стандартный вывод ошибок.
Для операций с этими дескрипторами, существуют специальные символы: >
(перенаправление вывода), <
(перенаправление ввода). Оперировать ими не сложно. Например:
cat /dev/random > /dev/null #перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция :)) )
или
ls -la > listing #записать в файл listing содержание текущего каталога (уже полезней)
Если есть необходимость дописывать в файл(при использовании «>
» он заменятеся), необходимо вместо «>
» использовать «>>
«
sudo < my_password
после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.
Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:
./program_with_error 2> error_file
цифра 2 перед «>
» означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).
Если необходимо заставить stderr
писать в stdout
, то это можно след. образом:
./program_with_error 2>&1
символ «&
» означает указатель на дескриптор 1(stdout)
(Поумолчанию stderr
пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)).
2. Конвееры.
Конвеер — очень мощный инструмент для работы с консолью Bash. Синтаксис простой:
команда1 | команда 2
— означает, что вывод команды 1 передастся на ввод команде 2
Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например:
ls -la | grep «hash» |sort > sortilg_list
вывод команды ls -la
передается команде grep
, которая отбирает все строки, в которых встретится слово hash, и передает команде сортировке sort
, которая пишет результат в файл sorting_list. Все довольно понятно и просто.
Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке stdout
одной команды и передача на stdin
другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты.
1. Передача вывода в переменную.
Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в ``
ковычки, например
a = ` echo «qwerty» `
echo $a
Результат работы: qwerty
Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример:
LIST=`find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr '\n' ' '`
for ONE_OF_LIST in $LIST
do
svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST
done
Здесь мы используем цикл for-do-done
для архивирование всех директорий в папке /svn/ с помощью команды svnadmin hotcopy
(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr '\n' ' '
В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу.
Введение
Набор встроенных команд bash (и его аналогов sh, zsh, etc) совместим с любым POSIX-совместимым приложением в Linux, что позволяет встроить в ваш bash-скрипт любое совместимое приложение. Это дает очень большой набор возможностей в сфере автоматизации рутинных задач администрирования систем Linux, деплоя и сборки приложений, различных пакетных обработок, в том числе аудио и видео.
Командная строка — самый мощный пользовательский интерфейс из существующих на данный момент. Базовый уровень знаний получить достаточно просто. Рекомендуется изучить руководство bash. Это можно сделать, выполнив команду man bash.
Суть bash-скриптов — записать все ваши действия в один файл и выполнять их по необходимости.
В этой статье расскажем про написание bash-скриптов с нуля и получим представление, какую пользу можно из них извлечь. Рекомендуем держать под рукой bash-справочник, если вы планируете заняться этим всерьез.
Развертывание среды
Для выполнения скриптов, которые мы будем учиться писать, нужна среда. Если вы используете на своем компьютере систему Linux, вы можете делать все локально. Если Windows, — можете установить WSL/WSL2. Кроме того, вы можете создать виртуальный сервер и подключиться к нему по SSH. Так вы не навредите своему компьютеру если что-то пойдет не так.
Мы выбрали вариант создать виртуальную машину. Залогинимся в личном кабинете https://my.selectel.ru/, нажав на вкладку «Облачная платформа». Там вы сможете создать виртуальный сервер.
Необходимо выбрать зону размещения сервера исходя из его близости к пользователям. Чем дальше сервер, тем выше пинг.
Нажмем «Создать сервер».
В разделе «Источник» убеждаемся, что выбран образ Ubuntu 20.04.
Конфигурацию можно настроить по своим потребностям.
В разделе «Сеть» стоит выбрать «Подсеть — Плавающий IP-адрес».
В разделе «Доступ» загрузите SSH-ключ и не забудьте сохранить root-пароль. Подробнее об этом рассказано в этой статье
Теперь можно создать сервер кнопкой «Создать» в самом низу.
Будет отображена страница статуса сервера, надо дождаться индикации ACTIVE вверху справа.
Теперь на вкладке «Порты» можно посмотреть IP-адрес, присвоенный серверу.
Не копируйте чужой код
Копирование чужого кода на свой компьютер/сервер опасно. Ранее существовал «патч Бармина», представляющий из себя команду rm -rf /*. Ее очень любили давать новичкам Linux на некоторых конференциях в качестве универсального средства от всех проблем. Суть команды — рекурсивное удаление всех каталогов внутри корневого каталога, т. е. всех системных и пользовательских файлов. Сейчас эта команда не сработает во всех актуальных версиях Linux, но раньше она служила злой шуткой и наказанием тем, кто копировал чужие скрипты на свои серверы и выполнял их. Способов навредить серверу/компьютеру все еще достаточно, но они не столь очевидны.
Выбор редактора
Вам потребуется удобный текстовый редактор. Если вы подключаетесь по SSH, то лучшим выбором будут 3 варианта:
- * vim (если умеете из него выходить)
- * nano (прост, удобен и надежен)
- * mcedit (входит в пакет mc, классический двухпанельный консольный файловый менеджер)
Если вы делаете все локально, выбор полностью на вас. Обычный выбор под Linux — gedit. В этой инструкции мы пользовались nano через SSH на удаленном сервере.
Запуск “Hello, World!”
Первая программа, которую обычно пишут программисты это «Hello, World!» — простой вывод этой строки. Мы тоже с этого начнем. За вывод строки в консоль отвечает команда echo. Прямо в консоли вы можете напечатать echo «Hello, World!» и получить соответствующий вывод:
root@geneviev:~ # echo "Hello, World!"
Hello, World!
Сделаем это программой. Команда touch helloworld.sh создаст файл helloworld.sh. Команда nano helloworld.sh откроет этот файл для редактирования. Заполним файл нашей программой:
#!/bin/bash
echo "Hello, World!"
Для выхода с сохранением из nano надо нажать CTRL + O для сохранения (после чего нажать enter для перезаписи текущего открытого файла), а потом CTRL + X для выхода. Можно выходить без сохранения, при этом он спросит, точно ли вы хотите выйти без сохранения. Если да, надо нажать N для выхода без сохранения. Если вы нажмете Y, он спросит куда сохранить измененный файл, можно нажать enter для перезаписи редактируемого файла.
Разберем, что мы написали.
Первой строкой идет #!/bin/bash — фактически это указание на местоположение интерпретатора. Чтобы при запуске скрипта не требовалось указывать отдельно интерпретатор. Убедиться, что ваш bash интерпретатор лежит по этому пути можно через команду which bash:
root@geneviev:~ # which bash
/usr/bin/bash
Второй строкой идет непосредственно вся наша программа. Как она работает, мы разобрали выше, перейдем к выполнению.
Запустить ваш скрипт/команду можно двумя способами.
Способ №1: bash helloworld.sh. Вы вызываете интерпретатор и в аргументе передаете ему имя файла для исполнения.
root@geneviev:~ # bash helloworld.sh
Hello, World!
Способ №2: Сначала надо разрешить системе исполнять скрипт: chmod +x helloworld.sh. Эта команда сделает файл исполняемым. Теперь вы можете запустить его как любой бинарный файл в linux: ./helloworld.sh.
root@geneviev:~ # ./helloworld.sh
Hello, World!
Первая программа готова, она просто выводит строку в консоль.
Аргументы
Это параметры, которые вы можете передать программе при ее вызове. Например, программа ping принимает в качестве обязательного аргумента IP-адрес или DNS-имя, которое требуется пропинговать: ping selectel.ru. Это простой и удобный способ общения пользователя с программой.
Давайте научим нашу программу принимать аргументы и работать с ними. Доступ к аргументам осуществляется через служебную команду $X где X это число. $0 — всегда имя исполняемого скрипта. $1 — первый аргумент, $2 — второй и так далее. Конечно, если вы планируете передавать пару десятков аргументов вашему приложению, это может быть несколько утомительно, так что вам понадобится что-то вроде этого цикла, чтобы перебрать все поступившие аргументы:
for var in "$@"; do
echo "$var"
done
Подробнее про циклы будет рассказано в следующих разделах.
Пример, создадим новый файл: touch hellousername.sh. Выдаем права на исполнение chmod +x hellousername.sh.
Открываем nano hellousername.sh
Код примера следующий:
#!/bin/bash
echo "Hello, $1!"
Сохраняем, закрываем. Смотрим, что получилось.
root@geneviev:~ # ./hellousername.sh Vasya
Hello, Vasya!
Программа получилась маленькая, но она учит пользоваться (на самом базовом уровне) аргументами, которые мы в нее можем передать. В данном случае аргумент передается один, Vasya, мы сразу его используем, не делая никаких проверок.
root@geneviev:~ # ./hellousername.sh
Hello, !
При таком сценарии в нашей программе баг: она ни с кем не поздоровается. Чтобы исправить ситуацию, есть 2 способа: проверить число аргументов или проверить содержимое аргумента.
Способ №1
#!/bin/bash
if [ "$#" -lt 1 ]; then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
echo "Hello, $1!"
Более подробно конструкцию if then [else] fi мы рассмотрим далее, пока не будем на ней останавливаться. Важно понимать, что тут проверяется. $# Это число аргументов без учета имени скрипта, который всегда $0.
Способ №2
#!/bin/bash
if [ -z "$1" ]; then
echo "Имя пустое или не передано. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
echo "Hello, $1!"
Здесь тоже используется конструкция if then [else] fi. Ключ -z в if используется для проверки переменной на пустую строку. Есть противоположный ключ -n, он проверяет что строка не пустая. Конечно, этот способ некорректно использовать для проверки входящих аргументов, но в теле самой программы он будет полезен. Например, чтобы проверить что выполненное в самой программе приложение что-то вернуло.
Управляющие конструкции
if-else
Написание программ на любом из языков длиннее нескольких строчек сложно представить без ветвления. В разных языках бывают разные варианты ветвления, но в большинстве случаев используется синтаксис if else. В bash это также присутствует.
Возьмем один из примеров выше.
#!/bin/bash
if [ "$#" -lt 1 ]; then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
echo "Hello, $1!"
Происходит проверка системной переменной $# на то, что она меньше, чем (lower than, -lt) 1. Если это выражение истинно, мы переходим в блок команд, открывающийся ключевым словом then. Весь блок, начинающийся с if, должен завершаться ключевым словом fi. Более сложная структура ветвления может выглядеть примерно так:
if TEST-COMMAND1
then
STATEMENTS1
elif TEST-COMMAND2
then
STATEMENTS2
else
STATEMENTS3
fi
Реальный пример:
#!/bin/bash
if [ "$#" -lt 1 ];
then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
if [ "$1" = "Vasya" ]; then
echo "Whatsupp, Vasiliy?"
elif [ "$1" = "Masha" ];
then
echo "Hey, Masha"
elif [ "$1" = "Michael" ];
then
echo "Shalom, Michael"
else
echo "Hi, $1"
fi
Вывод программы:
root@geneviev:~ # ./hellousername.sh Vasya
Whatsupp, Vasiliy?
root@geneviev:~ # ./hellousername.sh Masha
Hey, Masha
root@geneviev:~ # ./hellousername.sh Michael
Shalom, Michael
root@geneviev:~ # ./hellousername.sh Andrew
Hi, Andrew
root@geneviev:~ # ./hellousername.sh
Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: ./hellousername.sh Vasya
Выражение «$1» = «Vasya» проверяет строки на идентичность. Блок после else выполняется только если выше не найден более подходящий блок.
&& и ||
В предыдущей главе вы могли заметить, что я использовал exit 1 для завершения работы программы в случае неуспешной проверки аргумента. Это означает, что программа завершилась с ошибкой. В bash есть операторы && и ||, которые используются для создания цепочек команд. Каждая цепочка зависит от результата выполнения предыдущей программы.
Пример 1: command1 && command2. В этом случае command2 выполнится, только если command1 завершится с кодом 0 (exit 0, по умолчанию).
Пример 2: command1 || command2. В этом случае command2 выполнится, только если command1 завершится с кодом отличным от 0.
Пример 3: command1 && command2 || command3. Если command1 завершится с кодом 0, то будет выполнен command2, иначе command3.
Переменные
Как гласит один из основных принципов программирования — Do Not Repeat Yourself (DRY). Вот и мы не будем повторять себя и перепишем предыдущий пример с использованием переменных, чтобы не вызывать echo каждый раз.
Переменные в bash создаются присваиванием: x=»foo bar» или z=$1. Переменной x мы присвоили строку @foo bar«, а переменной z мы присвоили значение первого аргумента. Использовать именованные переменные гораздо удобнее, чем использовать $1, особенно когда надо использовать его значение во многих местах.
К тому же, аргументы могут идти в разном порядке. Осмысленные названия переменных очень важны, при разрастании программы это снизит неизбежную путаницу. Избегайте имен переменных (и функций) вроде «a», «b», «zzzz», etc.
Чтобы не вызывать echo в каждом варианте с разными строками, разобьем строку на части. Первая часть будет приветствием. Вторая — именем. Третья — завершающим знаком препинания. Его можно не выносить в переменную.
#!/bin/bash
greetString="Hello"
nameString="stranger"
if [ "$#" -lt 1 ];
then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
if [ "$1" = "Vasya" ];
then
nameString="Vasiliy"
greetString="Whatsup"
elif [ "$1" = "Masha" ];
then
nameString="Maria"
elif [ "$1" = "Michael" ];
then
greetString="Shalom"
nameString="Michael"
fi
echo "$greetString, $nameString!"
В этом примере мы создаем переменные greetString и nameString, которым присваиваем значения по умолчанию. В конце программа выводит значения этих двух переменных с помощью echo и форматированной строки (в двойных кавычках переменные раскрываются). Между этими действиями программа определяет, надо ли присваивать переменным другие значения.
Switch case
Использование if-else конструкции в нашем примере не является оптимальным вариантом. Мы всего лишь сверяем значение переменной с определенным набором значений. В такой ситуации лучшим выбором будет switch-case-конструкция.
case "$variable" in
"$condition1" )
command...
;;
"$condition2" )
command...
;;
esac
Перепишем нашу программу приветствий с использованием switch-case:
#!/bin/bash
name=$1
case "$name" in
"Vasya" )
nameString="Vasiliy"
greetString="Whatsup"
;;
"Masha" )
greetString="Hey"
nameString="Maria"
;;
* )
greetString="Hello"
nameString="stranger"
;;
esac
echo "$greetString, $nameString!"
Циклы
Как и любой полноценный язык программирования, bash поддерживает циклы. Цикл for и цикл while. Циклы нужны, чтобы выполнять какой-то код заданное число раз. Например, при парсинге CSV перебирать построчно и каждую строку рассматривать отдельно.
Цикл for
Вот пример структуры цикла for:
for var in list
do
команды
done
Простой реальный пример:
#!/bin/bash
for name in Maria Vasya Michael stranger
do
echo "Hello, $name!"
done
Вывод:
root@geneviev:~ # ./cycle.sh
Hello, Maria!
Hello, Vasya!
Hello, Michael!
Hello, stranger!
Программа просто перебирает все имена, разделенные пробелом, и выводит их с помощью echo.
Попробуем немного усложнить пример:
#!/bin/bash
file=$1
for name in $(cat $file)
do
echo "Hello, $name!"
done
Создадим файл с именами touch names и запишем в него список имен для приветствия:
Maria
Vasiliy
Ivan
Nikolai
Innokentiy
Вывод:
root@geneviev:~ # ./cycle.sh
^C
root@geneviev:~ # ./cycle.sh names
Hello, Maria!
Hello, Vasiliy!
Hello, Ivan!
Hello, Nikolai!
Hello, Innokentiy!
Обратите внимание на ^C. Это символ прерывания выполнения программы. В нашем случае мы вызвали программу без аргумента, и она вошла в вечный цикл. Можно сказать, зависла. Пришлось завершить ее принудительно. Не забывайте делать проверки входных данных в реальных программах. Как это делать, можете посмотреть в главах if-else и switch case, например.
В нашей программе есть небольшой баг. Модифицируем файл имен:
Erich Maria Remarque
Vasiliy
Ivan
Nikolai
Innokentiy
Запустим программу, получим вывод:
root@geneviev:~ # ./cycle.sh names
Hello, Erich!
Hello, Maria!
Hello, Remarque!
Hello, Vasiliy!
Hello, Ivan!
Hello, Nikolai!
Hello, Innokentiy!
Как говорится, «Кто все эти люди?». Так получается, потому что у нас не задана переменная окружения IFS (Internal Field Separator), указывающая на разделители полей. Наш цикл использует пробелы и переносы строки как разделители. В начале скрипта (после #!/bin/bash) укажите использовать перенос строки как разделитель полей: IFS=$’\n’.
root@geneviev:~ # ./cycle.sh names
Hello, Erich Maria Remarque!
Hello, Vasiliy!
Hello, Ivan!
Hello, Nikolai!
Hello, Innokentiy!
В итоге мы получим возможность работать со строками целиком. Это пригодится для парсинга CSV.
Обычно цикл for используется со счетчиком. В C-like стиле. Что-то вроде for (i=0;i<10;i++){}. В bash тоже так можно.
#!/bin/bash
for (( i=1; i <= 10; i++ ))
do
echo "number is $i"
done
Вывод:
root@geneviev:~ # ./cycle.sh
number is 1
number is 2
number is 3
number is 4
number is 5
number is 6
number is 7
number is 8
number is 9
number is 10
Цикл while
Схема организации цикла while:
while команда проверки условия
do
другие команды
done
Простой способ сделать бесконечную петлю (бесконечный цикл):
while true
do
echo "this is infinity loop"
done
Это может пригодится, например, когда вам нужно вызывать что-то чаще, чем позволяет cron (например, раз в минуту). Или когда вам просто надо проверять какое-то значение постоянно. Областей применения у бесконечных циклов много.
Здесь используются те же самые выражения, что и в if:
#!/bin/bash
count=0
while [ $count -lt 10 ]
do
(( count++ ))
echo "count: $count"
done
Вывод:
root@geneviev:~ # ./cycle.sh
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
count: 7
count: 8
count: 9
count: 10
Из цикла можно выйти с помощью команды break (работает также и для for):
#!/bin/bash
count=0
while [ $count -lt 10 ]
do
(( count++ ))
echo "count: $count"
if [ "$count" -gt 5 ]
then
break
fi
done
Вывод:
root@geneviev:~ # ./cycle.sh
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
Заключение
Несмотря на огромную конкуренцию в сфере автоматизации рутины со стороны python, ruby, perl bash не сдает позиции. Он прост в освоении и использовании, гибок и так или иначе присутствует в абсолютном большинстве дистрибутивов Linux.
В этой статье были приведены только основы написания программ на bash. Надеемся, вы нашли их полезными для себя.
Advanced Bash-Scripting Guide
��������� ���������������� �� ����� ���������
��������� ��������
�������: ������ �������
��������: Russian Linux Gazette
����� ����������� � html-������� (~380��)
����� ����������� � sgml-������� (~380��)
������ ����������� �� ������������ ������� � ��������
�������� � ������� ���������������� �� ����� ���������,
������, ������ ���������� ���� ���������� . . . ����������, ��� �� ����� ���������
�������� � ������� UNIX. ��� ����������� �����
��������������� ��� �������, ��������������� ���
���������������� �������� ��� ��� ���������� ��
���������������� �� shell. ����������� �������� ������
������ ������������������� ��������, ��������� ������ ���� � �������� �����
��������� — ��� ��������� ���������.
��������� ������ ���������, � ���� .bz2 ������, ����������
�������� ������ � ������� SGML � HTML, �� ������� �� �������� ��������� ������. ��� �� ��
������� � change log.
In Linux, process automation relies heavily on shell scripting. This involves creating a file containing a series of commands that can be executed together.
In this article, we’ll start with the basics of bash scripting which includes variables, commands, inputs/ outputs, and debugging. We’ll also see examples of each along the way.
Let’s get started. 🚀
Table of Contents
- Pre-requisites
- Introduction
- Definition of Bash scripting
- Advantages of Bash scripting
- Overview of Bash shell and command line interface
- How to Get Started with Bash Scripting
- How to Run Bash Commands from the Command Line
- How to Create and Execute Bash Scripts
- Bash Scripting Basics
- Comments in bash scripting
- Variables and data types in Bash
- Input and output in Bash scripts
- Basic Bash commands (echo, read, etc.)
- Conditional statements (if/else)
- Looping and Branching in Bash
- While loop
- For loop
- Case statements
- How to Schedule Scripts using cron
- How to Debug and Troubleshoot Bash Scripts
- Conclusion
- Resources for learning more about Bash scripting
Pre-requisites
To follow along with this tutorial, you should have the following accesses:
- A running version of Linux with access to the command line.
If you do not have Linux installed or you are just starting out, you can easily access the Linux command line through Replit. Replit is a browser-based IDE where you can access the bash shell in a few minutes.
You can also install Linux on top of your Windows system using WSL (Windows Subsystem for Linux). Here is a tutorial for that.
Introduction
Definition of Bash scripting
A bash script is a file containing a sequence of commands that are executed by the bash program line by line. It allows you to perform a series of actions, such as navigating to a specific directory, creating a folder, and launching a process using the command line.
By saving these commands in a script, you can repeat the same sequence of steps multiple times and execute them by running the script.
Advantages of Bash scripting
Bash scripting is a powerful and versatile tool for automating system administration tasks, managing system resources, and performing other routine tasks in Unix/Linux systems. Some advantages of shell scripting are:
- Automation: Shell scripts allow you to automate repetitive tasks and processes, saving time and reducing the risk of errors that can occur with manual execution.
- Portability: Shell scripts can be run on various platforms and operating systems, including Unix, Linux, macOS, and even Windows through the use of emulators or virtual machines.
- Flexibility: Shell scripts are highly customizable and can be easily modified to suit specific requirements. They can also be combined with other programming languages or utilities to create more powerful scripts.
- Accessibility: Shell scripts are easy to write and don’t require any special tools or software. They can be edited using any text editor, and most operating systems have a built-in shell interpreter.
- Integration: Shell scripts can be integrated with other tools and applications, such as databases, web servers, and cloud services, allowing for more complex automation and system management tasks.
- Debugging: Shell scripts are easy to debug, and most shells have built-in debugging and error-reporting tools that can help identify and fix issues quickly.
Overview of Bash shell and command line interface
The terms «shell» and «bash» are used interchangeably. But there is a subtle difference between the two.
The term «shell» refers to a program that provides a command-line interface for interacting with an operating system. Bash (Bourne-Again SHell) is one of the most commonly used Unix/Linux shells and is the default shell in many Linux distributions.
A shell or command-line interface looks like this:
In the above output, zaira@Zaira
is the shell prompt. When a shell is used interactively, it displays a $
when it is waiting for a command from the user.
If the shell is running as root (a user with administrative rights), the prompt is changed to #
. The superuser shell prompt looks like this:
[root@host ~]#
Although Bash is a type of shell, there are other shells available as well, such as Korn shell (ksh), C shell (csh), and Z shell (zsh). Each shell has its own syntax and set of features, but they all share the common purpose of providing a command-line interface for interacting with the operating system.
You can determine your shell type using the ps
command:
ps
Here is the output for me:
In summary, while «shell» is a broad term that refers to any program that provides a command-line interface, «Bash» is a specific type of shell that is widely used in Unix/Linux systems.
Note: In this tutorial, we will be using the «bash» shell.
How to Get Started with Bash Scripting
Running Bash commands from the command line
As mentioned earlier, the shell prompt looks something like this:
[username@host ~]$
You can enter any command after the $
sign and see the output on the terminal.
Generally, commands follow this syntax:
command [OPTIONS] arguments
Let’s discuss a few basic bash commands and see their outputs. Make sure to follow along
date
: Displays the current date
zaira@Zaira:~/shell-tutorial$ date
Tue Mar 14 13:08:57 PKT 2023
pwd
: Displays the present working directory.
zaira@Zaira:~/shell-tutorial$ pwd
/home/zaira/shell-tutorial
ls
: Lists the contents of the current directory.
zaira@Zaira:~/shell-tutorial$ ls
check_plaindrome.sh count_odd.sh env log temp
echo
: Prints a string of text, or value of a variable to the terminal.
zaira@Zaira:~/shell-tutorial$ echo "Hello bash"
Hello bash
You can always refer to a commands manual with the man
command.
For example, the manual for ls
looks something like this:
man
How to Create and Execute Bash scripts
Script naming conventions
By naming convention, bash scripts end with .sh
. However, bash scripts can run perfectly fine without the sh
extension.
Adding the Shebang
Bash scripts start with a shebang
. Shebang is a combination of bash #
and bang !
followed by the bash shell path. This is the first line of the script. Shebang tells the shell to execute it via bash shell. Shebang is simply an absolute path to the bash interpreter.
Below is an example of the shebang statement.
#!/bin/bash
You can find your bash shell path (which may vary from the above) using the command:
which bash
Creating our first bash script
Our first script prompts the user to enter a path. In return, its contents will be listed.
Create a file named run_all.sh
using the vi
command. You can use any editor of your choice.
vi run_all.sh
Add the following commands in your file and save it:
#!/bin/bash
echo "Today is " `date`
echo -e "\nenter the path to directory"
read the_path
echo -e "\n you path has the following files and folders: "
ls $the_path
Let’s take a deeper look at the script line by line. I am displaying the same script again, but this time with line numbers.
1 #!/bin/bash
2 echo "Today is " `date`
3
4 echo -e "\nenter the path to directory"
5 read the_path
6
7 echo -e "\n you path has the following files and folders: "
8 ls $the_path
- Line #1: The shebang (
#!/bin/bash
) points toward the bash shell path. - Line #2: The
echo
command is displaying the current date and time on the terminal. Note that thedate
is in backticks. - Line #4: We want the user to enter a valid path.
- Line #5: The
read
command reads the input and stores it in the variablethe_path
. - line #8: The
ls
command takes the variable with the stored path and displays the current files and folders.
Executing the bash script
To make the script executable, assign execution rights to your user using this command:
chmod u+x run_all.sh
Here,
chmod
modifies the ownership of a file for the current user :u
.+x
adds the execution rights to the current user. This means that the user who is the owner can now run the script.run_all.sh
is the file we wish to run.
You can run the script using any of the mentioned methods:
sh run_all.sh
bash run_all.sh
./run_all.sh
Let’s see it running in action 🚀
Bash Scripting Basics
Comments start with a #
in bash scripting. This means that any line that begins with a #
is a comment and will be ignored by the interpreter.
Comments are very helpful in documenting the code, and it is a good practice to add them to help others understand the code.
These are examples of comments:
# This is an example comment
# Both of these lines will be ignored by the interpreter
Variables and data types in Bash
Variables let you store data. You can use variables to read, access, and manipulate data throughout your script.
There are no data types in Bash. In Bash, a variable is capable of storing numeric values, individual characters, or strings of characters.
In Bash, you can use and set the variable values in the following ways:
- Assign the value directly:
country=Pakistan
2. Assign the value based on the output obtained from a program or command, using command substitution. Note that $
is required to access an existing variable’s value.
same_country=$country
country
to the new variable same_country
To access the variable value, append $
to the variable name.
zaira@Zaira:~$ country=Pakistan
zaira@Zaira:~$ echo $country
Pakistan
zaira@Zaira:~$ new_country=$country
zaira@Zaira:~$ echo $new_country
Pakistan
Variable naming conventions
In Bash scripting, the following are the variable naming conventions:
- Variable names should start with a letter or an underscore (
_
). - Variable names can contain letters, numbers, and underscores (
_
). - Variable names are case-sensitive.
- Variable names should not contain spaces or special characters.
- Use descriptive names that reflect the purpose of the variable.
- Avoid using reserved keywords, such as
if
,then
,else
,fi
, and so on as variable names.
Here are some examples of valid variable names in Bash:
name
count
_var
myVar
MY_VAR
And here are some examples of invalid variable names:
2ndvar (variable name starts with a number)
my var (variable name contains a space)
my-var (variable name contains a hyphen)
Following these naming conventions helps make Bash scripts more readable and easier to maintain.
Input and output in Bash scripts
Gathering input
In this section, we’ll discuss some methods to provide input to our scripts.
- Reading the user input and storing it in a variable
We can read the user input using the read
command.
#!/bin/bash
echo "Today is " `date`
echo -e "\nenter the path to directory"
read the_path
echo -e "\nyour path has the following files and folders: "
ls $the_path
2. Reading from a file
This code reads each line from a file named input.txt
and prints it to the terminal. We’ll study while loops later in this article.
while read line
do
echo $line
done < input.txt
3. Command line arguments
In a bash script or function, $1
denotes the initial argument passed, $2
denotes the second argument passed, and so forth.
This script takes a name as a command-line argument and prints a personalized greeting.
echo "Hello, $1!"
We have supplied Zaira
as our argument to the script.
#!/bin/bash
echo "Hello, $1!"
greeting.sh
Output:
Displaying output
Here we’ll discuss some methods to receive output from the scripts.
- Printing to the terminal:
echo "Hello, World!"
This prints the text «Hello, World!» to the terminal.
2. Writing to a file:
echo "This is some text." > output.txt
This writes the text «This is some text.» to a file named output.txt
. Note that the >
operator overwrites a file if it already has some content.
3. Appending to a file:
echo "More text." >> output.txt
This appends the text «More text.» to the end of the file output.txt
.
4. Redirecting output:
ls > files.txt
This lists the files in the current directory and writes the output to a file named files.txt
. You can redirect output of any command to a file this way.
Basic Bash commands (echo, read, etc.)
Here is a list of some of the most commonly used bash commands:
cd
: Change the directory to a different location.ls
: List the contents of the current directory.mkdir
: Create a new directory.touch
: Create a new file.rm
: Remove a file or directory.cp
: Copy a file or directory.mv
: Move or rename a file or directory.echo
: Print text to the terminal.cat
: Concatenate and print the contents of a file.grep
: Search for a pattern in a file.chmod
: Change the permissions of a file or directory.sudo
: Run a command with administrative privileges.df
: Display the amount of disk space available.history
: Show a list of previously executed commands.ps
: Display information about running processes.
Conditional statements (if/else)
Expressions that produce a boolean result, either true or false, are called conditions. There are several ways to evaluate conditions, including if
, if-else
, if-elif-else
, and nested conditionals.
Syntax:
if [[ condition ]];
then
statement
elif [[ condition ]]; then
statement
else
do this by default
fi
We can use logical operators such as AND -a
and OR -o
to make comparisons that have more significance.
if [ $a -gt 60 -a $b -lt 100 ]
true
: a is greater than 60 AND b is less than 100.Let’s see an example of a Bash script that uses if
, if-else
, and if-elif-else
statements to determine if a user-inputted number is positive, negative, or zero:
#!/bin/bash
echo "Please enter a number: "
read num
if [ $num -gt 0 ]; then
echo "$num is positive"
elif [ $num -lt 0 ]; then
echo "$num is negative"
else
echo "$num is zero"
fi
The script first prompts the user to enter a number. Then, it uses an if
statement to check if the number is greater than 0. If it is, the script outputs that the number is positive. If the number is not greater than 0, the script moves on to the next statement, which is an if-elif
statement. Here, the script checks if the number is less than 0. If it is, the script outputs that the number is negative. Finally, if the number is neither greater than 0 nor less than 0, the script uses an else
statement to output that the number is zero.
Seeing it in action 🚀
Looping and Branching in Bash
While loop
While loops check for a condition and loop until the condition remains true
. We need to provide a counter statement that increments the counter to control loop execution.
In the example below, (( i += 1 ))
is the counter statement that increments the value of i
. The loop will run exactly 10 times.
#!/bin/bash
i=1
while [[ $i -le 10 ]] ; do
echo "$i"
(( i += 1 ))
done
For loop
The for
loop, just like the while
loop, allows you to execute statements a specific number of times. Each loop differs in its syntax and usage.
In the example below, the loop will iterate 5 times.
#!/bin/bash
for i in {1..5}
do
echo $i
done
Case statements
In Bash, case statements are used to compare a given value against a list of patterns and execute a block of code based on the first pattern that matches. The syntax for a case statement in Bash is as follows:
case expression in
pattern1)
# code to execute if expression matches pattern1
;;
pattern2)
# code to execute if expression matches pattern2
;;
pattern3)
# code to execute if expression matches pattern3
;;
*)
# code to execute if none of the above patterns match expression
;;
esac
Here, «expression» is the value that we want to compare, and «pattern1», «pattern2», «pattern3», and so on are the patterns that we want to compare it against.
The double semicolon «;;» separates each block of code to execute for each pattern. The asterisk «*» represents the default case, which executes if none of the specified patterns match the expression.
Let’s see an example.
fruit="apple"
case $fruit in
"apple")
echo "This is a red fruit."
;;
"banana")
echo "This is a yellow fruit."
;;
"orange")
echo "This is an orange fruit."
;;
*)
echo "Unknown fruit."
;;
esac
In this example, since the value of «fruit» is «apple», the first pattern matches, and the block of code that echoes «This is a red fruit.» is executed. If the value of «fruit» were instead «banana», the second pattern would match and the block of code that echoes «This is a yellow fruit.» would execute, and so on. If the value of «fruit» does not match any of the specified patterns, the default case is executed, which echoes «Unknown fruit.»
How to Schedule Scripts using cron
Cron is a powerful utility for job scheduling that is available in Unix-like operating systems. By configuring cron, you can set up automated jobs to run on a daily, weekly, monthly, or specific time basis. The automation capabilities provided by cron play a crucial role in Linux system administration.
Below is the syntax to schedule crons:
# Cron job example
* * * * * sh /path/to/script.sh
Here, the *
s represent minute(s) hour(s) day(s) month(s) weekday(s), respectively.
Below are some examples of scheduling cron jobs.
Schedule | Description | Example |
---|---|---|
0 0 * * * |
Run a script at midnight every day | 0 0 * * * /path/to/script.sh |
*/5 * * * * |
Run a script every 5 minutes | */5 * * * * /path/to/script.sh |
0 6 * * 1-5 |
Run a script at 6 am from Monday to Friday | 0 6 * * 1-5 /path/to/script.sh |
0 0 1-7 * * |
Run a script on the first 7 days of every month | 0 0 1-7 * * /path/to/script.sh |
0 12 1 * * |
Run a script on the first day of every month at noon | 0 12 1 * * /path/to/script.sh |
Using crontab
The crontab
utility is used to add and edit the cron jobs.
crontab -l
lists the already scheduled scripts for a particular user.
You can add and edit the cron through crontab -e
.
You can read more about corn jobs in my other article here.
How to Debug and Troubleshoot Bash Scripts
Debugging and troubleshooting are essential skills for any Bash scripter. While Bash scripts can be incredibly powerful, they can also be prone to errors and unexpected behavior. In this section, we will discuss some tips and techniques for debugging and troubleshooting Bash scripts.
Set the set -x
option
One of the most useful techniques for debugging Bash scripts is to set the set -x
option at the beginning of the script. This option enables debugging mode, which causes Bash to print each command that it executes to the terminal, preceded by a +
sign. This can be incredibly helpful in identifying where errors are occurring in your script.
#!/bin/bash
set -x
# Your script goes here
Check the exit code
When Bash encounters an error, it sets an exit code that indicates the nature of the error. You can check the exit code of the most recent command using the $?
variable. A value of 0
indicates success, while any other value indicates an error.
#!/bin/bash
# Your script goes here
if [ $? -ne 0 ]; then
echo "Error occurred."
fi
Use echo
statements
Another useful technique for debugging Bash scripts is to insert echo
statements throughout your code. This can help you identify where errors are occurring and what values are being passed to variables.
#!/bin/bash
# Your script goes here
echo "Value of variable x is: $x"
# More code goes here
Use the set -e
option
If you want your script to exit immediately when any command in the script fails, you can use the set -e
option. This option will cause Bash to exit with an error if any command in the script fails, making it easier to identify and fix errors in your script.
#!/bin/bash
set -e
# Your script goes here
Troubleshooting crons by verifying logs
We can troubleshoot crons using the log files. Logs are maintained for all the scheduled jobs. You can check and verify in logs if a specific job ran as intended or not.
For Ubuntu/Debian, you can find cron
logs at:
/var/log/syslog
The location varies for other distributions.
A cron job log file can look like this:
2022-03-11 00:00:01 Task started
2022-03-11 00:00:02 Running script /path/to/script.sh
2022-03-11 00:00:03 Script completed successfully
2022-03-11 00:05:01 Task started
2022-03-11 00:05:02 Running script /path/to/script.sh
2022-03-11 00:05:03 Error: unable to connect to database
2022-03-11 00:05:03 Script exited with error code 1
2022-03-11 00:10:01 Task started
2022-03-11 00:10:02 Running script /path/to/script.sh
2022-03-11 00:10:03 Script completed successfully
Conclusion
In this article, we started with how to access the terminal and then ran some basic bash commands. We also studied what a bash shell is. We briefly looked at branching the code using loops and conditionals. Finally, we discussed automating the scripts using cron followed by some troubleshooting techniques.
Resources for learning more about Bash scripting
If you want to dig deeper into the world of bash scripting, I would suggest you have a look at this 6-hour course on Linux at freeCodeCamp.
What’s your favorite thing you learned from this tutorial? You can also connect with me on any of these platforms. 📧�
See you in the next tutorial, happy coding 😁
Banner image credits: Image by Freepik
Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started