6 мая 2017

Конспект по bash

Если вы работаете в ИТ, то как никто знаете о том, как ценно время. Оптимизация рабочего процесса — один из важнейших аспектов работы в ИТ. Так или иначе, наша работа (будь то верстка сайта, написание модулей, или тестирования приложений) требует повторения одних и тех же действий: быстрые скриншоты с загрузкой на сервер, обработка выделенного текста, конвертация файлов, парсинг данных и многое другое. Чтобы не делать лишних действий, а сконцентрироваться на идее и самой сути ее реализации, еще в 1978 году Стивен Борн разработал командную оболочку sh, которая впоследствии, в 1987 году была усовершенствована Брайаном Фоксом и переросла в то, что мы знаем сегодня как bash (Bourne again shell).

Вполне логично, что появляется вопрос: «Для чего мне нужно что-то, что написали почти полвека назад?» Так вот ответ на него прост: это «что-то» до сих пор является самым мощным инструментом автоматизации и, де-факто, стандартом для написания простых, но эффективных сценариев на всех unix-based системах. Именно поэтому знать общий синтаксис bash и уметь писать на нем — критический скилл для разработчика.

В этом посте автор описал синтаксические конструкции, привел примеры простых сценариев и указал на некоторые подводные камни в bash. Он уверен, что такой конспект обязательно пригодится как начинающим, так и опытным разработчикам, которые могли забыть некоторые особенности этой сверхмощной командной оболочки.

Оболочки и вызов сценариев

Пользовательская оболочка bash может работать в двух режимах — интерактивном и, соответственно, не интерактивном. Открыть оболочку в Ubuntu можно комбинацией клавиш Ctrl + Alt + F1, привычный графический интерфейс исчезнет, а перед вами откроется один из семи виртуальных терминалов, доступных в дистрибутиве Ubuntu.
Если оболочка выдает приглашение (что-то вроде того, которое можно увидеть ниже), то вы работаете в интерактивном режиме:

Здесь можно вводить самые разнообразные unix-команды (как например: ls, grep, cd, mkdir, rm) и видеть результат их выполнения. Интерактивной эта оболочка называется потому, что она взаимодействует с пользователем напрямую. Окружение рабочего стола (графический интерфейс), в семействе систем Debian (к которым относится и Ubuntu), принято размещать в седьмом виртуальном терминале, для того чтобы вернуться к привычному окружение рабочего стола наберите комбинацию Ctrl + Alt + F7.

Конечно работать в виртуальных терминалах не слишком удобно, особенно, если нужно редактировать документ и одновременно выполнять какие-либо команды, поэтому в дальнейшем мы будем пользоваться встроенным в графический интерфейс эмулятором виртуального терминала, встроенным в Ubuntu. Открыть его можно комбинацией клавиш Ctrl + Alt + T, или Unity Dash, найдя его в списке программ.

В не интерактивном режиме оболочка читает команды из некоторого файла и последовательно выполняет их. Когда интерпретатор дойдет до конца файла, работа с оболочкой автоматически завершится. Запустить оболочку в не интерактивном режиме можно с помощью команд:

Где скрипт— это путь к файлу, содержащему команды для выполнения. Такой файл является обычным текстовым документом, который можно создать с помощью любого текстового документа. Впрочем, можно упростить вызов скрипта всего лишь сделав его исполняемым. Для этого необходимо предоставить соответствующие права доступа этому файлу с помощью команды chmod:
chmod +x скрипт

Кроме этого, в первой строке скрипта необходимо указать какая именно оболочка должна выполнять этот сценарий. Это можно сделать, разместив в начале соответствующее указание #! / Bin / sh(для оболочки sh) или #! / Bin / bash(соответственно для bash). После этого файл можно будет вызвать на выполнение обратившись к нему в терминале:

./скрипт

Комментарии

