Bash для настоящих мужиков

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

Итак, приступим к изучению командной оболочки Bash.
Давайте начнем наше обучение с простого скрипта "Hello World".

1. Bash-скрипт Hello World
Для начала необходимо выяснить, где находится транслятор. Для этого введите следующую команду:
$ which bash

Откройте ваш любимый текстовый редактор и создайте файл под названием hello_world.sh. В этом файле сохраните следующие строки:
#!/bin/bash
# объявите переменную типа STRING
STRING="Hello World"
#выведите переменную на экран
echo $STRING

Примечание. Каждый bash-скрипт в этом тьюториале начинается с последовательности знаков "#!", который не интерпретируется как комментарий!
Перейдите в директорию, в которую вы сохранили файл hello_world.sh и сделайте этот файл исполняемым:
$ chmod +x hello_world.sh 

Ну, теперь вы полностью готовы создать свой первый bash-скрипт:
./hello_world.sh 


2. Простой bash-скрипт для резервного копирования:
#!/bin/bash
tar -czf myhome_directory.tar.gz /home/linuxconfig


3. Переменные
В этом примере мы объявим простую bash-переменную и выведем ее на экран с помощью команды echo.
#!/bin/bash
 STRING="HELLO WORLD!!!"
 echo $STRING 

Скрипт для резервной копии и переменных:
#!/bin/bash
 OF=myhome_directory_$(date +%Y%m%d).tar.gz
 tar -czf $OF /home/linuxconfig


3.1. Глобальные и локальные переменные
#!/bin/bash
#Объявление глобальной переменной
#Эта переменная глобальна и может быть использована в любом месте скрипта
VAR="global variable"
function bash {
#Объявление локальной переменной
#Эта переменная локальна и может быть использована только в этой функции bash
local VAR="local variable"
echo $VAR
}
echo $VAR
bash
# Обратите внимание, глобальная переменная не изменилась
# "local" это зарезервированное слово в bash
echo $VAR


4. Передача аргументов в bash-скрипт
#!/bin/bash
# используйте ранее определенные переменные для доступа к переданным аргументам to access passed arguments
# аргументы на экран
echo $1 $2 $3 ' -> echo $1 $2 $3'

# Мы можем сохранить аргументы из командной строки bash в специальный массив
args=("$@")
# echo arguments to the shell
echo ${args[0]} ${args[1]} ${args[2]} ' -> args=("$@"); echo ${args[0]} ${args[1]} ${args[2]}'

# используйте $@ для вывода сразу всех аргументов
echo $@ ' -> echo $@'

# используйте переменную $# для вывода
# числа переданных в bash-скрипт переменных
echo Number of arguments passed: $# ' -> echo Number of arguments passed: $#' 
/arguments.sh Bash Scripting Tutorial 


5. Исполнение команд shell с помощью bash
#!/bin/bash
# используйте обратные кавычки " ` ` " для исполнения команд shell
echo `uname -o`
# исполнение bash-команд вызывается без кавычек
echo uname -o 


6. Чтение введенных пользователем данных
#!/bin/bash
 
echo -e "Hi, please type the word: \c "
read  word
echo "The word you entered is: $word"
echo -e "Can you please enter two words? "
read word1 word2
echo "Here is your input: \"$word1\" \"$word2\""
echo -e "How do you feel about bash scripting? "
# команда read сохраняет ответы в дефолтную переменную$REPLY
read
echo "You said $REPLY, I'm glad to hear that! "
echo -e "What are your favorite colours ? "
# -a позволяет команде read сохранять несколько значений в массив
read -a colours
echo "My favorite colours are also ${colours[0]}, ${colours[1]} and ${colours[2]}:-)" 


7. Bash-команда trap
#!/bin/bash
# команда trap
trap bashtrap INT
# bash-команда очистки экрана
clear;
# bash функция trap исполняется, когда нажимается CTRL-C
# bash выводит сообщение => Executing bash trap !
bashtrap()
{
    echo "CTRL+C Detected !...executing bash trap !"
}
# для значений от 1/10 до 10/10
for a in `seq 1 10`; do
    echo "$a/10 to Exit." 
    sleep 1;
done
echo "Exit Bash Trap Example!!!" 


