zloy-zhake
8/28/2017 - 5:17 PM

Заметки про Bash

Заметки про Bash

Заметки из https://habrahabr.ru/company/ruvds/blog/325522/

Bash-скрипты: начало

----------

#!/usr/bin/env bash

командная строка позволяет выполнить несколько команд за один раз, введя их через точку с запятой:
pwd ; whoami

Подстановка команд
- С помощью значка обратного апострофа «`» - mydir=`pwd`
- С помощью конструкции $() - mydir=$(pwd)

Математические операции
$((a+b))

if условие
then
	команды
elif условие2
then
	команды
else
	команды
fi

Сравнение чисел
n1 -eq n2 Возвращает истинное значение, если n1 равно n2.
n1 -ge n2 Возвращает истинное значение, если n1 больше или равно n2.
n1 -gt n2 Возвращает истинное значение, если n1 больше n2.
n1 -le n2 Возвращает истинное значение, если n1 меньше или равно n2.
n1 -lt n2 Возвращает истинное значение, если n1 меньше n2.
n1 -ne n2 Возвращает истинное значение, если n1 не равно n2.

if [ $val1 -gt 5 ]
then
	...

Сравнение строк
str1 = str2 Проверяет строки на равенство, возвращает истину, если строки идентичны.
str1 != str2 Возвращает истину, если строки не идентичны.
str1 < str2 Возвращает истину, если str1 меньше, чем str2.
str1 > str2 Возвращает истину, если str1 больше, чем str2.
-n str1 Возвращает истину, если длина str1 больше нуля.
-z str1 Возвращает истину, если длина str1 равна нулю.

операторы «>» и «<» необходимо экранировать с помощью обратной косой черты, иначе скрипт будет работать неправильно, хотя сообщений об ошибках и не появится. Скрипт интерпретирует знак «>» как команду перенаправления вывода.

if [ $val1 \> $val2 ]
then
	...

Проверки файлов
-d file Проверяет, существует ли файл, и является ли он директорией.
-e file Проверяет, существует ли файл.
-f file Проверяет, существует ли файл, и является ли он файлом.
-r file Проверяет, существует ли файл, и доступен ли он для чтения.
-s file Проверяет, существует ли файл, и не является ли он пустым.
-w file Проверяет, существует ли файл, и доступен ли он для записи.
-x file Проверяет, существует ли файл, и является ли он исполняемым.
file1 -nt file2 Проверяет, новее ли file1, чем file2.
file1 -ot file2 Проверяет, старше ли file1, чем file2.
-O file Проверяет, существует ли файл, и является ли его владельцем текущий пользователь.
-G file Проверяет, существует ли файл, и соответствует ли его идентификатор группы идентификатору группы текущего пользователя.

==========

Bash-скрипты, часть 2: циклы

----------

for var in list
do
	команды
done

Инициализация цикла списком, полученным из результатов работы команды
for var in $(cat $file)
do
	echo " $var"
done

переменная окружения IFS (Internal Field Separator) позволяет указывать разделители полей
IFS=$'\n'
IFS=:

for (( a = 1; a < 10; a++ ))

while команда проверки условия
do
	другие команды
done

Данные, выводимые в цикле, можно обработать, либо перенаправив вывод, либо передав их в конвейер. Делается это с помощью добавления команд обработки вывода после инструкции done.
Например, вместо того, чтобы показывать на экране то, что выводится в цикле, можно записать всё это в файл или передать ещё куда-нибудь:
for ...
	...
done > myfile.txt

==========

Bash-скрипты, часть 3: параметры и ключи командной строки 

----------

$0 — имя скрипта.
$1 — первый параметр.
$2 — второй параметр — и так далее, вплоть до переменной $9, в которую попадает девятый параметр.
Если скрипту надо больше девяти параметров, при обращении к ним номер в имени переменной надо заключать в фигурные скобки, например так:
${10}

переменная $# содержит количество параметров, переданных сценарию при вызове

Эта переменная даёт необычный способ получения последнего из переданных скрипту параметров, не требующий знания их количества. Вот как это выглядит:
echo The last parameter was ${!#}

Захват всех параметров командной строки
- Переменная $* содержит все параметры, введённые в командной строке, в виде единого «слова».
- В переменной $@ параметры разбиты на отдельные «слова». Эти параметры можно перебирать в циклах. 

Команда shift, по умолчанию, сдвигает значения позиционных параметров влево. Например, значение переменной $3 становится значением переменной $2, значение $2 переходит в $1, а то, что было до этого в $1, теряется. Обратите внимание на то, что при этом значение переменной $0, содержащей имя скрипта, не меняется.

case "$1" in
	-a) echo "Found the -a option" ;;
	-b) echo "Found the -b option" ;;
	-c) echo "Found the -c option" ;;
	*) echo "$1 is not an option" ;;
esac

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

"Стандартные ключи"
-a Вывести все объекты.
-c Произвести подсчёт.
-d Указать директорию.
-e Развернуть объект.
-f Указать файл, из которого нужно прочитать данные.
-h Вывести справку по команде.
-i Игнорировать регистр символов.
-l Выполнить полноформатный вывод данных.
-n Использовать неинтерактивный (пакетный) режим.
-o Позволяет указать файл, в который нужно перенаправить вывод.
-q Выполнить скрипт в quiet-режиме.
-r Обрабатывать папки и файлы рекурсивно.
-s Выполнить скрипт в silent-режиме.
-v Выполнить многословный вывод.
-x Исключить объект.
-y Ответить «yes» на все вопросы.

команда read позволяет принимать введённые данные либо со стандартного ввода (с клавиатуры), либо используя другие дескрипторы файлов. После получения данных, эта команда помещает их в переменную:
echo -n "Enter your name: "
read name
echo "Hello $name, welcome to my program."

команда echo, которая выводит приглашение, вызывается с ключом -n. Это приводит к тому, что в конце приглашения не выводится знак перевода строки, что позволяет пользователю скрипта вводить данные там же, где расположено приглашение, а не на следующей строке.

При вызове read можно указывать и несколько переменных:
read -p "Enter your name: " first last

Если, вызвав read, не указывать переменную, данные, введённые пользователем, будут помещены в специальную переменную среды REPLY:
read -p "Enter your name: "
echo Hello $REPLY, welcome to my program.

Если скрипт должен продолжать выполнение независимо от того, введёт пользователь какие-то данные или нет, вызывая команду read можно воспользоваться ключом -t. А именно, параметр ключа задаёт время ожидания ввода в секундах:
if read -t 5 -p "Enter your name: " name
then
	echo "Hello $name, welcome to my script"
else
	echo "Sorry, too slow! "
fi

Иногда то, что вводит пользователь в ответ на вопрос скрипта, лучше на экране не показывать. Например, так обычно делают, запрашивая пароли. Ключ -s команды read предотвращает отображение на экране данных, вводимых с клавиатуры. На самом деле, данные выводятся, но команда read делает цвет текста таким же, как цвет фона.
read -s -p "Enter your password: " pass
echo "Is your password really $pass? "

Команда read может, при каждом вызове, читать одну строку текста из файла. Когда в файле больше не останется непрочитанных строк, она просто остановится. Если нужно получить в скрипте всё содержимое файла, можно, с помощью конвейера, передать результаты вызова команды cat для файла, конструкции while, которая содержит команду read
cat myfile | while read line
do
	...
done

==========

Bash-скрипты, часть 4: ввод и вывод

----------

Стандартные дескрипторы файлов
Каждому процессу позволено иметь до девяти открытых дескрипторов файлов. Оболочка bash резервирует первые три дескриптора с идентификаторами 0, 1 и 2. Вот что они означают.
0, STDIN — стандартный поток ввода.
1, STDOUT — стандартный поток вывода.
2, STDERR — стандартный поток ошибок.

STDERR, и STDOUT можно перенаправить в один и тот же файл, воспользовавшись командой &>

Временное перенаправление вывода
В скрипте можно перенаправить вывод отдельной строки в STDERR. Для того, чтобы это сделать, достаточно использовать команду перенаправления, указав дескриптор STDERR, при этом перед номером дескриптора надо поставить символ амперсанда (&):
echo "This is an error" >&2
echo "This is normal output"

Постоянное перенаправление вывода
Если в скрипте нужно перенаправлять много выводимых на экран данных, добавлять соответствующую команду к каждому вызову echo неудобно. Вместо этого можно задать перенаправление вывода в определённый дескриптор на время выполнения скрипта, воспользовавшись командой exec:
exec 1>outfile
echo "This is a test of redirecting all output"
echo "from a shell script to another file."
echo "without having to redirect every line"

Команду exec можно использовать не только в начале скрипта, но и в других местах

Перенаправление ввода в скриптах
Для перенаправления ввода можно воспользоваться той же методикой, которую мы применяли для перенаправления вывода. Например, команда exec позволяет сделать источником данных для STDIN какой-нибудь файл:
exec 0< myfile
Эта команда указывает оболочке на то, что источником вводимых данных должен стать файл myfile, а не обычный STDIN.

Создание собственного перенаправления вывода
Перенаправляя ввод и вывод в сценариях, вы не ограничены тремя стандартными дескрипторами файлов. Как уже говорилось, можно иметь до девяти открытых дескрипторов. Остальные шесть, с номерами от 3 до 8, можно использовать для перенаправления ввода или вывода. Любой из них можно назначить файлу и использовать в коде скрипта.
Назначить дескриптор для вывода данных можно, используя команду exec:
exec 3>myfile
echo "This should display on the screen"
echo "and this should be stored in the file" >&3
echo "And this should be back on the screen"

Создание дескрипторов файлов для ввода данных
Перенаправить ввод в скрипте можно точно так же, как и вывод. Сохраните STDIN в другом дескрипторе, прежде чем перенаправлять ввод данных.
После окончания чтения файла можно восстановить STDIN и пользоваться им как обычно:
exec 6<&0
exec 0< myfile
...
exec 0<&6

Закрытие дескрипторов файлов
Оболочка автоматически закрывает дескрипторы файлов после завершения работы скрипта. Однако, в некоторых случаях нужно закрывать дескрипторы вручную, до того, как скрипт закончит работу. Для того, чтобы закрыть дескриптор, его нужно перенаправить в &-. Выглядит это так:
exec 3> myfile
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3

Будьте внимательны, закрывая дескрипторы файлов в сценариях. Если вы отправляли данные в файл, потом закрыли дескриптор, потом — открыли снова, оболочка заменит существующий файл новым. То есть всё то, что было записано в этот файл ранее, будет утеряно.

Для того, чтобы получить список всех открытых в Linux дескрипторов, можно воспользоваться командой lsof. Во многих дистрибутивах, вроде Fedora, утилита lsof находится в /usr/sbin. Эта команда весьма полезна, так как она выводит сведения о каждом дескрипторе, открытом в системе. Сюда входит и то, что открыли процессы, выполняемые в фоне, и то, что открыто пользователями, вошедшими в систему.
У этой команды есть множество ключей, рассмотрим самые важные.
-p Позволяет указать ID процесса.
-d Позволяет указать номер дескриптора, о котором надо получить сведения.
Для того, чтобы узнать PID текущего процесса, можно использовать специальную переменную окружения $$, в которую оболочка записывает текущий PID.
Ключ -a используется для выполнения операции логического И над результатами, возвращёнными благодаря использованию двух других ключей:
lsof -a -p $$ -d 0,1,2

Подавление вывода
Иногда надо сделать так, чтобы команды в скрипте, который, например, может исполняться как фоновый процесс, ничего не выводили на экран. Для этого можно перенаправить вывод в /dev/null. Это — что-то вроде «чёрной дыры».
Вот, например, как подавить вывод сообщений об ошибках:
ls -al badfile anotherfile 2> /dev/null

==========

Bash-скрипты, часть 5: сигналы, фоновые задачи, управление сценариями

----------

Перехват сигналов
Для того, чтобы включить в скрипте отслеживание сигналов Linux, используется команда trap. Если скрипт получает сигнал, указанный при вызове этой команды, он обрабатывает его самостоятельно, при этом оболочка такой сигнал обрабатывать не будет.
Команда trap позволяет скрипту реагировать на сигналы, в противном случае их обработка выполняется оболочкой без его участия.
Рассмотрим пример, в котором показано, как при вызове команды trap задаётся код, который надо выполнить, и список сигналов, разделённых пробелами, которые мы хотим перехватить. В данном случае это всего один сигнал:
trap "echo ' Trapped Ctrl-C'" SIGINT

Перехватить сигнал выхода из скрипта можно, использовав при вызове команды trap имя сигнала EXIT:
trap "echo Goodbye..." EXIT

Перехват сигналов можно и отменить, для этого достаточно выполнить команду trap, передав ей двойное тире и имя сигнала:
trap -- SIGINT

Выполнение скриптов, не завершающих работу при закрытии терминала
Скрипты можно выполнять в фоновых процессах даже после выхода из терминальной сессии. Для этого можно воспользоваться командой nohup. Эта команда позволяет запустить программу, блокируя сигналы SIGHUP, отправляемые процессу. В результате процесс будет исполняться даже при выходе из терминала, в котором он был запущен.
nohup ./myscript &
Команда nohup отвязывает процесс от терминала. Это означает, что процесс потеряет ссылки на STDOUT и STDERR. Для того, чтобы не потерять данные, выводимые скриптом, nohup автоматически перенаправляет сообщения, поступающие в STDOUT и в STDERR, в файл nohup.out.
Обратите внимание на то, что при запуске нескольких скриптов из одной и той же директории то, что они выводят, попадёт в один файл nohup.out.

==========

Bash-скрипты, часть 6: функции и разработка библиотек

----------

Функцию можно объявить так:
function Name {
}
Или так:
function Name() {
}

Команда return позволяет задавать возвращаемый функцией целочисленный код завершения.
function myfunc {
	read -p "Enter a value: " value
	echo "adding value"
	return $(( $value + 10 ))
}
myfunc
echo "The new value is $?"
То, что возвратила функция, выводится командой echo с использованием переменной $?.
Если вы выполните любую другую команду до извлечения из переменной $? значения, возвращённого функцией, это значение будет утеряно. Дело в том, что данная переменная хранит код возврата последней выполненной команды.
Учтите, что максимальное число, которое может вернуть команда return — 255.

Ещё один способ возврата результатов работы функции заключается в записи данных, выводимых функцией, в переменную. Такой подход позволяет обойти ограничения команды return и возвращать из функции любые данные
function myfunc {
	read -p "Enter a value: " value
	echo $(( $value + 10 ))
}
result=$( myfunc)
echo "The value is $result"

Аргументы передают функции, записывая их после её имени:
myfunc $val1 10 20

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

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

Глобальные переменные — это переменные, которые видны из любого места bash-скрипта. Если вы объявили глобальную переменную в основном коде скрипта, к такой переменной можно обратиться из функции.
Почти то же самое справедливо и для глобальных переменных, объявленных в функциях. Обращаться к ним можно и в основном коде скрипта после вызова функций.
По умолчанию все объявленные в скриптах переменные глобальны. Так, к переменным, объявленным за пределами функций, можно без проблем обращаться из функций

Переменные, которые объявляют и используют внутри функции, могут быть объявлены локальными. Для того, чтобы это сделать, используется ключевое слово local перед именем переменной

Чтобы передать функцие массив в качестве аргумента из массива надо извлечь имеющиеся в нём данные и передать их функции как самостоятельные аргументы. Если надо, внутри функции полученные ей аргументы можно снова собрать в массив:
function myfunc {
	local newarray
	newarray=("$@")
	echo "The new array value is: ${newarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[*]}"
myfunc ${myarray[*]}

Ключ к использованию библиотек — в команде source. Эта команда используется для подключения библиотек к скриптам. В результате функции, объявленные в библиотеке, становятся доступными в скрипте, в противном же случае функции из библиотек не будут доступны в области видимости других скриптов.
У команды source есть псевдоним — оператор «точка». Для того, чтобы подключить файл в скрипте, в скрипт надо добавить конструкцию такого вида:
. ./myscript

==========