Сценарии могут содержать комментарии. Комментарии — это операторы, их можно размещать в сценарии оболочки, но который игнорируется при исполнении. Комментарии должны начинаются с символа # и продолжаются до символа новой строки.
Например:

#!/bin/bash<br># Сценарий выведет имя пользователя<br>whoami

Переменные

Оболочка позволяет создавать и удалять переменные, а также выполнять операции над ними. Переменные в bash могут находиться в 3-х областях видимости:

Локальные переменные — это обычные переменные внутри одного сценария. Они не доступны другим программам и сценариям, которые запускаются с этой оболочки. Объявляются переменные с помощью символа = (обратите внимание на то, что перед и после = нет пробелов), а к их значениям обращаются с помощью символа  $:

name="Петро Петрович"<br>echo $name # вывод значения<br>unset name # вывод переменной

Также можно создать локальную переменную внутри функции, которая будет доступна только в теле этой функции:

local локальная_переменная=значение

Переменные окружения — это переменные, которые доступны любым программам, запущенные с данной оболочки. Объявляются они так же как и локальные переменные, но с командой  export :

export глобальная_переменная=значение

В bash есть много переменных окружения, которые достаточно часто встречаются в сценариях, например:

  • HOME — путь к домашнему каталогу пользователя;
  • PATH — список каталогов, в которых оболочка ищет исполняемые файлы;
  • PWD — путь к рабочему каталогу;
  • RANDOM — формирует целое случайное число;
  • HOSTNAME — имя компьютера, на котором выполняется оболочка;

Переменные оболочки — это переменные, которые устанавливаются оболочкой и необходимы ей для корректной работы. Эти переменные имеют имена порядкового номера ( $ 1$ 2$ 3, …) и содержат аргументы, которые передавались сценарию при запуске, как:

./some_script.sh VAL1 VAL2 # внутри сценария $1='VAL1', $2='VAL2'

Переменным можно присваивать значения по умолчанию следующим образом:

: ${VAR:='значение по умолчанию'} # Если переменная VAR пустая, присвоить “значение по умолчанию”

Массивы и списки

В bash также есть возможность работы с массивами. При работе с ними часто пользуются переменной окружения IFS — разделителя полей для входных строк (IFS — Input Field Separator). По умолчанию IFS равен символу пробела, но может быть изменен для разбиения строки на элементы массива, например, запятыми. Обратите внимание, что для формирования переменных оболочки, которые доступны через  $ 1$ 2и т.д., используется именно переменная IFS, то есть введенная после имени скрипта строка аргументов будет разделена именно первым символом, который хранится в этой переменной.

Объявить массив можно следующим образом:

files[0]=Яблоко<br>files[1]=Груша<br>echo ${files[*]} # напечатает элементы массива без учета<br>IFS echo ${files[@]} # напечатает элементы массива с IFS в качестве разделителя

Получить доступ к элементу массива можно с помощью срезов:  $ {arr: 0: 1}. Удалить первый элемент массива можно с помощью сдвига:  shift arr. Добавить в элементы в массив:  arr = ("$ {arr [@]}" "Item 1" "Item 2"). Проверка вхождения элемента в массив реализуется с помощью несколько более сложной конструкции:

if [[ ${arr[(r)some]} == some ]]; then # команды,если элемент входит<br>else # команды, если не входит<br>fi

В этом примере  arr — некоторый массив, а  some — это элемент, который мы проверяем на вхождение.

Результаты операций

Присвоить переменной результат работы команды или арифметических операций можно с помощью апострофов, или конструкции  $ (выражение):

now=[crayon-61a4e345cd3c4592753281  ]data +%T
# или
now=$(data +%T)
echo now # 19:08:26[/crayon]

Арифметические операции необходимо помещать в двойные скобки:

foo=$(( ((10 + 5*3) 7) / 2 ))<br>echo $foo #> 9

С помощью фигурных скобок можно генерировать строки произвольного вида, или учитывать различные варианты написания слова:

