Введение в программирование на Bash
Опубликовано в : 27-11-2009 | Автор : admin | В рубрике : Bash
0
Bash – это стандартная командная оболочка UNIX-систем (подробнее в wiki). Существует множество других оболочек, производных от Bash – csh, zsh, fish и другие. По сути нельзя сказать, что bash – «самостоятельный» язык (как php например). Bash надо рассматривать как совокупность утилит. Ну не будем много говорить, перейдем к делу.
1. Как запускать скрипт
Напишем стандартный первый скрипт Hello, world! ) И назовем его hello.sh
#!/bin/bash
echo Hello, world!
Первая строка – комментарий, но она указывает на программу, которой нужно обработать скрипт. Например если бы вы писали скрипт на питоне, то указали бы там #!/usr/bin/python. Бывают ситуации, когда для выполнения скрипта нужен не bash, а zsh или csh, тогда в этой строке прописываете нужный Вам интерпретатор. Что будет если эту строку убрать? Ничего страшного, просто тогда скрипт придется запускать так:
$ bash hello.sh
Тогда как с этой строкой достаточно набрать:
$ ./hello.sh
Многие спрашивают, зачем ставить ./? Отвечаю, пытаетесь запустить программу bash ищет её в директориях, указанных в переменной окружения PATH (наберите в консоли echo $PATH). Чтобы сказать Bash’у, что искать нужно в текущей директории ставится «./».
Вторая строка. Echo – стандартная утилита UNIX-систем, которая выводит на экран переданную ей строку.
Подведем итог. Что нужно сделать, чтобы запустить скрипт:
1. Сохраняем скрипт в файл hello.sh
2. Даем права на исполнение файлу chmod +x hello.sh
3. Исполняем ./hello.sh
2. Переменные
Переменные Bash не имеют типа, как в других языках программирования. По сути здесь все переменные строковые, но в зависимости от содержания Bash допускает целочисленные операции над ними. Обратите внимание, только целочисленные! Но об этом чуть ниже. Рассмотрим как инициализировать переменную:
#!/bin/bash
# Это комментарий
STR1=строка
STR2="вторая строка"
# STR2=вторая строка - так делать нельзя, bash начнет искать команду "строка"
DIG1=2
DIG2=12
let "DIG1++"
let "SUMM=$DIG1+$DIG2"
echo $SUMM
Пробелы до и после =, при присвоении значения переменной ставить нельзя. Чтобы получить значение переменной нужно использовать конструкцию $ИМЯ_ПЕРЕМЕННОЙ. Команда let позволяет производить вычисления в стиле C/C++.
3. Массивы
Bash поддерживает только одномерные массивы. Есть конечно махинации, с помощью которых можно эмулировать двумерные, но это все равно не очень удобно
Рассмотрим пример:
#!/bin/bash
# Объявляем массив1
ARR1=( 1 2 3 4 5 пять )
echo "${ARR1[0]} ${ARR1[1]}"
# Объявление второго массива
ARR2[0]="first"
ARR2[150]=19
echo "${ARR2[0]} ${ARR2[2]}"
echo "Длина массива ARR2 - ${#ARR2[@]}"
echo "Длина первого элемента массива ARR2 - ${#ARR2}"
# Объявление третьего массива, Формат - ( [индекс1]=значение1 [индекс2]=значение2 )
ARR3=( [0]="первый элемент" [4]="четвертый элемент" [9]=190 )
echo ${ARR3[4]}
Вывод скрипта:
1 2
first
Длина массива ARR2 – 2
Длина первого элемента массива ARR2 – 5
четвертый элемент
Из примера видно неприхотливость Bash по отношению к типам переменных. В первом случае мы явно задаем элементы массива. Для этого используется конструкция (), внутри скобок через пробел перечисляются элементы.
Во втором случае мы без явного объявления задаем элементы массива. Нумерация идет от нуля, отрицательные индексы не допускаются. Обращение к неинициализированному элементу не вызывает ошибку, просто вернется пустая строка.
Чтобы получить элемент массива нужно использовать фигурные скобки ${ИМЯ_МАССИВА[индекс]}. По поводу длины массива, нужно использовать конструкцию ${#ИМЯ_МАССИВА[@]} или ${#ИМЯ_МАССИВА[*]}, потому что ${#ИМЯ_МАССИВА} – указатель на первый элемент массива.
4. Проверка условий
В Bash все в точности как в других языках программирования, да наоборот. Ноль – истина, остальные значения – ложь. Почему так? Потому что код успешного завершения программы – ноль, остальные означают ошибку. В системе есть команды true и false, расположенные в /bin. Попробуйте вызвать такой скриптик:
#!/bin/bash
true
echo $?
false
echo $?
* $? содержит код завершения последней выполненной команды.
Формат условия простой:
if <команда/[ условие ]>
then
<тело>
elif <команда 2>; then
<тело 2>
else
<тело 3>
fi;
Elif и else – необязательные параметры. Можно писать then на одной строке с if, но тогда нужно поставить «;» после условия, например:
if [ 0 ]; then
echo True
else
echo False
fi;
При работе с условиями обязательно столкнетесь с командой test. [ - является её синонимом. Т.е. в конструкции
if [ -e /etc/passwd ]; then
cat /etc/passwd
fi;
Команда test принимает в качестве параметров -e /etc/passwd и возвращает 0 или 1 в зависимости от того, существует файл или нет. У этой команды много разных параметров, можно их посмотреть здесь – условия проверки файлов и здесь – операции сравнения.
В Bash также есть более продвинутая встроенная команда [[, она позволяет проверять условия как в языке Си.
#!/bin/bash
let "A=5*7-12"
let "B=4*6"
#if [[ "$A" > "$B" ]]; then – аналог следующей строки, но сравнивает ASCII-коды
if [ "$A" -gt "$B" ]; then
echo "${A} > ${B}"
else
echo "${A} <= ${B}"
fi;
Кстати забыл упомянуть, в условиях переменные нужно заключать в кавычки, чтобы предотвратить возможное разделение их на отдельные слова, мало ли что там будет в этой переменной. Т.е. иногда и без них может прокатить (например когда там числа), но лучше сразу взять для себя это за правило. Пробелы между скобками и самим условием также обязательны.
Еще один важный момент - операции <,>,= и другие будут сравнивать ASCII-символы. Попробуйте например сравнить так 3 и 10, 3 окажется больше. Чтобы сравнивать числа используется конструкция (( )), или -gt, -lt внутри [], т.е в предыдущем примере можно было написать:
if (( "$A" > "$B" )); then
5. Циклы
Цикл for в Bash похож на питоновский, C# и т.д. он тоже перебирает коллекции. Пример:
for i in 1 2 3 4 5 6; do
echo $i
done
for file in `ls /etc`
do
echo $file
done
Т.е. перебирать можно практически все, массивы тоже
Учтите, что если написать вместо ls /etc. ls -la /etc, то for не будет писать в переменную отдельные строки. Он воспринимает команды как массив, т.е. разбивает все по пробелам.
А что будет если написать вот так?
#!/bin/bash
for i
do
echo $i
done
Отвечаю: ничего
НО! Пока не зададите параметры скрипту. Когда for'у не говорят что перебирать, он берет переменную $@.
Цикл while и until
Формат:
while <условие>; do
<команды>
done
until <условие>; do
<команды>
done
While работает, пока условие истинно, а until пока ложно. Условие проверяется перед каждой итерацией, даже в until! Поэтому честно говоря не знаю зачем нужен until, для while можно поставить ! перед условием и все. Цикла с проверкой условия после выполнения тела в Bash нет.
В качестве условия можно подставлять все то же, что и в if.
Давайте попробуем написать скрипт вычисления факториала:
#!/bin/bash
if [ -z "$1" ]; then
echo "Передайте число в качестве первого параметра"
exit 1
fi
if (( "$1" < 0 )); then
echo "Число должно быть больше нуля!"
exit 1
fi
# Здесь будем хранить произведение чисел
FACT="1"
i=1
while (( "$i" < "$1" )); do
(( i++ ))
(( FACT *= i ))
# Размер числовой переменной у bash не очень большой, поэтому
# можно воспользоваться сторонней утилиткой bc
# FACT=$(echo "$FACT*$i" | bc -l)
done
echo "Факториал числа ${1} = ${FACT}"
Для начала думаю хватит
Советую почитать Advanced Bash-Scripting Guide, если уж решили изучать Bash. А я в следующий раз постараюсь написать что-нибудь более интересное, о всяких хаках и полезных методах написания скриптов.


