zabika.ru   1 ... 4 5 6 7 8

Недеструктивное перенаправление вывода.

Эффект от использования символа > (больше) для перенаправления вывода в файл является деструктивным. Иными словами, команда

$ ls > file-list

уничтожит содержимое файла file-list, если этот файл ранее существовал, и создаст на его месте новый файл. Если вместо этого перенаправление будет сделано с помощью символов >>, то вывод будет дописан в конец указанного файла, при этом исходное содержимое файла не будет уничтожено. Например, команда

$ ls >> file-list

дописывает вывод команды ls в конец файла file-list.

Следует иметь в виду, что перенаправление ввода и вывода и стыковка команд осуществляется командными оболочками, которые поддерживают использование символов >, >> и |. Сами команды неспособны воспринимать и интерпретировать эти символы.

Основы регулярных выражений.


Регулярные выражения (англ. regular expressions, сокращённо regex) — это система поиска фрагментов в тексте, основанная на специальной системе записи образцов для поиска. Образец (англ. pattern), задающий правило поиска, также называют шаблоном или маской.

Сейчас регулярные выражения используются многими текстовыми редакторами и утилитами для поиска и изменения текста на основе выбранных правил. Многие языки программирования имеют встроенную поддержку работы с регулярными выражениями, для других они доступны как внешние библиотеки. Набор утилит (включая редактор sed и фильтр grep), поставляемых в дистрибутивах *nix, одним из первых способствовал распространению регулярных выражений.


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


  • Перечисление: вертикальная черта разделяет допустимые варианты. Например, «one|two» соответствует one или two.

  • Группировка: круглые скобки используются для определения области действия и приоритета операторов. Например, шаблоны «abd|acd» и «a(b|c)d» описывают одно и то же множество, abd и acd.

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

  • {m,n} — общее выражение, повторений может быть от m до n включительно.

  • {m,} — общее выражение, m и более повторений.

  • {,n} —общее выражение, не более n повторений.

  • ? (вопросительный знак) означает 0 или 1 раз, то же самое, что и {0,1}. Например, «colou?r» соответствует и color, и colour.

  • * (астериск) означает 0, 1 или любое число раз ({0,}). Например, «go*gle» соответствует ggle, gogle, google и т.д.

  • + (плюс) означает хотя бы 1 раз ({1,}). Например, «go+gle» соответствует gogle, google и т.д. (но не ggle).

Конкретный синтаксис регулярных выражений зависит от их программной реализации. Мы будем рассматривать синтаксис «базовых» регулярных выражений UNIX. Хотя он на данный момент и определён POSIX как устаревший, но до сих пор широко распространён из соображений обратной совместимости. Многие UNIX-утилиты используют такие регулярные выражения по умолчанию.

В этом синтаксисе большинство символов соответствуют сами себе («a» соответствует a и т.д.). Исключения из этого правила называются метасимволами:


.

Соответствует любому единичному символу.

[ ]

Соответствует любому единичному символу из числа заключённых в скобки. Символ - (дефис) интерпретируется буквально только в том случае, если он расположен непосредственно после открывающей или перед закрывающей скобкой: [abc-] или [-abc]. В противном случае, он обозначает интервал символов. Например, [abc] соответствует a, b или c. [0-9] соответствует цифрам.

[^ ]

Соответствует единичному символу из числа тех, которых нет в скобках. Например, [^abc] соответствует любому символу, кроме a, b или c. [^0-9] соответствует любому символу, кроме цифр.

^

Используемое в начале регулярного выражения, соответствует началу строки текста.

$

Используемое в конце регулярного выражения, соответствует концу строки текста.

\(\)

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

\n

n — цифра от 1 до 9, соответствует n-му отмеченному подвыражению.

*

Звёздочка после выражения, соответствующего единичному символу, соответствует нулю или более копий этого выражения. Например, «[xyz]*» соответствует пустой строке, x, y, zx, zyx, и т.д.


\{x,y\}

Соответствует последнему блоку, встречающемуся не менее x и не более y раз. Например, «a\{3,5\}» соответствует aaa, aaaa или aaaaa.