echo beg{i,a,u}n #> begin began begun

Стоит вспомнить и о строгости кавычек в bash: одинарные кавычки — строгие, двойные — нестрогие. Это означает, что при подстановке переменных в строку с двойными кавычками, интерпретатор подставит соответствующее значение переменной. Одинарные кавычки выведут строку так, как вы её написали. Пример:

echo "Домашняя директория: $HOME" #> Домашняя директория: /home/user<br>echo 'Домашняя директория: $HOME' #> Домашняя директория: $HOME

Потоки

Файл с которого происходит чтение, называют стандартным потоком ввода, а тот в который происходит запись, соответственно — стандартным потоком вывода. В bash есть три стандартных потока:

0 stdin ввод<br>1 stdout вивод<br>2 stderr поток ошибок

Для перенаправления потоков используют основные операторы:

  • > — перенаправление потока вывода в файл (файл будет создан, или перезаписан)
  • >> — дописать поток вывода в конец файла;
  • < — перенаправляет данные из файла в поток ввода;
  • <<< — чтение данных из строки, вместо всего содержимого файла (работает для bash 3+)
  • 2> — перенаправляет поток ошибок в файл (файл будет создан, или перезаписан)
  • 2>> — дописать ошибки в конец файла;

Каналы

Стандартные потоки можно перенаправить не только в файлы, но и на вход других сценариев. Соединение потока вывода одной программы с потоком ввода другой называют каналом или пайпом (pipe) . Ниже приведен простой конвейер из трех команд:  команда1 перенаправляет свой вывод на вход  команды2, которая, в свою очередь, перенаправляет собственный вывод на вход  команды3:

cmd1 | cmd2 | cmd3

Конвейеры

Конвейеры — это команды, которые соединены операторами  ;, &&||для выполнения в определенной последовательности. Операторы организации конвейеров работают следующим образом:

  • команда1 ;  команда2команда2 выполняется после  команды1 независимо от результата её работы ;
  • команда1 && команда2команда2 выполняется только после успешного выполнения команды1(то есть с кодом завершения 0);
  • команда1 || команда2команда2 выполняется только после неудачного выполнения команды1 (то есть код завершения  команды1будет отличным от 0)

Условные операторы

В скриптовом языке bash поддерживаются два оператора ветвления:  if и  case. Оператор  if, как и в других языках, выполняет определенный блок указаний, в зависимости от условия. Условие помещают в двойные квадратные скобки  [[ ... ]], которые bash рассматривает как один элемент с кодом выхода. Внутри блока операторов помещенных в  [[ ]] разрешается использовать операторы  && и  ||. Например:

Обратите внимание, что  [, условие и  ] обязательно должны быть разделены пробелами, иначе оболочка воспримет в качестве команды  [условие.

Ниже приведена таблица с возможными условиями сравнения:

Пример:

== «Adam»]; then echo «Не ешь яблоко!» elif [ == «Eva»] then echo «Не бери яблоко!» else echo «Яблоки сейчас очень дорогие!» fi; [/crayon]

Если необходимо сделать выбор из нескольких альтернатив, пригодится оператор case. Принцип его работы легче понять на примере:

В примере оператор проверяет значение переменной  $extension на совпадение с одним из шаблонов и в случае совпадения выполнит соответствующий блок кода. Если же совпадений не будет, выполнятся указания, соответствующие шаблону  *.

Циклы

Язык оболочки дает пользователю возможность организовывать циклическое выполнение инструкций при помощи циклов:  *

while

Оператор  while описывается следующим образом:

Интерпретатор в первую очередь выполняет команды, описанные в  условии. Если результат выполнения нулевой, то выполняется тело, а после ее выполнения, переход к следующей итерации, в противном случае происходит выход из цикла. В условии может быть любая допустимая команда. Например:

# увеличиваем х на 1 done [/crayon]

for