8. Массивы
8.1. Объявление простого bash-массива
#!/bin/bash
# объявление массива из 3 элементов
ARRAY=( 'Debian Linux' 'Redhat Linux' 'Ubuntu Linux' )
# получение числа элементов массива
ELEMENTS=${#ARRAY[@]}

# вывод каждого элемента массива 
# при заданном диапазоне номеров элементов
for (( i=0;i<$ELEMENTS;i++)); do
    echo ${ARRAY[${i}]}
done 


8.2. Импорт файла в массив bash
#!/bin/bash
# объявление массива
declare -a ARRAY
# вывести filedescriptor 10 в stdin
exec 10<&0
# stdin заменяется файлом, указанным в качестве первого аргумента
exec < $1
let count=0

while read LINE; do

    ARRAY[$count]=$LINE
    ((count++))
done

echo Number of elements: ${#ARRAY[@]}
# вывод содержимого массива
echo ${ARRAY[@]}
# вывод stdin из filedescriptor 10
# и закрыть filedescriptor 10
exec 0<&10 10<&-


Исполнение bash-скрипта с выводом в файл execution with an output:
linuxconfig.org $ cat bash.txt 
Bash
Scripting
Tutorial
Guide
linuxconfig.org $ ./bash-script.sh bash.txt 
Number of elements: 4
Bash Scripting Tutorial Guide
linuxconfig.org $ 


9. Bash-скрипты с if / else / fi
9.1. Простое Bash-выражение с if/else
Обратите внимание на пробелы в скобках [ и ]! Без этих пробелов, скрипт работать не будет.
#!/bin/bash
directory="./BashScripting"

# bash проверка существования директории
if [ -d $directory ]; then
	echo "Directory exists"
else 
	echo "Directory does not exists"
fi 


9.2. Встроенный if/else
#!/bin/bash
 
# объявление выбранной переменной и присваивание ей значения 4
choice=4
# Вывод на stdout
 echo "1. Bash"
 echo "2. Scripting"
 echo "3. Tutorial"
 echo -n "Please choose a word [1,2 or 3]? "
# Повторение цикла, пока переменная равна 4
# bash цикл while 
while [ $choice -eq 4 ]; do
 
# чтение введенного значения
read choice
# bash nested if/else
if [ $choice -eq 1 ] ; then
 
        echo "You have chosen word: Bash"

else                   

        if [ $choice -eq 2 ] ; then
                 echo "You have chosen word: Scripting"
        else
         
                if [ $choice -eq 3 ] ; then
                        echo "You have chosen word: Tutorial"
                else
                        echo "Please make a choice between 1-3 !"
                        echo "1. Bash"
                        echo "2. Scripting"
                        echo "3. Tutorial"
                        echo -n "Please choose a word [1,2 or 3]? "
                        choice=4
                fi   
        fi
fi
done 


10. Bash сравнения
10.1. Арифметические сравнения
-lt <
-gt >-le <=
-ge >=
-eq ==
-ne !=

#!/bin/bash
# объявление целочисленных переменных
NUM1=2
NUM2=2
if [ $NUM1 -eq $NUM2 ]; then
	echo "Both Values are equal"
else 
	echo "Values are NOT equal"
fi 


#!/bin/bash
# объявление целочисленных переменных
NUM1=2
NUM2=1
if [ $NUM1 -eq $NUM2 ]; then
	echo "Both Values are equal"
else 
	echo "Values are NOT equal"
fi 


#!/bin/bash
# объявление целочисленных переменных
NUM1=2
NUM2=1
if   [ $NUM1 -eq $NUM2 ]; then
	echo "Both Values are equal"
elif [ $NUM1 -gt $NUM2 ]; then
	echo "NUM1 is greater then NUM2"
else 
	echo "NUM2 is greater then NUM1"
fi 


10.2. Сравнение строк
= equal
!= not equal
< less then
> greater then
-n s1 string s1 is not empty
-z s1 string s1 is empty

#!/bin/bash
#Объявление строки S1
S1="Bash"
#Объявление строки S2
S2="Scripting"
if [ $S1 = $S2 ]; then
	echo "Both Strings are equal"
else 
	echo "Strings are NOT equal"
fi 


#!/bin/bash
#Объявление строки S1
S1="Bash"
#Объявление строки S2
S2="Bash"
if [ $S1 = $S2 ]; then
	echo "Both Strings are equal"
else 
	echo "Strings are NOT equal"
fi 


11. Bash проверка файла
-b filename - блокирование файла
-c filename - файл содержит спец. символы
-d directoryname - проверка существования директории
-e filename - проверка существования файла
-f filename - проверка существования НЕдиректории
-G filename Check if file exists and is owned by effective group ID.
-g filename true if file exists and is set-group-id.
-k filename Sticky bit
-L filename Symbolic link
-O filename True if file exists and is owned by the effective user id.
-r filename - проверка доступности файла
-S filename Check if file is socket
-s filename - проверка того, что размер файла больше нуля
-u filename Check if file set-ser-id bit is set
-w filename - проверка перезаписываемости файла
-x filename - проверка исполняемости файла
#!/bin/bash
file="./file"
if [ -e $file ]; then
	echo "File exists"
else 
	echo "File does not exists"
fi 

Аналогично для проверки существования файла можно использоватьe цикл while. Этот скрипт не будет работать, пока файл существует. Обратите внимание на bash оператор отрицания "!", который отрицает функцию -e.
#!/bin/bash
 
while [ ! -e myfile ]; do
# Не реагировать, пока файл создан и существует
sleep 1
done 


12. Цикл
12.1. Bash команда зацикливания
#!/bin/bash

# bash цикл
for f in $( ls /var/ ); do
	echo $f
done 

Запуск цикла из командной строки bash:
$ for f in $( ls /var/ ); do echo $f; done 


12.2. Bash цикл while
#!/bin/bash
COUNT=6
# bash цикл while 
while [ $COUNT -gt 0 ]; do
	echo Value of count is: $COUNT
	let COUNT=COUNT-1
done 


12.3. Bash цикл until
#!/bin/bash
COUNT=0
# bash цикл until
until [ $COUNT -gt 5 ]; do
        echo Value of count is: $COUNT
        let COUNT=COUNT+1
done 


12.4. Контролирование цикла bash
Ниже представлен пример цикла while, контролируемого стандартным вводом. Bash цикл while работает, пока существует переход от STDOUT к STDIN и к команде read.
#!/bin/bash
# этот bash скрипт найдет и заменит пробелы
# в именах файлов
DIR="."
# контролирование цикла с помощью bash команды read путем перенаправления STDOUT к
# STDIN в цикле while
# команда find не будет отбрасывать имена файлов, содержащие пробелы
find $DIR -type f | while read file; do
# использование POSIX class [:space:] для поиска пробела в имени файла
if [[ "$file" = *[[:space:]]* ]]; then
# замена пробела символом "_" и, следовательно, переименование файла
mv "$file" `echo $file | tr ' ' '_'`
fi;
# конец цикла while
done 


13. Функции Bash
#!/bin/bash
# функции Bash могут объявляться в любом порядке
function function_B {
        echo Function B.
}
function function_A {
        echo $1
}
function function_D {
        echo Function D.
}
function function_C {
        echo $1
}
# вызов функций
# передача параметра функции А
function_A "Function A."
function_B
# передача параметра функции С
function_C "Function C."
function_D 


14. Bash выделение
#!/bin/bash
 
PS3='Choose one word: ' 

# bash select
select word in "linux" "bash" "scripting" "tutorial" 
do
  echo "The word you have selected is: $word"
# остановка цикла, иначе он станет бесконечным
  break  
done

exit 0 


15. Переключатель
#!/bin/bash
echo "What is your preferred programming / scripting language"
echo "1) bash"
echo "2) perl"
echo "3) python"
echo "4) c++"
echo "5) I do not know !"
read case;
# структура простого bash условия
# обратите внимание: в этом случае $case -это переменная, которая не обязана так называться
# здесь она названа так для примера
case $case in
    1) echo "You selected bash";;
    2) echo "You selected perl";;
    3) echo "You selected python";;
    4) echo "You selected c++";;
    5) exit