При использовании диапазонов символов следует учитывать, что они могут зависеть от выбранных настроек локализации. Например, диапазон «[b-e]» означает символы от b до e включительно. В английском языке, где сортировка букв идёт по-порядку (...XYZabcdefg...), ему соответствует набор символов b,c,d,e. Согласно правилам русского языка, сортировка тех же символов идёт в другом порядке (...эЭюЮяЯaAbBcCdDeEfFgG...), и тому же диапазону соответствуют символы b,B,c,C,d,D,e.

Для решения таких проблем в стандарте POSIX имеются объявления некоторых классов и категорий символов:

Класс

Диапазон для английского языка

Описание

[:upper:]

[A-Z]

Латинские буквы верхнего регистра

[:lower:]

[a-z]

Латинские буквы нижнего регистра

[:alpha:]

[A-Za-z]

Латинские буквы верхнего и нижнего регистра

[:alnum:]


[A-Za-z0-9]

Цифры, латинские буквы верхнего и нижнего регистра

[:digit:]

[0-9]

Цифры

[:xdigit:]

[0-9A-Fa-f]

Шестнадцатеричные цифры

[:punct:]

[.,!?:…]

Знаки пунктуации

[:blank:]

[ \t]

Пробел и табуляция

[:space:]

[ \t\n\r\f\v]

Символы пропуска

[:cntrl:]

-

Символы управления

[:graph:]

[^\t\n\r\f\v]

Символы печати