Цикл for выполняет тело для каждого элемента из списка. Синтаксис цикла for таков:

В качестве элементов обычно используют различные шаблоны (wildcards). Очень удобно применять for для прохождения по каталогам и выполнения операций над группой файлов. В примере ниже, цикл проходит по всем файлам с расширением  *.bash, перемещает их в директорию  ~/scripts и добавляет их права на исполнение.

select

Цикл  select помогает организовать удобное меню выбора и применяется тогда, когда пользователь должен выбрать один элемент из предложенного списка. В общем цикл  select имеет такой же синтаксис, как и цикл  for:

При выполнении этого оператора, все элементы из списка высвечиваются на экране со своими порядковыми номерами в виде списка вариантов ответа, после списка выводится специальное приглашение для ввода. Обычно оно имеет вид  #?. Введенный пользователем номер списка записывается в переменную ответ. Если ответ содержит номер пункта меню, то в переменную заносится значение соответствующего элемента из списка. Если в списке нет введенного пункта, список будет показан снова. После того, как пользователь сделает правильный выбор, выполнятся указания в теле, а цикл перейдет к следующей итерации и все действия повторятся с самого начала — именно поэтому работу цикла  select желательно прерывать.

Пример выше запрашивает у пользователя название пакета, который он желает установить, далее интересуется тем какой пакетный менеджер использовать и в зависимости от выбора устанавливает нужный пакет и останавливает выполнение.
Оболочка также имеет команды, которые изменяют нормальное выполнение цикла. Оператор  break полностью останавливает выполнение цикла, оператор  continue — переходит к следующей итерации.

Функции

В сценариях оболочки возможны объявление и
вызов функций. Стоит отметить, что само понятие функций в  bash несколько урезано. На самом деле, функции в  bash — это именуемая группа команд, которые выполнятся при обращении к функции. В любом случае функциями следует пользоваться везде, где есть код, повторяющиеся с небольшими вариациями.

Объявление функции имеет следующий вид:

Объявление функции обязательно должно предшествовать ее первый вызов. Обращение к функции происходит путем указания ее имени в качестве команды.
Функция может принимать аргументы и возвращать после своего выполнения результат — код выхода. Функция обращается к своим аргументам точно так же, как и к локальным переменным, с помощью позиционных переменных —  $1$2 и тд. Результат работы можно возвращать с помощью команды  return. Например, функция, которая принимает параметр (имя) и завершает свою работу с кодом 0:

Команда  return возвращает код завершения 0 — это код успешного завершения сценария. Каждая программа по завершению работы записывает в переменную окружения  #? код завершения — число от 0 до 255. С помощью этой переменной можно определять статус выполнения каждой отдельной команды или скрипта. Если программа завершилась ошибкой, кодом завершения будет целое число отличное от нуля. Обратите внимание, на то что, если сценарий завершается командой  exit без параметров, кодом завершения сценария будет код завершения последней выполненной команды.

Отладка сценариев

Оболочка дает несколько средств для отладки сценариев. Для активации режима отладки, он должен быть запущен с помощью специальных опций.
Первая строка сценария должна иметь вид:

#!/bin/sh опция

Можно выбирать среди следующих функций:
n — читать все команды, но не выполнять их;
v — выводить все строки по мере их обработки интерпретатором;
*x- выводить все команды и их аргументы по мере их выполнения.

Для отладки сценария частями, нужный фрагмент отмечают вызовом команды  set с помощью соответствующей опции из таблицы. Причем, для включения режима отладки, перед опцией указывают символ  -, для отключения режима отладки используют  +:

Общая практика отладки заключается в том, чтобы прежде чем запустить ее, необходимо проверить его синтаксис с помощью опции  -n. Для большей детальности можно комбинировать ключи  -nv. После исправления синтаксических ошибок проводится отладка с помощью опции  -x.

Метки:

Опубликовано 06.05.2017 от evgeniyalf в категории "Linux