esac 


16. Кавычки в Bash
Кавычки являются важным элементом языка bash и bash скриптов. Ниже вы найдете некоторые основы использования кавычек в bash.
16.1. Экранирование специальных символов
Перед тем, как мы приступим к использованию кавычек, нам необходимо узнать, как экранировать специальные символы. Эта процедура позволит bash напрямую распознать значение этих символов. Для этого нам нужно использовать обратный слеш "\". Например:
#!/bin/bash
 
# объявление bash переменной типа string 
BASH_VAR="Bash Script"

# вывод переменной BASH_VAR
echo $BASH_VAR

# если спецсимвол "$" экранирован символом "\", он может быть напрямую распознан 
echo \$BASH_VAR 

# у обратного слеша также есть специальное значение, и оно может быть раскрыто с помощью еще одного слеша "\"
echo "\\" 


16.2. Одинарные кавычки
Одинарные кавычки в bash экранируют значение всех специальных символов. Таким образом, специальные символы будут распознаны буквально. При этом нельзя использовать одинарную кавычку внутри двух дургих таких же кавычек, даже если какая-то из них экранирована обратным слешем.
#!/bin/bash
 
 # объявление bash переменной типа string
 BASH_VAR="Bash Script"
 
 # вывод переменной BASH_VAR
 echo $BASH_VAR
 
 # значение спец. символов в bash экранируется при использовании одинарных кавычек 
 echo '$BASH_VAR  "$BASH_VAR"' 