Способ представить сами метасимволы — ., - [ ] и другие в регулярных выражениях без интерпретации, т.е. в качестве простых (не специальных) символов — предварить их обратной символом: \ (обратный слеш). Например, чтобы представить сам символ «точка» (просто точка, и ничего более), надо написать \. (обратный слеш, а за ним — точка). Чтобы представить символ открывающей квадратной скобки [, надо написать

\[ ( обратный слеш, и следом за ним скобка [ ) и т.д. Сам метасимвол

\ (обратный слеш) тоже может быть защищен, то есть представлен как
\\ (два обратных слеша), и тогда интерпретатор регулярных выражений воспримет его как простой символ обратного слеша \.

При составлении регулярных выражений следует также учитывать их две основные черты: они являются т.н. «ленивыми» и «жадными». Первое означает, что в строке, где есть несколько совпадений с шаблоном, шаблон совпадёт с первым из них. Например, регулярное выражение
«шаблон\(..\)» для строки

в строке, где есть несколько совпадений с шаблоном, шаблон совпадёт с первым из них

вернёт в подвыражении \1 символы ом, соответствующие первому встретившемуся подходящему совпадению (шаблоном). Второе возможное место совпадения (шаблон с) рассмотрено не будет.

«Жадность» регулярных выражений заключается в том, что, при использовании квантификаторов * (астериск) и + (плюс), шаблон будет совпадать с максимально длинным из возможных вариантов. Для той же строки шаблон «шаблон.*н», означающий подстроку, начинающуюся с «шаблон», заканчивающуюся на «н» и с произвольным количеством (*) любых (.) символов между «шаблон» и «н», совпадёт с подстрокой

шаблоном, шаблон совпадёт с первым из н ,

а не с более короткой

шаблоном, шаблон

Рассмотрим далее применение регулярных выражений на примерах использования утилит grep и sed.

Утилита grep.


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

grep [options] PATTERN [FILE...]

где PATTERN — регулярное выражение, а FILE — один или несколько файлов, к содержимому которых будет применено это регулярное выражение.


Если файл не задан, то grep читает текст со стандартного ввода.
С помощью опций (англ. options) можно управлять поведением grep, например. опция -v приводит к выводу всех строк, не совпадающих с заданным регулярным выражением.

Рассмотрим некоторые примеры использования grep и регулярных выражений. Как говорилось в предыдущей лабораторной работе, команда ls выводит список файлов в каталоге. Команда ls /bin выведет список файлов из каталога /bin. Вывод команда ls осуществляет в stdout.

Предположим, нас интересуют те программы (файлы) из /bin, которые содержат подстроку zip. Этой подстроке соответствует простейшее регулярное выражение «zip». Перенаправляем вывод из ls в grep и получаем:

$ ls /bin | grep 'zip' bunzip2 bzip2 bzip2recover gunzip gzip

Здесь регулярное выражение заключено в одиночные кавычки '', которые указывают bash, что внутри них — обычная строка. Такой синтаксис позволяет использовать в регулярном выражении пробелы, и его разумно придерживаться во всех случаях (например, регулярное выражение 'a b' описывает шаблон для строк, содержащих последовательно a, пробел и b. Если этот шаблон указать grep без кавычек, т.е. grep a b, то командный интерпертатор оболочки, разобрав строку, вызовет grep с двумя параметрами, и grep будет искать строки
с буквами а в файле b. При использовании кавычек командный интерпретатор будет считать выражение 'a b' одним параметром, и передаст его grep целиком, вместе с пробелом внутри).


Файлы из /bin, которые кончаются на 2:

$ ls /bin | grep '2$' bash2 bunzip2 bzip2

Файлы из /bin, которые начинаются на b:

$ ls /bin | grep '^b' basename bash bash2 bunzip2 bzcat bzip2 bzip2recover


Файлы из /bin, начинающиеся на b и содержащие в своём имени букву a:

$ ls /bin | grep '^b.*a' basename bash bash2 bzcat

Здесь в регулярном выражении мы указали, что оно:


  • должно совпадать с началом строки — ^

  • в начале строки должна быть буква b — ^b

  • дальше может быть любой символ — ^b.

  • и таких символов может быть сколько угодно — 0 или больше — ^b.*

  • а дальше должна быть буква a — ^b.*a

Файлы из /bin, начинающиеся на b и содержащие в своём имени буквы a, e или k:

$ ls /bin | grep '^b.*[aek]' basename bash bash2 bzcat bzip2recover

Здесь используется описание набора символов — [aek].

Рассмотрим более полезный пример.

На предыдущей лабораторной работе производилась настройка сервера lighttpd. Его конфигурационный файл — /etc/lighttpd/lighttpd.conf. Как было видно, в нём (как и в большинстве других конфигурационных файлов) содержится большое количество комментариев, как с поясняющим текстом, так и с примерами различных опций настройки. Предположим, нам нужно посмотреть текущую конфигурацию сервера. Однако посмотреть её простой командой cat /etc/lighttpd/lighttpd.conf неудобно — текст не помещается на экране. Мы можем, конечно, использовать команду less для прокрутки текста, но комментарии при этом всё равно будут мешать. Мы можем удалить их из файла, но тогда сложно будет что-либо изменять в нём в дальнейшем.

Проще отфильтровать ненужный текст непосредственно при выводе файла на экран.

Комментарии в lighttpd.conf начинаются с символа # (октоторп). Перед ним в начале строки может или не быть ничего, или быть один или несколько пробелов.


Таким образом, регулярное выражение для выделения строк с комментариями — «^ *#»: начало строки, ноль или несколько пробелов, и затем — #.

Кроме того, нас не очень интересуют просто пустые строки, в которых нет никакого текста. Такие строки можно описать выражением «^$»: начало строки, и сразу — её конец. Может быть и другой вариант: строка, состоящая из одних пробелов, которая также не несёт никакой информации. Таким образом, общее регулярное выражение приобретает вид «^ *$».

Итого, строкам комментариев соответствует выражение «^ *#», а пустым строкам — «^ *$». Фильтру grep можно приказать выводить строки, которые не совпадают с регулярным выражением, вызвав его с ключом -v.

Выводим файл lighttpd.conf в stdout и последовательно пропускаем вывод через два фильтра:

# cat /etc/lighttpd/lighttpd.conf | grep -v '^ *#' | grep -v '^ *$'

Этот вариант не очень эффективен, хотя и приносит желаемый результат. Можно избежать двух последовательных вызовов grep, объединив шаблоны. Видно, что они очень похожи: возможные пробелы в начале строки и или # (октоторп), или конец строки. Т.е. общий шаблон — «^ *(#|$)».

grep поддерживает несколько вариантов синтаксиса регулярных выражений и в варианте по умолчанию рассматривает круглые скобки как обычные символы. Поэтому надо или приказать grep'у рассматривать их как оператор выбора, экранировав скобки символом \ (обратный слеш), или переключить grep в режим работы с расширенным синтаксисом регулярных выражений, вызвав его с ключом -E, или использовать версию grep с включённой по умолчанию поддержкой расширенных регулярных выражений — egrep:

# cat /etc/lighttpd/lighttpd.conf | grep -v '^ *\(#\|$\)' # cat /etc/lighttpd/lighttpd.conf | grep -E -v '^ *(#|$)' # cat /etc/lighttpd/lighttpd.conf | egrep -v '^ *(#|$)'


Ну и наконец, нам не обязательно передавать файл lighttpd.conf на стандарный вход grep/egrep, эти утилиты могут сами прочитать файл с диска:

# egrep -v '^ *(#|$)' /etc/lighttpd/lighttpd.conf

Утилита sed.


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

sed [options] COMMAND [FILE...]

Из большого числа возможных команд sed мы рассмотрим только команду поиска и замены текста. Эта команда имеет вид s/PATTERN/EXPRESSION/ и осуществляет поиск в каждой из входящих строк текста регулярного выражения PATTERN. Результаты совпадения заменяются на выражение EXPRESSION. Результирующий текст выводится в стандартный поток вывода.

Рассмотрим использование команды замены в sed на примерах.

В простейшем случае просто поменяем один фрагмент текста на другой:

$ ls -1 /var/cache apt fontconfig man $ ls /var/cache/ | sed 's/apt/APT/' APT fontconfig man

В каталоге /var/cache есть несколько файлов, список их можно получить командной ls. Регулярное выражение «apt» совпадает с одной из строк вывода, и мы меняем совпадение на APT.

$ ls /var/cache/ | sed 's/a/A/' Apt fontconfig mAn

В этом случае мы заменили в выводе ls букву a на А. sed применяет свои команды для каждой из строк вывода, поэтому в обеих строках, где была буква a, она была заменена.


Утилита uptime выдаёт определённую статистику по работе системы:

$ uptime 07:48:42 up 27 days, 22:13, 1 user, load average: 0.00, 0.00, 0.00

Для того, чтобы выделить из этой строки текущее число пользователей в системе, используем sed. Число пользователей — это одна или несколько цифр — «[0-9]\+», за которыми после пробела (или нескольких пробелов в общем случае) — «[0-9]\+ \+» следует слово user (или users). Нам интересно число пользователей — выберем его в подвыражении:
«\([0-9]\+\) \+user». В начале строки идёт некоторый текст, отделённый от числа пользователей пробелом: «^.* \([0-9]\+\) \+user». Конец строки тоже может быть любой: «^.* \([0-9]\+\) \+user.*».


Данное выражение совпадает со всей строкой и выделяет в подстроку \1 число пользователей. Заменив целиком строку на \1, мы получим в результате только это число:

$ uptime | sed 's/^.* \([0-9]\+\) \+user.*/\1/' 1

Аналогично можно получить, например, время работы системы (подстроку вида 27 days, 22:13):

$ uptime | sed 's/^.* up \+\(.\+\), \+[0-9]\+ \+user.*/\1/' 27 days, 22:13

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


  • любое число любых символов от начала строки, далее пробел и слово up — ^.* up

  • за которым следует через один или несколько пробелов время работы системы — ^.* up \+\(\)

  • само время работы системы может содержать фактически любые символы, в т.ч. пробелы, знаки пунктуации и пр. —
    ^.* up \+\(.\+\)


  • однако за ним через запятую и один или несколько пробелов —

    ^.* up \+\(.\+\), \+


  • следует количество пользователей (число, одна или несколько
    цифр) — ^.* up \+\(.\+\), \+[0-9]\+


  • и слово user (или users). Далее до конца строки может быть что угодно — ^.* up \+\(.\+\), \+[0-9]\+ \+user.*

Отметим, что то же самое мы могли бы сделать и по-другому — просто удаляя из вывода ненужный нам текст. Например:

$ uptime | sed 's/user.*//' 08:18:07 up 27 days, 22:43, 2

убирает весь текст от user включительно и до конца строки. Также убираем в полученном результате и всё в конце строки от запятой включительно:

$ uptime | sed 's/user.*//'| sed 's/,[^,]*$//' 08:24:13 up 27 days, 22:49

Отметим, что более простой вариант без привязки к концу строки

$ uptime | sed 's/user.*//'| sed 's/,[^,]*//' 08:24:18 up 27 days, 2

из-за «ленивости» регулярных выражений совпадёт с первым вхождением запятой (, 22:43), а ещё более простой вариант

$ uptime | sed 's/user.*//'| sed 's/,.*$//' 08:25:11 up 27 days

из-за «жадности» будет совпадать с текстом от первой запятой до конца строки (, 22:43, 2).

Далее нам нужно удалить текст от начала строки до up включительно:

$ uptime | sed 's/user.*//'| sed 's/,[^,]*$//' | \ sed 's/^.*up \+//' 27 days, 22:54

и мы получаем требуемый результат. (Символ \ (обратный слеш) в конце строки здесь означает, что команда будет продолжена на следующей строке).

Утилита awk.

AWK — интерпретируемый скриптовый язык, предназначенный для обработки текстовой информации. Первая версия AWK была написана в 1977 году в AT&T Bell Laboratories и получила название по фамилиям своих разработчиков: Альфреда Ахо (Alfred V. Aho), Питера Вейнбергера (Peter J. Weinberger) и Брайана Кернигана (Brian W. Kernighan).


AWK рассматривает входной поток как набор записей, каждая из которых состоит из набора полей. По-умолчанию для AWK записью является строка, а разделителями полей в строке — пробелы. Внутри программы на AWK значение поля можно получить как значение переменной $1, $2, $3, ... Переменная $0 содержит в себе всю запись.

Программа на AWK имеет вид

PATTERN {ACTION} PATTERN {ACTION} ...

Для каждой строки, совпадающей с шаблоном, выполняется указанное действие. Если шаблон не указан, то действие выполняется для всех строк.

Шаблон — это регулярное выражение, из большого числа возможных действий мы рассмотрим только команду print.

Рассмотрим использование команды awk на примерах.

Список файлов с указанием их владельцев, прав, и даты последнего изменения можно получить командой ls -l. Он имеет вид:

$ ls -l /bin | head -n 5 total 5596 lrwxrwxrwx 1 root root 4 Feb 25 05:30 awk -> gawk -rwxr-xr-x 1 root root 19064 Apr 20 2008 basename -rwxr-xr-x 1 root root 549368 Mar 27 2008 bash lrwxrwxrwx 1 root root 4 Feb 25 05:30 bash2 -> bash

Преобразуем этот список в формат

<имя файла> <владелец>:<группа> <права>

awk обрабатывает каждую строку списка отдельно, и самостоятельно разбивает её на поля по границам слов. Права файла — поле 1, владелец и группа — поля 3 и 4, имя файла — поле 9. Тогда:

$ ls -l /bin | awk '{print $9,$3":"$4,$1;}' | head : total awk root:root lrwxrwxrwx basename root:root -rwxr-xr-x bash root:root -rwxr-xr-x bash2 root:root lrwxrwxrwx bunzip2 root:root lrwxrwxrwx bzcat root:root lrwxrwxrwx bzip2 root:root -rwxr-xr-x bzip2recover root:root -rwxr-xr-x cat root:root -rwxr-xr-x


Можно отфильтровать список и вывести только файлы. Для файлов первый символ поля прав — - (дефис). Для форматирования вывода разделим выводящиеся значения символами табуляции (код символа \t). С учётом этого получаем:

$ ls -l /bin | awk '/^-/ {print $9"\t->\t"$3":"$4"\t"$1;}' | head basename -> root:root -rwxr-xr-x bash -> root:root -rwxr-xr-x bzip2 -> root:root -rwxr-xr-x bzip2recover -> root:root -rwxr-xr-x cat -> root:root -rwxr-xr-x chgrp -> root:root -rwxr-xr-x chmod -> root:root -rwxr-xr-x chown -> root:root -rwxr-xr-x clock_unsynced -> root:root -rwxr-xr-x cp -> root:root -rwxr-xr-x

Создание скриптов.


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

В простейшем случае, скрипт можно создать, например, так:

$ echo "ls | grep script" > script $ cat script ls | grep script $ sh script script

Здесь мы создали текстовый файл, содержащий команды ls и grep, и далее выполнили эти команды, вызвав интерпретатор команд и передав ему в качестве аргумента имя скрипта. Интерпретатор команд, получив в качестве аргумента имя файла, считал из него команды и выполнил их.

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


Имя программы, которая должна интерпретировать записанную в текстовый файл (скрипт) последовательность команд, можно указать в самом скрипте. Это делается с помощью специальным образом оформленной первой строки скрипта, которая обычно выглядит примерно как

#!/bin/bash

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

Как рассматривалось в предыдущей лабораторной работе,
в операционных системах *nix существуют права доступа к файлам. Если для файла задано право его выполнения, то интерпретатор команд откроет его и прочитает несколько первых символов файла. Если там обнаружится начало скомпилированной программы, то она будет запущена, если же там обнаружится последовательность символов #!, то будет запущен указанный после неё интерпретатор, которому будет передано в качестве аргумента имя файла.


Итого:

$ echo '#!/bin/bash' > script $ echo 'ls | grep script' >> script $ chmod a+x script $ cat script #!/bin/bash ls | grep script $ ls -l script -rwxr-xr-x 1 student student 29 Мар 20 09:35 script $ ./script script

Здесь мы создали путём вызова двух команд echo файл (обратите внимание, что во второй команде мы дописали строку в имеющий файл), задали этому файлу право на выполнение, проверили результат (выведя файл через cat и проверив права на него через ls -l) и запустили его на выполнение.


Отметим, что командный интерпретатор ищет выполняемые файлы в определённых местах: /bin, /usr/bin и т.п. Для запуска программы из нестандартного места требуется указывать путь к ней, т.е., в данном случае, запустить программу как script нельзя — вместо созданного нами скрипта командный интерпретатор запустит стандартную утилиту script из /usr/bin.

Часто простого последовательного выполнения недостаточно — для эффективного программирования требуются переменные, условное выполнение команд и т.п. Командный интерпретатор имеет собственный язык, который по своим возможностям приближается к высокоуровневым языкам программирования. Этот язык позволяет создавать программы (shell-файлы, скрипты), которые могут включать операторы языка и команды UNIX.

Такие файлы не требуют компиляции и выполняются в режиме интерпретации, но они должны обладать правом на исполнение (устанавливается с помощью команды chmod).

Скрипту могут быть переданы аргументы при запуске. Каждому из первых девяти аргументов ставится в соответствие позиционный параметр от $1 до $9 ($0 — имя самого скрипта), и по этим именам к ним можно обращаться из текста скрипта.

Прежде, чем начать рассмотрение некоторых операторов shell, следует обратить внимание на использование в командах некоторых символов.


  • \ (обратный слеш) — знак отмены специального символа перевода строки, следующего непосредственно вслед за этим знаком.

  • '' (апострофы) — используются для обрамления текста, передаваемого как единый аргумент команды.

  • "" (двойные кавычки) — используются для обрамления текста, содержащего имена переменных для подстановки ($имя) или стандартные команды, заключенные в символы обратного апострофа (`команда`).
  • `` (обратные апострофы) — служат для выполнения команды, заключенной между ними.


Кроме того, для удобства работы с файлами почти все командные интерпретаторы интерпретируют символы ? (знак вопроса) и * (астериск), используя их как шаблоны имен файлов (т.н. метасимволы):

  • ? — один любой символ;

  • * — произвольное количество любых символов.

Например, *.c обозначает все файлы с расширением c,
pr???.* обозначает файлы, имена которых начинаются с pr, содержат пять символов и имеют любое расширение.

Переменные языка shell.


Язык shell позволяет работать с переменными без предварительного объявления. Имена переменных начинаются с буквы и могут содержать буквы, цифры и символ подчеркивания. Обращение к переменным начинается со знака $ (знак доллара).

Имеется большое количество уже определённых переменных —
т.н. переменных окружения. Их полный список можно получить командой set. Переменные окружения используются для настройки различных параметров окружения пользователя, например, в переменной TMP задаётся каталог для временных файлов, используемый рядом программ:


$ echo $TMP /tmp/.private/student $ ls $TMP mc-student

Переопределить (в т.ч. случайно) такие системные переменные можно, но стоит учесть, что это может привести к нежелательным последствиям.

Оператор присваивания.


Присвоение значений переменным осуществляется с помощью оператора = (знак равенства). Пробелов между именем переменной, = и значением быть не должно. Например:

$ A=5 $ B=пять $ C=$A+$B $ echo A A $ echo B=$B B=пять $ echo C=$C C=5+пять


Как мы видим, интерпретатор команд все переменные рассматривает как строки. Однако есть возможность и вычисления арифметических выражений — через внешние программы.

Вычисление выражений.


Вычисление выражений осуществляется с помощью команды expr и арифметических и логических операторов:

$ a=5 b=12 $ a=`expr $a + 4` $ d=`expr $b - $a` $ echo $a $b $d $A 9 12 3 5

Для expr аргументы и операции обязательно разделяются пробелами (они должны передаться команде как отдельные параметры). Кроме того,
мы видим, что имена переменных чувствительны к регистру, a и A — разные переменные.

Условные выражения.


Ветвление вычислительного процесса осуществляется с помощью оператора if:

if список_команд1; then список_команд2 [else список_команд3] fi

(В квадратных скобках указывается необязательная часть команды.)

Список_команд — это одна или несколько команд (для задания пустого списка используется : (двоеточие). Список_команд1 передает оператору if код возврата последней команды из списка. Если код равен 0, то выполняются команды из списка_команд2, таким образом нулевой код возврата эквивалентен значению «истина». В противном случае выполняются команды из списка_команд3, если он указан.

Проверка условия может осуществляется с помощью команды test. Аргументами этой команды могут быть имена файлов, числовые и нечисловые строки. Она используется в следующих режимах:
  • Проверка файлов: test -ключ имя_файла


    Ключи: -r файл существует и доступен для чтения;

    -w файл существует и доступен для записи;

    -x файл существует и доступен для исполнения;

    -f файл существует и является обычным файлом (т. е. не каталогом, не файлом устройства и т.п.);

    -s файл существует, является обычным файлом и не пуст, т. е. его размер больше 0 байт;

    -d файл существует и является каталогом.


  • Сравнение чисел: test число1 -ключ число2

    Ключи: -eq равно;

    -ne не равно;

    -lt меньше;

    -le меньше или равно;

    -gt больше

    -ge больше или равно.

  • Сравнение строк: test [строка1] выражение строка2

    [-n] строка строка не пуста;

    -z строка строка пуста;

    строка1 = строка2 строки равны;

    строка1 != строка2 строки не равны.

В качестве альтернативой записи test можно использовать команду [ (открывающая квадратная скобка), при этом, например, для проверки существования файла вместо

$ if test -f /bin/bash; then echo 'bash найден!'; fi bash найден!

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

$ if [ -f /bin/bash ]; then echo 'bash найден!'; fi bash найден!

Построение циклов.


В языке командного интерпретатора существует три типа циклов: while, until и for.

Цикл while:


while список_команд1; do список_команд2 done

В условии учитывается код возврата последней выполненной команды из списка_команд1, при этом 0 интерпретируется как «истина».

Цикл until:

until список_команд1; do список_команд2{;|перевод строки} done

Проверка условия выполняется перед выполнением цикла. Учитывается код возврата последней выполненной команды из списка_команд1, при этом цикл выполняется до тех пор, пока код возврата не примет значение «истина», т. е. будет равным нулю.

Цикл for:

for переменная [in список_значений]; do список_команд done

Переменной присваивается значение очередного слова из списка_значений, и для этого значения выполняется список_команд. Количество итераций равно количеству цепочек символов в списке_значений, разделённых пробелами. Если ключевое слово in и список_значений опущены как необязательные, то переменной поочередно присваиваются значения параметров, переданных при запуске программы-скрипта. В качестве передаваемых параметров можно использовать шаблоны имён файлов, тогда интерпретатор превращает эти шаблоны в требуемый синтаксисом список имён файлов, удовлетворяющих шаблону.

Например,

$ A=1; for i in `ls /bin | grep '^b'`; do > echo "$A :$i" > A=`expr $A + 1` > done 1 :basename 2 :bash 3 :bash2 4 :bunzip2 5 :bzcat 6 :bzip2 7 :bzip2recover


Здесь мы получили список файлов из /bin (ls /bin), отфильтровали из него файлы, начинающиеся на b (ls /bin | grep '^b'), и передали полученный список в качестве параметра оператору цикла for. В самом цикле мы вывели текущее значение переменной цикла и номер записи.

Периодическое (регулярное) выполнение задач.


Скрипты можно использовать для автоматизации тех или иных задач. Очень часто при этом требуется организовать выполнение скрипта в заданное время или через определённые интервалы времени. Для этого существует специальный демон — crond.

Для настройки программ на регулярное выполнение используется файл конфигурации, который можно посмотреть командной crontab -l и изменить командой crontab -e.

Рассмотрим такой файл:

$ crontab -l # DO NOT EDIT THIS FILE - edit the master and reinstall. # (/tmp/.private/student/crontab.6WaeT9 installed on Mon Mar 17 12:39:10 2008) # (Cron version V5.0 -- vixie-cron-4.1.20060426-alt3) #minute (0-59), #| hour (0-23), #| | day of the month (1-31), #| | | month of the year (1-12), #| | | | day of the week (0-6 with 0=Sunday). #| | | | | commands */1 * * * * /var/www/bin/log-local.sh */2 * * * * /var/www/bin/log-snmp.sh

Строки, начинающиеся с # — как обычно, комментарии. Для каждой из запускаемых команд указывается, когда её надо выполнить. Для этого используются пять полей — минуты, часы, дни месяца, месяцы и дни недели. Для каждого из полей можно указать или какое-либо определённое значение, или *, что означает «для всех».

Рассмотрим значения этих полей на примера вызова программы /bin/false:

* * * * * /bin/false


Запускать каждую минуту (каждого часа, каждого дня, каждого месяца, в любой день недели)

*/3 * * * * /bin/false

Запускать каждые три минуты (каждого часа, каждого дня, каждого месяца, в любой день недели)

*/3 1-2 * * * /bin/false

Запускать каждые три минуты первого и второго часа ночи (каждого дня, каждого месяца, в любой день недели)

1 1,6 * * * /bin/false

Запускать в первую минуту первого и шестого часа ночи, т.е. в 01:01 и 06:01 (каждого дня, каждого месяца, в любой день недели)

1 1 * * 1 /bin/false

Запускать в 01:01 каждый понедельник

1 1 * 2 1 /bin/false

Запускать в 01:01 каждый понедельник февраля

* * 13 * 5 /bin/false

Запускать каждую минуту каждого часа 13 числа каждого месяца, приходящегося на пятницу

При выполнении по cron'у задач, которые потенциально могут выполняться длительное время, следует предусмотреть и блокировать повторный запуск cron'ом скрипта в то время, когда ещё не успел завершиться предыдущий. Обычно такое можно делать, создавая и анализируя при запуске скрипта файл блокировки. Например:

$ cat lock.sh #!/bin/bash LOCK=/tmp/file.lock if [ -f "$LOCK" ]; then echo 'Скрипт уже работает' exit 1 fi touch "$LOCK" sleep 1m rm -f "$LOCK"


Здесь при запуске скрипта проверяется существование файла, и если он существует, то выполнение скрипта завершается. Иначе файл создаётся, выполняется некое действие (в данном случае — просто ожидание на 1 минуту), и перед завершением работы файл блокировки удаляется.

Пример выполнения:

$ ./lock.sh & [2] 7704 $ Скрипт уже работает [2]+ Exit 1 ./lock.sh



<< предыдущая страница   следующая страница >>