16.3. Двойные кавычки
Двойные кавычки используются в bash для экранирования всех специальных символов, кроме "$", "\" и "`". Все остальные спец. символы будут распознаваться буквально. В отличие от предыдущего пункта, внутри двойных кавычек можно использовать одинарную. Если между двойными кавычками нужно использовать еще одну двойную кавычку, можно заставить bash распознать их буквально, используя обратный слеш "\". Например:
#!/bin/bash
 
# объявление bash переменной типа string
BASH_VAR="Bash Script"

# echo variable BASH_VAR
echo $BASH_VAR

# спец. символ и его значение экранируется 
# при использовании двойных кавычек вместно "$", "\" и "`"

echo "It's $BASH_VAR  and \"$BASH_VAR\" using backticks: `date`" 


16.4. Bash экранирование ANSI-C
В bash существует также еще один вид экранирования - ANSI-C. При использовании этого типа экранирования, символы, обозначенные обратным слешем "\" получают специальное значение в соответствии со стандартом ANSI-C.
\a alert (bell)
\b backspace
\e an escape character
\f form feed
\n newline
\r carriage return
\t horizontal tab
\v vertical tab
\\ backslash
\` single quote
\nnn octal value of characters ( see [http://www.asciitable.com/ ASCII table] )
\xnn hexadecimal value of characters ( see [http://www.asciitable.com/ ASCII table] )

Обозначение ansi-c bash-экранирования: $'' . Пример:
#!/bin/bash
 
# для примера мы использовали \n для новой строки, \x40 для шестнадцатеричного значения @
# и \56 для восьмеричного значения .
echo $'web: www.linuxconfig.org\nemail: web\x40linuxconfig\56org' 


17. Арифметические операции
17.1. Пример сложения с помощью bash
#!/bin/bash
 
let RESULT1=$1+$2
echo $1+$2=$RESULT1 ' -> # let RESULT1=$1+$2'
declare -i RESULT2
RESULT2=$1+$2
echo $1+$2=$RESULT2 ' -> # declare -i RESULT2; RESULT2=$1+$2'
echo $1+$2=$(($1 + $2)) ' -> # $(($1 + $2))' 


17.2. Арифметика в bash
#!/bin/bash
 
echo '### let ###'
# bash сложение
let ADDITION=3+5
echo "3 + 5 =" $ADDITION

# bash вычитание
let SUBTRACTION=7-8
echo "7 - 8 =" $SUBTRACTION 

# bash умножение
let MULTIPLICATION=5*8
echo "5 * 8 =" $MULTIPLICATION

# bash деление
let DIVISION=4/2
echo "4 / 2 =" $DIVISION

# bash modulus
let MODULUS=9%4
echo "9 % 4 =" $MODULUS

# bash возведение в степень
let POWEROFTWO=2**2
echo "2 ^ 2 =" $POWEROFTWO


echo '### Bash Arithmetic Expansion ###'
# существуют два формата обозначения арифметических выражений: $[ выражение ] 
# и $(( выражение #)) - вы можете выбирать тот, который вам нравится больше

echo 4 + 5 = $((4 + 5))
echo 7 - 7 = $[ 7 - 7 ]
echo 4 x 6 = $((3 * 2))
echo 6 / 3 = $((6 / 3))
echo 8 % 7 = $((8 % 7))
echo 2 ^ 8 = $[ 2 ** 8 ]

echo '### Declare ###'
echo -e "Please enter two numbers \c"
# распознавание ввода
read num1 num2
declare -i result
result=$num1+$num2
echo "Result is:$result "

# bash конвертация бинарного числа 10001
result=2#10001
echo $result

# bash конвертация восьмеричного числа 16
result=8#16
echo $result

# bash конвертация шестнадцатеричного числа 0xE6A
result=16#E6A
echo $result 


17.3. Округление числа с плавающей точкой
#!/bin/bash
# получение числа с плавающей точкой
floating_point_number=3.3446
echo $floating_point_number
# округление числа с плавающей точкой с помощью bash
for bash_rounded_number in $(printf %.0f $floating_point_number); do
echo "Rounded number with bash:" $bash_rounded_number
done 


17.4. Bash вычисления с плавающей точкой
#!/bin/bash
# реализация простого калькулятора в linux на bash 
echo "Enter input:" 
read userinput
# вывод результата с двумя знаками после запятой
echo "Result with 2 digits after decimal point:" 
echo "scale=2; ${userinput}" | bc 
# вывод результата с десятью знаками после запятой
echo "Result with 10 digits after decimal point:"
echo "scale=10; ${userinput}" | bc 
# вывод результата в виде округленного целого числа
echo "Result as rounded integer:"
echo $userinput | bc 


18. Перенаправления
18.1. STDOUT из bash скрипта в STDERR
#!/bin/bash
 
 echo "Redirect this STDOUT to STDERR" 1>&2 

Для проверки перенаправления STDOUT в STDERR можно перенаправить вывод скрипта в файл:

18.2. STDERR из bash скрипта в STDOUT
#!/bin/bash
 
 cat $1 2>&1 

Для проверки перенаправления STDERR iв STDOUT можно перенаправить вывод скрипта в файл:

18.3. STDOUT на экран
Простым способом перенаправления STDOUT является простое использование любой команды, поскольку по умолчанию STDOUT автоматически выводится на экран. Но для начала создайте файл "file1":
$ touch file1
$ ls file1 
file1

Как вы можете увидеть из вышеприведенного примера, выполнение команды ls вызывает STDOUT, который по умолчанию выводится на экран.

18.4. STDOUT в файл
Чтобы блокировать дефолтное поведение STDOUT, мы можем использовать ">" для перенаправления вывода в файл:
$ ls file1 > STDOUT
$ cat STDOUT 
file1


18.5. STDERR в файл
По умолчанию, STDERR выводится на экран:
$ ls
file1  STDOUT
$ ls file2
ls: cannot access file2: No such file or directory

В следующем примере перенаправим стандартную ошибку ( stderr ) в файл, а stdout - как по умолчанию, на экран. Имейте в виду, что STDOUT выводится на экран, а STDERR записывается в файл под наазванием STDERR:
$ ls
file1  STDOUT
$ ls file1 file2 2> STDERR
file1
$ cat STDERR 
ls: cannot access file2: No such file or directory


18.6. STDOUT в STDERR
Также возможно перенаправление STDOUT и STDERR в один файл. В следующем примере перенаправим STDOUT в тот же дескриптор, что и STDERR. И STDOUT, и STDERR будут перенаправлены в файл "STDERR_STDOUT".
$ ls
file1  STDERR  STDOUT
$ ls file1 file2 2> STDERR_STDOUT 1>&2
$ cat STDERR_STDOUT
ls: cannot access file2: No such file or directory
file1

Теперь файл STDERR_STDOUT содержит и STDOUT, и STDERR.

18.7. STDERR to STDOUT
Пример выше можно переделать так, чтобы перенаправлять STDERR в тот же дескриптор, что и SDTOUT:
$ ls
file1  STDERR  STDOUT
$ ls file1 file2 > STDERR_STDOUT 2>&1
$ cat STDERR_STDOUT 
ls: cannot access file2: No such file or directory
file1


18.8. STDERR и STDOUT в файл
Оба верхних примера перенаправляли STDOUT и STDERR в файл. Существует и другой способ добиться того же эффекта:
$ ls
file1  STDERR  STDOUT
$ ls file1 file2 &> STDERR_STDOUT
$ cat STDERR_STDOUT 
ls: cannot access file2: No such file or directory
file1

or
ls file1 file2 >& STDERR_STDOUT
$ cat STDERR_STDOUT 
ls: cannot access file2: No such file or directory
file1



Данная статья является любительским переводом статьи с linuxconfig.org.




Вас также может заинтересовать:

Интересные приемы программирования на Bash (Advanced bash scripting)
5 советов для работы с историей в Bash
13.07.2010 Bash-cкрипт для бекапирования хостинга (файлы+бд)