Shell
Shell
им. Н. Э. Баумана
Земляков В. Н.
Учебное пособие
Москва – 2006г.
2
Содержание.
Введение....................................................................................................................................5
Служебные символы.................................................................................................................7
Переменные и параметры....................................................................................................... 20
Операции................................................................................................................................. 40
Операторы ....................................................................................................................... 40
Числовые константы....................................................................................................... 43
Переменные ............................................................................................................................ 44
Внутренние переменные................................................................................................. 44
Позиционные переменные.............................................................................................. 44
Прочие специальные переменные.................................................................................. 44
Работа со строками ......................................................................................................... 49
Извлечение подстроки....................................................................................... 49
Удаление части строки...................................................................................... 49
Замена подстроки .............................................................................................. 49
Подстановка параметров ................................................................................................ 53
$RANDOM: генерация псевдослучайных целых чисел ................................................ 58
Двойные круглые скобки................................................................................................ 59
Циклы .............................................................................................................................. 60
Вложенные циклы........................................................................................................... 63
Управление ходом выполнения цикла ........................................................................... 64
Операторы выбора .......................................................................................................... 64
3
Арифметические подстановки ....................................................................................... 69
Перенаправление ввода/вывода ..................................................................................... 69
Использование exec........................................................................................... 72
Функции................................................................................................................................ 124
Массивы................................................................................................................................ 131
4
Введение.
Shell -- это командная оболочка. Но это не просто промежуточное звено между
пользователем и операционной системой, это еще и мощный язык программирования.
Программы на языке shell называют сценариями, или скриптами. Фактически, из скрип-
тов доступен полный набор команд, утилит и программ UNIX. Если этого недостаточно,
то к вашим услугам внутренние команды shell -- условные операторы, операторы циклов и
пр., которые увеличивают мощь и гибкость сценариев. Shell-скрипты исключительно хо-
роши при программировании задач администрирования системы и др., которые не требу-
ют для своего создания полновесных языков программирования.
Зачем необходимо знание языка Shell?
Знание языка командной оболочки является залогом успешного решения задач ад-
министрирования системы. Даже если вы не предполагаете заниматься написанием своих
сценариев. Во время загрузки Linux выполняется целый ряд сценариев из /etc/rc.d, ко-
торые настраивают конфигурацию операционной системы и запускают различные серви-
сы, поэтому очень важно четко понимать эти скрипты и иметь достаточно знаний, чтобы
вносить в них какие либо изменения.
Язык сценариев легок в изучении, в нем не так много специфических операторов и
конструкций. Синтаксис языка достаточно прост и прямолинеен, он очень напоминает
команды, которые приходится вводить в командной строке. Короткие скрипты практиче-
ски не нуждаются в отладке, и даже отладка больших скриптов отнимает весьма незначи-
тельное время.
Shell - скрипты очень хорошо подходят для быстрого создания прототипов сложных при-
ложений, даже не смотря на ограниченный набор языковых конструкций и определенную
"медлительность". Такая метода позволяет детально проработать структуру будущего
приложения, обнаружить возможные "ловушки" и лишь затем приступить к кодированию
на C, C++, Java, или Perl.
Скрипты возвращают нас к классической философии UNIX - "разделяй и властвуй" т.е.
разделение сложного проекта на ряд простых подзадач. Многие считают такой подход
наилучшим или, по меньшей мере, наиболее эстетичным способом решения возникающих
проблем, нежели использование нового поколения языков - "все в одном", таких как Perl.
Для каких задач неприменимы скрипты
· для ресурсоемких задач, особенно когда важна скорость исполнения (поиск, сорти-
ровка и т.п.)
· для задач, связанных с выполнением математических вычислений, особенно это ка-
сается вычислений с плавающей запятой, вычислений с повышенной точностью,
комплексных чисел (для таких задач лучше использовать C++ или FORTRAN)
· для кросс-платформенного программирования (для этого лучше подходит язык C)
· для сложных приложений, когда структурирование является жизненной необходи-
мостью (контроль за типами переменных, прототипами функций и т.п.)
· для целевых задач, от которых может зависеть успех предприятия.
· когда во главу угла поставлена безопасность системы, когда необходимо обеспе-
чить целостность системы и защитить ее от вторжения, взлома и вандализма.
· для проектов, содержащих компоненты, очень тесно взаимодействующие между
собой.
· для задач, выполняющих огромный объем работ с файлами
· для задач, работающих с многомерными массивами
5
· когда необходимо работать со структурами данных, такими как связанные списки
или деревья
· когда необходимо предоставить графический интерфейс с пользователем (GUI)
· когда необходим прямой доступ к аппаратуре компьютера
· когда необходимо выполнять обмен через порты ввода-вывода или сокеты
· когда необходимо использовать внешние библиотеки
· для "закрытых" программ (скрипты представляют из себя исходные тексты про-
грамм, доступные для всеобщего обозрения)
Если выполняется хотя бы одно из вышеперечисленных условий, то вам лучше об-
ратиться к более мощным скриптовым языкам программирования, например Perl, Tcl,
Python, Ruby или к высокоуровневым компилирующим языкам -- C, C++ или Java. Но да-
же в этом случае, создание прототипа приложения на языке shell может существенно об-
легчить разработку.
Название BASH - это аббревиатура от "Bourne-Again Shell" и игра слов от, ставше-
го уже классикой, "Bourne Shell" Стефена Бурна (Stephen Bourne). В последние годы
BASH достиг такой популярности, что стал стандартной командной оболочкой de facto
для многих разновидностей UNIX. Большинство принципов программирования на BASH
одинаково хорошо применимы и в других командных оболочках, таких как Korn Shell
(ksh), от которой Bash позаимствовал некоторые особенности, и C Shell и его производ-
ных.
Далее, на наших занятиях разберем большое количество примеров скриптов, иллюстри-
рующих возможности shell. Все примеры - работающие. Они были протестированы, при-
чем некоторые из них могут пригодиться в повседневной работе.
В простейшем случае, скрипт - это ни что иное, как простой список команд систе-
мы, записанный в файл. Создание скриптов поможет сохранить ваше время и силы, кото-
рые тратятся на ввод последовательности команд всякий раз, когда необходимо их выпол-
нить.
Пример cleanup: Сценарий очистки лог-файлов в /var/log.
# cleanup
# Для работы сценария требуются права root.
cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Лог-файлы очищены."
Здесь нет ничего необычного, это простая последовательность команд, которая
может быть набрана в командной строке с консоли. Преимущество размещения последо-
вательности команд в скрипте состоит в том, что вам не придется всякий раз набирать эту
последовательность вручную. Кроме того, скрипты легко могут быть модифицированы
или обобщены для разных применений.
Если файл сценария начинается с последовательности #!, которая в мире UNIX на-
зывается sha-bang, то это указывает системе какой интерпретатор следует использовать
для исполнения сценария. Это двухбайтовая последовательность, или - специальный мар-
кер, определяющий тип сценария, в данном случае - сценарий командной. Более точно,
sha-bang определяет интерпретатор, который вызывается для исполнения сценария, это
может быть командная оболочка (shell), иной интерпретатор или утилита.
#!/bin/sh
#!/bin/bash
6
#!/usr/bin/perl
#!/usr/bin/tcl
#!/bin/sed -f
#!/usr/awk -f
Каждая, из приведенных выше сигнатур, приводит к вызову различных интерпре-
таторов, будь то /bin/sh - командный интерпретатор по-умолчанию (bash для Linux-
систем), либо иной. При переносе сценариев с сигнатурой #!/bin/sh на другие UNIX сис-
темы, где в качестве командного интерпретатора задан другой shell, вы можете лишиться
некоторых особенностей, присущих bash. Поэтому такие сценарии должны быть POSIX
совместимыми.
Обратите внимание на то, что сигнатура должна указывать правильный путь к ин-
терпретатору, в противном случае вы получите сообщение об ошибке - как правило это
"Command not found".
Сигнатура #! может быть опущена, если вы не используете специфичных команд.
Еще раз заметим, что сигнатура #!/bin/sh вызывает командный интерпретатор по умол-
чанию -- /bin/bash в Linux-системах.
Запуск сценария.
Запустить сценарий можно командой sh scriptname или bash scriptname. (Не ре-
комендуется запуск сценария командой sh <scriptname>, поскольку это запрещает ис-
пользование устройства стандартного ввода stdin в скрипте). Более удобный вариант -
сделать файл скрипта исполняемым, командой chmod.
Это:
chmod 555 scriptname (выдача прав на чтение/исполнение любому пользователю
в системе)
или
chmod +rx scriptname (выдача прав на чтение/исполнение любому пользователю
в системе)
chmod u+rx scriptname (выдача прав на чтение/исполнение только "владельцу"
скрипта)
После того, как вы сделаете файл сценария исполняемым, вы можете запустить его
примерно такой командой ./scriptname. Если, при этом, текст сценария начинается с
корректной сигнатуры ("sha-bang"), то для его исполнения будет вызван соответствующий
интерпретатор.
И наконец, завершив отладку сценария, вы можете поместить его в каталог
/usr/local/bin (естественно, что для этого вы должны обладать правами root), чтобы
сделать его доступным для себя и других пользователей системы. После этого сценарий
можно вызвать, просто напечатав название файла в командной строке и нажав клавишу
[ENTER].
Служебные символы.
Служебные символы, используемые в текстах сценариев.
Комментарии. Строки, начинающиеся с символа # (за исключением комбинации #!) --
# являются комментариями.
# Эта строка - комментарий.
Комментарии могут располагаться и в конце строки с исполняемым кодом.
7
echo "Далее следует комментарий." # Это комментарий.
Комментариям могут предшествовать пробелы (пробел, табуляция).
# Перед комментарием стоит символ табуляции.
Исполняемые команды не могут следовать за комментарием в той же самой строке.
Пока что еще не существует способа отделения комментария от "исполняемого ко-
да", следующего за комментарием в той же строке.
Само собой разумеется, экранированный символ # в операторе echo не воспри-
нимается как начало комментария. Более того, он может использоваться в
операциях подстановки параметров и в константных числовых выражениях.
echo "Символ # не означает начало комментария."
echo 'Символ # не означает начало комментария.'
echo Символ \# не означает начало комментария.
echo А здесь символ # означает начало комментария.
echo ${PATH#*:} # Подстановка -- не комментарий.
echo $(( 2#101011 )) # База системы счисления -- не комментарий.
# Спасибо, S.C.
Кавычки " ' и \ экранируют действие символа #.
В операциях поиска по шаблону символ # так же не воспринимается как начало
комментария.
.
abc) echo "$variable = abc" ;;
xyz) echo "$variable = xyz" ;;
esac
Команда "точка". Эквивалент команды source. Это встроенная команда bash.
"точка" может являться частью имени файла . Если имя файла начинается с
точки, то это "скрытый" файл, т.е. команда ls при обычных условиях его не ото-
бражает.
bash$ touch .hidden-file
bash$ ls -l
total 10
-rw-r--r-- 1 student 4034 Jul 18 22:04 [Link]
-rw-r--r-- 1 student 877 Dec 17 2000 [Link]
bash$ ls -al
total 14
drwxrwxr-x 2 student student 1024 Aug 29 20:54 ./
drwx------ 52 student student 3072 Aug 29 20:51 ../
-rw-r--r-- 1 student student 4034 Jul 18 22:04
[Link]
-rw-r--r-- 1 student student 877 Dec 17 2000 [Link]
-rw-rw-r-- 1 student student 0 Aug 29 20:54 .hidden-file
Если подразумевается имя каталога, то одна точка означает текущий каталог и две
точки -- каталог уровнем выше, или родительский каталог.
bash$ pwd
/home/student/projects
bash$ cd .
8
bash$ pwd
/home/student/projects
bash$ cd ..
bash$ pwd
/home/student/
9
then : # Никаких действий не
else
take-some-action
fi
Как символ-заполнитель в операциях, которые предполагают наличие двух операн-
дов.
: ${username=`whoami`}
# ${username=`whoami`} без символа : выдает сообщение об ошибке,
# если "username" не является командой...
В операциях замены подстроки с подстановкой значений переменных.
В комбинации с оператором > (оператор перенаправления вывода), усекает длину
файла до нуля. Если указан несуществующий файл -- то он создается.
10
В регулярных выражениях токен * представляет любое количество (в том числе и
0) символов.
Арифметический оператор. В арифметических выражениях символ * обозначает
операцию умножения.
Двойная звездочка (два символа звездочки, следующих подряд друг за другом --
**), обозначает операцию возведения в степень.
Оператор проверки условия. В некоторых выражениях символ ? служит для провер-
?ки выполнения условия.
В конструкциях с двойными скобками, символ ? подобен трехместному оператору
языка C.
В выражениях с подстановкой параметра, символ ? проверяет -- установлена ли пе-
ременная.
символ-шаблон. Символ ? обозначает одиночный символ при подстановке в имена
файлов. В регулярных выражениях служит для обозначения одиночного символа.
Подстановка переменной.
$
var1=5
var2=23skidoo
echo $var1 # 5
echo $var2 # 23skidoo
Символ $, предшествующий имени переменной, указывает на то, что будет полу-
чено значение переменной.
end-of-line (конец строки). В регулярных выражениях, символ "$" обозначает ко-
нец строки.
Подстановка параметра.
${}
параметры командной строки.
$*,$@
$?команды,
код завершения. Переменная $? хранит код завершения последней выполненной
функции или сценария.
id процесса. Переменная $$ хранит id процесса сценария.
$$
()группа команд.
(a=hello; echo $a)
Команды, заключенные в круглые скобки исполняются в дочернем процессе --
subshell-е.
Переменные, создаваемые в дочернем процессе не видны в "родительском" сцена-
рии. Родительский процесс-сценарий, не может обращаться к переменным, созда-
ваемым в дочернем процессе.
a=123
( a=321; )
11
инициализация массивов.
Фигурные скобки.
{}
grep Linux file*.{txt,htm*}
# Поиск всех вхождений слова "Linux"
# в файлах "[Link]", "[Link]", "[Link]", "[Link]", и пр.
a=123
{ a=321; }
echo "a = $a" # a = 321 (значение, присвоенное во вложенном блоке
кода)
# Спасибо, S.C.
12
SUCCESS=0
E_NOARGS=65
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` rpm-file"
exit $E_NOARGS
fi
{
echo
echo "Описание архива:"
rpm -qpi $1 # Запрос описания.
echo
echo "Список файлов:"
rpm -qpl $1 # Запрос списка.
echo
rpm -i --test $1 # Проверка возможности установки.
if [ "$?" -eq $SUCCESS ]
then
echo "$1 может быть установлен."
else
echo "$1 -- установка невозможна!"
fi
echo
} > "$[Link]" # Перенаправление вывода в файл.
exit 0
В отличие от групп команд в (круглых скобках), описанных выше, вложенные бло-
ки кода, заключенные в {фигурные скобки} исполняются в пределах того же про-
цесса, что и сам скрипт (т.е. не вызывают запуск дочернего процесса -- subshell).
{}\;используется
pathname -- полное имя файла (т.е. путь к файлу и его имя). Чаще всего
совместно с командой find.
Обратите внимание на то, что символ ";", которым завершается ключ -exec коман-
ды find, экранируется обратным слешем. Это необходимо, чтобы предотвратить
его интерпретацию.
test.
[]Проверка истинности выражения, заключенного в квадратные скобки [ ]. Примеча-
тельно, что [ является частью встроенной команды test (и ее синонимом), И не име-
ет никакого отношения к "внешней" утилите /usr/bin/test.
элемент массива.
При работе с массивами в квадратных скобках указывается порядковый номер того
элемента массива, к которому производится обращение.
Array[1]=slot_1
echo ${Array[1]}
диапазон символов.
В регулярных выражениях, в квадратных скобках задается диапазон искомых сим-
волов.
test.
[[ ]]Проверка истинности выражения, заключенного между [[ ]]
(зарезервированное слово интерпретатора).
13
двойные круглые скобки.
(( ))Вычисляется целочисленное выражение, заключенное между двойными
круглыми скобками (( )).
Перенаправление.
> &> >& >> <Конструкция scriptname >filename перенаправляет
вывод scriptname в файл filename. Если файл filename уже существовал, то его
прежнее содержимое будет утеряно.
Конструкция command &>filename перенаправляет вывод команды command, как со
stdout, так и с stderr, в файл filename.
Конструкция command >&2 перенаправляет вывод со stdout на stderr.
Конструкция scriptname >>filename добавляет вывод scriptname к файлу
filename. Если задано имя несуществующего файла, то он создается.
подстановка процесса.
(command)>
<(command)
В, символы "<" и ">" обозначают операции.
перенаправление ввода на встроенный документ.
<<
<, >Посимвольное ASCII-сравнение.
veg1=carrots
veg2=tomatoes
echo ls -l | sh
# Передает вывод "echo ls -l" команлному интерпретатору shell,
#+ тот же результат дает простая команда "ls -l".
14
cat или echo, от которых поток данных поступает в "фильтр" (команда, которая на
входе получает данные, преобразует их и обрабатывает).
cat $filename | grep $search_word
15
set -- $variable
if [ $file1 -ot $file2 ]
then
echo "Файл $file1 был создан раньше чем $file2."
fi
if [ "$a" -eq "$b" ]
then
echo "$a равно $b."
fi
if [ "$c" -eq 24 -a "$d" -eq 47 ]
then
echo "$c равно 24, а $d равно 47."
fi
перенаправление из/в stdin или stdout. [дефис]
(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)
# Перемещение полного дерева файлов и подкаталогов из одной директории в другую
Добавим символ "-" и получим более полезный результат. Это заставит командный
интерпретатор ожидать ввода от пользователя.
bash$ file -
abc
standard input: ASCII text
bash$ file -
#!/bin/bash
standard input: Bourne-Again shell script text executable
Теперь команда принимает ввод пользователя со stdin и анализирует его.
17
верять имена файлов и предаварять их префиксом пути, например, ./-FILENAME,
$PWD/-FILENAME или $PATHNAME/-FILENAME.
Если значение переменной начинается с символа "-", то это тоже может быть при-
чиной появления ошибок.
var="-n"
echo $var
# В данном случае команда приобретет вид "echo -n" и ничего не выведет.
bash$ echo ~/
/home/student/
bash$ echo ~:
/home/student:
bash$ echo ~nonexistent-user
~nonexistent-user
~+$PWD.
текущий рабочий каталог. Соответствует содержимому внутренней переменной
18
предыдущий рабочий каталог. Соответствует содержимому внутренней перемен-
~- ной $OLDPWD.
начало-строки. В регулярных выражениях символ "^" задает начало строки текста.
^Управляющий символ
изменяет поведение терминала или управляет выводом текста. Управляющий
символ набирается с клавиатуры как комбинация CONTROL + <клавиша>.
· Ctl-C
Завершение выполнения процесса.
· Ctl-D
Выход из командного интерпретатора (log out) (аналог команды exit).
"EOF" (признак конца файла). Этот символ может выступать в качестве за-
вершающего при вводе с stdin.
· Ctl-G
"BEL" (звуковой сигнал -- "звонок").
· Ctl-H
Backspace -- удаление предыдущего символа.
#!/bin/bash
# Вставка символа Ctl-H в строку.
a="^H^H" # Два символа Ctl-H (backspace).
echo "abcdef" # abcdef
echo -n "abcdef$a " # abcd f
# Пробел в конце ^ ^ двойной шаг назад.
echo -n "abcdef$a" # abcdef
# Пробела в конце нет backspace не работает (поче-
му?).
# Результаты могут получиться совсем не те, что вы ожидаете.
echo; echo
· Ctl-J
Возврат каретки.
· Ctl-L
Перевод формата (очистка экрана (окна) терминала). Аналогична команде
clear.
· Ctl-M
Перевод строки.
· Ctl-U
Стирание строки ввода.
· Ctl-Z
Приостановка процесса.
Пробельный символ
используется как разделитель команд или переменных. В качестве пробельного
символа могут выступать -- собственно пробел (space), символ табуляции, символ
перевода строки, символ возврата каретки или комбинация из вышеперечисленных
символов. В некоторых случаях, таких как присваивание значений переменным,
использование пробельных символов недопустимо.
19
Пустые строки никак не обрабатываются командным интерпретатором и могут
свободно использоваться для визуального выделения отдельных блоков сценария.
$IFS -- переменная специального назначения. Содержит символы-разделители
полей, используемые некоторыми командами. По-умолчанию -- пробельные сим-
волы.
Переменные и параметры.
Переменные -- это одна из основ любого языка программирования. Они участвуют
в арифметических операциях, в синтаксическом анализе строк и совершенно необходимы
для абстрагирования каких либо величин с помощью символических имен. Физически пе-
ременные представляют собой ни что иное как участки памяти, в которые записана неко-
торая информация.
Подстановка переменных.
Когда интерпретатор встречает в тексте сценария имя переменной, то он вместо не-
го подставляет значение этой переменной. Поэтому ссылки на переменные называются
подстановкой переменных.
$
Необходимо всегда помнить о различиях между именем переменной и ее значени-
ем. Если variable1 -- это имя переменной, то $variable1 -- это ссылка на ее значе-
ние. "Чистые" имена переменных, без префикса $, могут использоваться только при объ-
явлении переменный, при присваивании переменной некоторого значения, при удалении
(сбросе), при экспорте и в особых случаях -- когда переменная представляет собой назва-
ние сигнала. Присваивание может производится с помощью символа = (например:
var1=27), инструкцией read и в заголовке цикла (for var2 in 1 2 3).
Заключение ссылки на переменную в двойные кавычки (" ") никак не сказывается
на работе механизма подстановки. Этот случай называется "частичные кавычки", иногда
можно встретить название "нестрогие кавычки". Одиночные кавычки (' ') заставляют ин-
терпретатор воспринимать ссылку на переменную как простой набор символов, потому в
одинарных кавычках операции подстановки не производятся. Этот случай называется
"полные", или "строгие" кавычки.
Примечательно, что написание $variable фактически является упрощенной фор-
мой написания ${variable}. Более строгая форма записи ${variable} может с успе-
хом использоваться в тех случаях, когда применение упрощенной формы записи порожда-
ет сообщения о синтаксических ошибках.
Пример. Присваивание значений переменным и подстановка значений переменных.
#!/bin/bash
a=375
hello=$a
#-------------------------------------------------------------------------
# Использование пробельных символов
# с обеих сторон символа "=" присваивания недопустимо.
20
# Если записать "VARIABLE= value",
#+ то интерпретатор попытается установить переменную
# окружения "VARIABLE" в ""
#+ и выполнить команду "value".
#-------------------------------------------------------------------------
echo $hello
echo ${hello} # Идентично предыдущей строке.
echo "$hello"
echo "${hello}"
echo
hello="A B C D"
echo $hello # A B C D
echo "$hello" # A B C D
# Здесь вы сможете наблюдать различия в выводе echo $hello и echo "$hello".
# Заключение ссылки на переменную в кавычки сохраняет пробельные символы.
echo
# --------------------------------------------------------------
# --------------------------------------------------------------
echo; echo
21
uninitialized_variable= # Объявление неинициализированной переменной
#+ (то же, что и присваивание пустого значения,
# см. выше).
echo "uninitialized_variable = $uninitialized_variable"
# Переменная содержит "пустое" значение.
uninitialized_variable=23 # Присваивание.
unset uninitialized_variable # Сброс.
echo "uninitialized_variable = $uninitialized_variable"
# Переменная содержит "пустое" значение.
echo
exit 0
# Заключение:
# Неинициализированные переменные не имеют значения, однако
#+ в арифметических операциях за значение
# таких переменных принимается число 0.
# Это недокументированная (и возможно непереносимая) возможность.
=
оператор присваивания (пробельные символы до и после оператора -- недопустимы)
Не путайте с операторами сравнения = и -eq!
Обратите внимание: символ = может использоваться как в качестве оператора присваива-
ния, так и в качестве оператора сравнения, конкретная интерпретация зависит от контек-
ста применения.
Пример. Простое присваивание.
#!/bin/bash
# Явные переменные
# Когда перед именем переменной не употребляется символ '$'?
# В операциях присваивания.
# Присваивание
a=879
echo "Значение переменной \"a\" -- $a."
22
for a in 7 8 9 11
do
echo -n "$a "
done
exit 0
Пример. Присваивание значений переменным простое и замаскированное.
#!/bin/bash
# Взято из /etc/rc.d/[Link]
R=$(cat /etc/redhat-release)
arch=$(uname -m)
#!/bin/bash
# [Link]: Целое число или строка?
23
let "a += 1"
echo "a = $a " # a = 2335
echo # Все еще целое число.
c=BB34
echo "c = $c" # c = BB34
d=${c/BB/23} # замена "BB" на "23".
# Переменная $d становится целочисленной.
echo "d = $d" # d = 2334
let "d += 1" # 2334 + 1 =
echo "d = $d" # d = 2335
echo
exit 0
Отсутствие типов - это и благословение и проклятие. С одной стороны - отсутствие
типов делает сценарии более гибкими и облегчает чтение кода. С другой - является источ-
ником потенциальных ошибок и поощряет привычку к "неряшливому" программирова-
нию.
Бремя отслеживания типа той или иной переменной полностью лежит на плечах програм-
миста. Bash не будет делать это за вас!
локальные переменные
переменные, область видимости которых ограничена блоком кода или телом функ-
ции
переменные окружения
переменные, которые затрагивают командную оболочку и порядок взаимодействия
с пользователем
В более общем контексте, каждый процесс имеет некоторое "окружение" (среду
исполнения), т.е. набор переменных, к которым процесс может обращаться за получением
24
определенной информации. В этом смысле командная оболочка подобна любому другому
процессу.
Каждый раз, когда запускается командный интерпретатор, для него создаются пе-
ременные, соответствующие переменным окружения. Изменение переменных или добав-
ление новых переменных окружения заставляет оболочку обновить свои переменные, и
все дочерние процессы (и команды, исполняемые ею) наследуют это окружение.
Если сценарий изменяет переменные окружения, то они должны "экспортироваться", т.е
передаваться окружению, локальному по отношению к сценарию. Эта функция возложена
на команду export.
Сценарий может экспортировать переменные только дочернему процессу, т.е. командам
и процессам запускаемым из данного сценария. Сценарий, запускаемый из командной
строки не может экспортировать переменные "на верх" командной оболочке. Дочерний
процесс не может экспортировать переменные родительскому процессу.
позиционные параметры
аргументы, передаваемые скрипту из командной строки -- $0, $1, $2, $3..., где $0 --
это название файла сценария, $1 -- это первый аргумент, $2 -- второй, $3 -- третий и так
далее. Аргументы, следующие за $9, должны заключаться в фигурные скобки, например:
${10}, ${11}, ${12}.
Специальные переменные $* и $@ содержат все позиционные параметры (аргументы ко-
мандной строки).
Пример. Позиционные параметры.
#!/bin/bash
25
echo "Все аргументы командной строки: "$*""
if [ $# -lt "$MINPARAMS" ]
then
echo
echo "Количество аргументов командной строки должно быть не менее
$MINPARAMS !"
fi
echo
exit 0
Скобочная нотация позиционных параметров дает довольно простой способ обра-
щения к последнему аргументу, переданному в сценарий из командной строки. Такой спо-
соб подразумевает использование косвенной адресации.
variable1_=$1_
# Это предотвратит появление ошибок, даже при отсутствии входного аргумента.
critical_argument01=$variable1_
26
# ln -s /usr/local/bin/wh /usr/local/bin/wh-radb
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` [domain-name]"
exit 65
fi
case `basename $0` in
# Проверка имени скрипта и, соответственно, имени сервера
"wh" ) whois $1@[Link];;
"wh-ripe") whois $1@[Link];;
"wh-radb") whois $1@[Link];;
"wh-cw" ) whois $1@[Link];;
* ) echo "Порядок использования: `basename $0` [domain-name]";;
esac
exit 0
Команда shift "сдвигает" позиционные параметры, в результате чего парметры
"сдвигаются" на одну позицию влево.
$1 <--- $2, $2 <--- $3, $3 <--- $4, и т.д.
Прежний аргумент $1 теряется, но аргумент $0 (имя файла сценария) остается без
изменений. Если вашему сценарию передается большое количество входных аргументов,
то команда shift позволит вам получить доступ к аргументам, с порядковым номером
больше 9, без использования {фигурных скобок}.
Пример. Использование команды shift.
#!/bin/bash
# Использование команды 'shift' с целью перебора всех аргументов командной
строки.
# Назовите файл с этим сценарием, например "shft",
#+ и вызовите его с набором аргументов, например:
# ./shft a b c def 23 skidoo
until [ -z "$1" ] # До тех пор пока не будут разобраны все входные аргумен-
ты...
do
echo -n "$1 "
shift
done
echo # Дополнительная пустая строка.
exit 0
Кавычки.
Кавычки, ограничивающие строки с обеих сторон, служат для предотвращения ин-
терпретации специальных символов, которые могут находиться в строке. (Символ называ-
ется "специальным", если он несет дополнительную смысловую нагрузку, например сим-
вол шаблона -- *.)
bash$ ls -l [Vv]*
-rw-rw-r-- 1 student student 324 Apr 2 15:05 [Link]
-rw-rw-r-- 1 student student 507 May 4 14:25 [Link]
-rw-rw-r-- 1 student student 539 Apr 14 17:11 [Link]
bash$ ls -l '[Vv]*'
ls: [Vv]*: No such file or directory
27
Вообще, желательно использовать двойные кавычки (" ") при обращении к пере-
менным. Это предотвратит интерпретацию специальных символов, которые могут содер-
жаться в именах переменных, за исключением $, ` (обратная кавычка) и \ (escape -- обрат-
ный слэш). То, что символ $ попал в разряд исключений, позволяет выполнять обращение
к переменным внутри строк, ограниченных двойными кавычками ("$variable"), т.е. вы-
полнять подстановку значений переменных.
Двойные кавычки могут быть использованы для предотвращения разбиения строки
на слова. Заключение строки в кавычки приводит к тому, что она передается как один ар-
гумент, даже если она содержит пробельные символы - разделители.
Одиночные кавычки (' ') схожи по своему действию с двойными кавычками, только
не допускают обращение к переменным, поскольку специальный символ "$" внутри оди-
нарных кавычек воспринимается как обычный символ. Внутри одиночных кавычек, любой
специальный символ, за исключением ', интерпретируется как простой символ. Одиноч-
ные кавычки ("строгие, или полные кавычки") следует рассматривать как более строгий
вариант чем двойные кавычки ("нестрогие, или неполные кавычки").
Поскольку внутри одиночных кавычек даже экранирующий (\) символ восприни-
мается как обычный символ, попытка вывести одиночную кавычку внутри строки, огра-
ниченной одинарными кавычками, не даст желаемого результата.
echo "Why can't I write 's between single quotes"
echo
# Обходной метод.
echo 'Why can'\''t I write '"'"'s between single quotes'
# |-------| |----------| |-----------------------|
# Три строки, ограниченных одинарными кавычками,
# и экранированные одиночные кавычки между ними.
Экранирование -- это способ заключения в кавычки одиночного символа. Экрани-
рующий (escape) символ (\) сообщает интерпретатору, что следующий за ним символ
должен восприниматься как обычный символ.
\"
кавычки
28
echo "----------------------------------------------------------------------"
# Что произойдет, если экранировать пробелы в списке?
ls -l /usr/X11R6/bin/xsetroot\ /sbin/dump\ $file_list
# Ошибка: первые три файла будут "слиты" воедино
# и переданы команде 'ls -l' как один аргумент
# потому что два пробела, разделяющие аргументы (слова) -- экранированы.
Кроме того, escape-символ позволяет писать многострочные команды. Обычно, каждая
команда занимает одну строку, но escape-символ позволяет экранировать символ перево-
да строки, в результате чего одна команда может занимать несколько строк.
(cd /source/directory && tar cf - . ) | \
(cd /dest/directory && tar xpvf -)
# Команда копирования дерева каталогов.
# Разбита на две строки для большей удобочитаемости.
# Альтернативный вариант:
tar cf - -C /source/directory . |
tar xpvf - -C /dest/directory
29
тически все команды и утилиты UNIX возвращают 0 в случае успешного завершения, но
имеются и исключения из правил.
Аналогичным образом ведут себя функции, расположенные внутри сценария, и сам
сценарий, возвращая код завершения. Код, возвращаемый функцией или сценарием, опре-
деляется кодом возврата последней команды. Команде exit можно явно указать код воз-
врата, в виде: exit nnn, где nnn -- это код возврата (число в диапазоне 0 - 255).
Когда работа сценария завершается командой exit без параметров, то код возврата
сценария определяется кодом возврата последней исполненной командой.
Код возврата последней команды хранится в специальной переменной $?. После
исполнения кода функции, переменная $? хранит код завершения последней команды, ис-
полненной в функции. Таким способом в Bash передается "значение, возвращаемое"
функцией. После завершения работы сценария, код возврата можно получить, обратив-
шись из командной строки к переменной $?, т.е. это будет код возврата последней коман-
ды, исполненной в сценарии.
Пример. завершение / код завершения.
#!/bin/bash
echo hello
echo $? # код возврата = 0, поскольку команда выполнилась успешно.
30
Проверка условий.
Практически любой язык программирования включает в себя условные операторы,
предназначенные для проверки условий, чтобы выбрать тот или иной путь развития собы-
тий в зависимости от этих условий. В Bash, для проверки условий, имеется команда test,
различного вида скобочные операторы и условный оператор if/then.
Конструкции проверки условий.
· Оператор if/then проверяет - является ли код завершения списка команд 0 (по-
скольку 0 означает "успех"), и если это так, то выполняет одну, или более, команд,
следующие за словом then.
· Существует специальная команда -- [ (левая квадратная скобка). Она является си-
нонимом команды test, и является встроенной командой (т.е. более эффективной, в
смысле производительности). Эта команда воспринимает свои аргументы как вы-
ражение сравнения или как файловую проверку и возвращает код завершения в со-
ответствии с результатами проверки (0 -- истина, 1 -- ложь).
· Начиная с версии 2.02, Bash предоставляет в распоряжение программиста конст-
рукцию [[ ... ]] расширенный вариант команды test, которая выполняет сравнение
способом более знакомым программистам, пишущим на других языках програм-
мирования. Обратите внимание: [[ -- это зарезервированное слово, а не команда.
Bash исполняет [[ $a -lt $b ]] как один элемент, который имеет код возврата.
Круглые скобки (( ... )) и предложение let ... так же возвращают код 0, если резуль-
татом арифметического выражения является ненулевое значение. Таким образом,
арифметические выражения могут учавствовать в операциях сравнения.
Предложение let "1<2" возвращает 0 (так как результат сравнения "1<2" -
- "1", или "истина")
(( 0 && 1 )) возвращает 1 (так как результат операции "0 && 1" -- "0",
или "ложь")
if [[ $comparison = "integer" ]]
then (( a < b ))
else
[[ $a < $b ]]
fi
then
31
echo '$a меньше $b'
fi
Пример. Что есть "истина"?
echo "Проверяется \"0\""
if [ 0 ] # ноль
then
echo "0 -- это истина."
else
echo "0 -- это ложь."
fi # 0 -- это истина.
32
echo "Проверяется \"-n \$xyz\""
if [ -n "$xyz" ]
then
echo "Пустая переменная -- это истина."
else
echo "Пустая переменная -- это ложь."
fi # Пустая переменная -- это ложь.
Else if и elif.
elif
elif -- это краткая форма записи конструкции else if. Применяется для построения
многоярусных инструкций if/then.
if [ condition1 ]
then
command1
command2
command3
elif [ condition2 ]
# То же самое, что и else if
then
command4
command5
else
default-command
fi
Конструкция if test condition-true является точным эквивалентом конструк-
ции if [ condition-true ], где левая квадратная скобка [ выполняет те же действия, что
и команда test. Закрывающая правая квадратная скобка ] не является абсолютно необхо-
димой, однако, более новые версии Bash требуют ее наличие.
Команда test -- это встроенная команда Bash, которая выполняет проверки файлов
и производит сравнение строк. Таким образом, в Bash-скриптах, команда test не вызывает
внешнюю (/usr/bin/test) утилиту, которая является частью пакета sh-utils. Аналогично,
[ не производит вызов утилиты /usr/bin/[, которая является символической ссылкой на
/usr/bin/test.
Пример. Эквиваленты команды test -- /usr/bin/test, [ ], и /usr/bin/[.
if test -z "$1"
33
then
echo "Аргументы командной строки отсутствуют."
else
echo "Первый аргумент командной строки: $1."
fi
file=/etc/passwd
if [[ -e $file ]]
then
echo "Файл паролей найден."
Fi
Строго говоря, после оператора if, ни команда test, ни квадратные скобки ( [ ] или [[
]] ) не являются обязательными.
dir=/home/bozo
34
fi
Инструкция "if COMMAND" возвращает код возврата команды COMMAND.
Точно так же, условие, находящееся внутри квадратных скобок может быть проверено без
использования оператора if.
var1=20
var2=22
[ "$var1" -ne "$var2" ] && echo "$var1 не равно $var2"
home=/home/bozo
[ -d "$home" ] || echo "каталог $home не найден."
35
-S файл является сокетом
-t файл (дескриптор) связан с терминальным устройством
Этот ключ может использоваться для проверки -- является ли файл стандартным
устройством ввода stdin ([ -t 0 ]) или стандартным устройством вывода stdout
([ -t 1 ]).
-r файл доступен для чтения (пользователю, запустившему сценарий)
-w файл доступен для записи (пользователю, запустившему сценарий)
-x файл доступен для исполнения (пользователю, запустившему сценарий)
-g set-group-id (sgid) флаг для файла или каталога установлен
Если для каталога установлен флаг sgid, то файлы, создаваемые в таком каталоге,
наследуют идентификатор группы каталога, который может не совпадать с иден-
тификатором группы, к которой принадлежит пользователь, создавший файл. Это
может быть полезно для каталогов, в которых хранятся файлы, общедоступные для
группы пользователей.
-u set-user-id (suid) флаг для файла установлен
Установленный флаг suid приводит к изменению привилегий запущенного процес-
са на привилегии владельца исполняемого файла. Исполняемые файлы, владельцем
которых является root, с установленным флагом set-user-id запускаются с при-
вилегиями root, даже если их запускает обычный пользователь. Это может ока-
заться полезным для некоторых программ (таких как pppd и cdrecord), которые
осуществляют доступ к аппаратной части компьютера. В случае отсутствия флага
suid, программы не смогут быть запущены рядовым пользователем, не обладаю-
щим привилегиями root.
-rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd
36
! "НЕ" -- логическое отрицание (инверсия) результатов всех вышеприведенных про-
верок (возвращается true если условие отсутствует).
Операции сравнения.
сравнение целых чисел.
-eq равно
if [ "$a" -eq "$b" ]
-ne не равно
if [ "$a" -ne "$b" ]
-gt больше
if [ "$a" -gt "$b" ]
-ge больше или равно
if [ "$a" -ge "$b" ]
-lt меньше
if [ "$a" -lt "$b" ]
-le меньше или равно
if [ "$a" -le "$b" ]
< меньше (внутри двойных круглых скобок )
(("$a" < "$b"))
<= меньше или равно (внутри двойных круглых скобок)
(("$a" <= "$b"))
> больше (внутри двойных круглых скобок)
(("$a" > "$b"))
>= больше или равно (внутри двойных круглых скобок)
(("$a" >= "$b"))
сравнение строк.
= равно
if [ "$a" = "$b" ]
== равно
if [ "$a" == "$b" ]
Синоним оператора =.
!= не равно
if [ "$a" != "$b" ]
Этот оператор используется при поиске по шаблону внутри [[ ... ]].
37
< меньше, в смысле величины ASCII-кодов
if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
Обратите внимание! Символ "<" необходимо экранировать внутри [ ].
> больше, в смысле величины ASCII-кодов
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
Обратите внимание! Символ ">" необходимо экранировать внутри [ ].
-z строка "пустая", т.е. имеет нулевую длину
-n строка не "пустая".
Оператор -n требует, чтобы строка была заключена в кавычки внутри квадратных
скобок. Как правило, проверка строк, не заключенных в кавычки, оператором ! -z,
или просто указание строки без кавычек внутри квадратных скобок, проходит нор-
мально, однако это небезопасная, с точки зрения отказоустойчивости, практика.
Всегда заключайте проверяемую строку в кавычки.
Пример. Операции сравнения.
a=4
b=5
# Здесь переменные "a" и "b" могут быть как целыми числами, так и строками.
# Здесь наблюдается некоторое размывание границ
#+ между целочисленными и строковыми переменными,
#+ поскольку переменные в Bash не имеют типов.
if [ "$a" != "$b" ]
then
echo "$a не равно $b."
echo "(сравнение строк)"
# "4" != "5"
# ASCII 52 != ASCII 53
fi
38
else
echo "Строка \"string1\" пустая."
fi
# Неверный результат.
# Выводится сообщение о том, что $string1 не пустая,
#+не смотря на то, что она не была инициализирована.
# Попробуем еще раз.
string1=initialized
string1="a = b"
39
[[ condition1 && condition2 ]]
Операторы -o и -a употребляются совместно с командой test или внутри одинарных квад-
ратных скобок.
if [ "$exp1" -a "$exp2" ]
if [ condition1 ]
then
if [ condition2 ]
then
do-something # Только если оба условия "condition1" и "condition2" ис-
тинны.
fi
fi
Операции.
Операторы.
Присваивание.
variable assignment
Инициализация переменной или изменение ее значения
= Универсальный оператор присваивания, пригоден как для сравнения целых чисел,
так и для сравнения строк.
var=27
category=minerals # Пробелы до и после оператора "=" -- недопустимы.
Пусть вас не смущает, что оператор присваивания ("="), по своему внешнему виду, совпа-
дает с оператором сравнения (=).
+ сложение
- вычитание
* умножение
/ деление
** возведение в степень
let "z=5**3"
echo "z = $z" # z = 125
40
% модуль (деление по модулю), возвращает остаток от деления
bash$ echo `expr 5 % 3`
: $((n = $n + 1))
# оператор ":" обязателен, поскольку в противном случае, Bash будет
#+ интерпретировать выражение "$((n = $n + 1))" как команду.
echo -n "$n "
n=$(($n + 1))
echo -n "$n "
: $[ n = $n + 1 ]
# оператор ":" обязателен, поскольку в противном случае, Bash будет
#+ интерпретировать выражение "$[ n = $n + 1 ]" как команду.
# Не вызывает ошибки даже если "n" содержит строку.
echo -n "$n "
Целые числа в Bash фактически являются знаковыми длинными целыми (32-бит), с
диапазоном изменений от -2147483648 до 2147483647. Если в результате какой либо опе-
рации эти пределы будут превышены, то результат получится ошибочным.
a=2147483646
echo "a = $a" # a = 2147483646
let "a+=1" # Увеличить "a" на 1.
echo "a = $a" # a = 2147483647
let "a+=1" # увеличить "a" еще раз, с выходом за границы диапазона.
echo "a = $a" # a = -2147483648
# ОШИБКА! (выход за границы диапазона)
41
<<= "сдвиг-влево-равно"
let "var <<= 2" значение переменной var сдвигается влево на 2 бита (умножает-
ся на 4)
>> сдвиг вправо на 1 бит (деление на 2)
>>= "сдвиг-вправо-равно" (имеет смысл обратный <<=)
& по-битовое И (AND)
&= "по-битовое И-равно"
| по-битовое ИЛИ (OR)
|= "по-битовое ИЛИ-равно"
~ по-битовая инверсия
! По-битовое отрицание
^ по-битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR)
^= "по-битовое ИСКЛЮЧАЮЩЕЕ-ИЛИ-равно"
логические операции.
&& логическое И (and)
if [ $condition1 ] && [ $condition2 ]
# То же самое, что: if [ $condition1 -a $condition2 ]
# Возвращает true если оба операнда condition1 и condition2 истинны...
a=24
b=47
42
# Опции -a и -o предоставляют
#+ альтернативный механизм проверки условий.
a=rhino
b=crocodile
if [ "$a" = rhino ] && [ "$b" = crocodile ]
then
echo "Пятая проверка прошла успешно."
else
echo "Пятая проверка не прошла."
fi
bash$ echo $(( 1 && 2 )) $((3 && 0)) $((4 || 0)) $((0 || 0))
1 0 1 0
прочие операции.
, запятая
С помощью оператора запятая можно связать несколько арифметических в одну
последовательность. При разборе таких последовательностей, командный интер-
претатор вычисляет все выражения (которые могут иметь побочные эффекты) в
последовательности и возвращает результат последнего.
43
Пример. Различные представления числовых констант.
# [Link]: Различные представления числовых констант.
# Десятичное: по-умолчанию
let "dec = 32"
echo "десятичное число = $dec" # 32
# Вобщем-то ничего необычного.
# Важное замечание:
# --------------
# Использование символов, для записи числа, выходящих за диапазон,
#+ соответствующий ОСНОВЕ счисления
#+ будет приводить к появлению сообщений об ошибках.
Переменные.
Правильное использование переменных может придать сценариям дополнительную мощь
и гибкость, а для этого необходимо изучить все тонкости и нюансы.
Внутренние переменные.
Встроенные переменные
$BASH
44
путь к исполняемому файлу Bash
$DIRSTACK
содержимое вершины стека каталогов (который управляется командами pushd и
popd)
Эта переменная соответствует команде dirs, за исключением того, что dirs показы-
вает полное содержимое всего стека каталогов.
$EDITOR
заданный по-умолчанию редактор, вызываемый скриптом, обычно vi или emacs.
$EUID
"эффективный" идентификационный номер пользователя (Effective User ID)
Идентификационный номер пользователя, права которого были получены, воз-
можно с помощью команды su.
$GLOBIGNORE
Перечень шаблонных символов, которые будут проигнорированы при выполнении
подстановки имен файлов (globbing) .
$GROUPS
группы, к которым принадлежит текущий пользователь
Это список групп (массив) идентификационных номеров групп для текущего поль-
зователя, как эо записано в /etc/passwd.
$HOME
домашний каталог пользователя, как правило это /home/username
$HOSTNAME
Сетевое имя хоста устанавливается командой hostname во время исполнения ини-
циализирующих сценариев на загрузке системы. Внутренняя переменная
$HOSTNAME Bash получает свое значение посредством вызова функции
gethostname().
$HOSTTYPE
тип машины
Подобно $MACHTYPE, идентифицирует аппаратную архитектуру.
45
По-умолчанию -- пробельный символ (пробел, табуляция и перевод строки), но
может быть изменен, например, для разбора строк, в которых отдельные поля раз-
делены запятыми. Обратите внимание: при составлении содержимого переменной
$*, Bash использует первый символ из $IFS для разделения аргументов.
$LC_COLLATE
Чаще всего устанавливается в .bashrc или /etc/profile, эта переменная задает
порядок сортировки символов, в операциях подстановки имен файлов и в поиске
по шаблону. При неверной настройке переменной LC_COLLATE можно получить
весьма неожиданные результаты.
$LC_CTYPE
Эта внутренняя переменная определяет кодировку символов. Используется в опе-
рациях подстановки и поиске по шаблону.
$LINENO
Номер строки исполняемого сценария. Эта переменная имеет смысл только внутри
исполняемого сценария и чаще всего применяется в отладочных целях.
# *** BEGIN DEBUG BLOCK ***
last_cmd_arg=$_ # Запомнить.
$MACHTYPE
аппаратная архитектура
Идентификатор аппаратной архитектуры.
46
bash$ echo $PATH
/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/sbin:/usr/sbin
$SECONDS
Время работы сценария в секундах.
TIME_LIMIT=10
INTERVAL=1
echo "Для прерывания работы сценария, ранее чем через $TIME_LIMIT се-
кунд, нажмите Control-C."
echo
$SHELLOPTS
список допустимых опций интерпретатора shell. Переменная доступна только для
чтения.
47
$0, $1, $2 и т.д.
аргументы передаются... из командной строки в сценарий, функциям или команде
set
$#
количество аргументов командной строки, или позиционных.
$*
Все аргументы в виде одной строки (слова)
$@
То же самое, что и $*, но при этом каждый параметр представлен как отдельная
строка (слово), т.е. параметры не подвергаются какой либо интерпретации.
Пример. arglist: Вывод списка аргументов с помощью переменных $* и $@
#!/bin/bash
# Вызовите сценарий с несколькими аргументами,
# например: "один два три".
E_BADARGS=65
if [ ! -n "$1" ]
then
echo "Порядок использования: `basename $0` argument1 argument2 и
т.д."
exit $E_BADARGS
fi
echo
index=1
index=1
После команды shift (сдвиг), первый аргумент, в переменной $@, теряется, а ос-
тальные сдвигаются на одну позицию "вниз" (или "влево", если хотите).
#!/bin/bash
# Вызовите сценарий в таком виде: ./scriptname 1 2 3 4 5
echo "$@" # 1 2 3 4 5
48
shift
echo "$@" # 2 3 4 5
shift
echo "$@" # 3 4 5
LOG=$[Link]
COMMAND1="sleep 100"
echo "Запись в лог всех PID фоновых процессов, запущенных из сценария:
$0" >> "$LOG"
# Таким образом возможен мониторинг и удаление процессов
# по мере необходимости.
echo >> "$LOG"
# Команды записи в лог.
echo -n "PID of \"$COMMAND1\": " >> "$LOG"
${COMMAND1} &
echo $! >> "$LOG"
# PID процесса "sleep 100": 1506
$_
Специальная переменная, содержит последний аргумент предыдущей команды.
Пример. Переменная "подчеркивание".
#!/bin/bash
echo $_ # /bin/bash
# Для запуска сценария был вызван /bin/bash.
du >/dev/null # Подавление вывода.
echo $_ # du
ls -al >/dev/null # Подавление вывода.
echo $_ # -al (последний аргумент)
:
echo $_ # :
$?
Код возврата команды, функции или скрипта
$$
PID самого процесса-сценария. Переменная $$ часто используется при генерации
"уникальных" имен для временных файлов. Обычно это проще чем вызов mktemp.
Работа со строками.
Bash поддерживает на удивление большое количество операций над строками. К
сожалению, этот раздел Bash испытывает недостаток унификации. Одни операции явля-
ются подмножеством операций подстановки параметров, а другие - совпадают с функцио-
нальностью команды UNIX -- expr. Это приводит к противоречиям в синтаксисе команд и
49
перекрытию функциональных возможностей, не говоря уже о возникающей путанице.
Длина строки.
${#string}
expr length $string
expr "$string" : '.*'
stringZ=abcABC123ABCabc
echo ${#stringZ} # 15
echo `expr length $stringZ` # 15
echo `expr "$stringZ" : '.*'` # 15
#!/bin/bash
# [Link]
len=${#line}
if [ "$len" -lt "$MINLEN" ]
then echo # Добавление пустой строки после последней строки параграфа.
fi
done
exit 0
Длина подстроки в строке (подсчет совпадающих символов ведется с начала строки).
expr match "$string" '$substring'
где $substring -- регулярное выражение.
expr "$string" : '$substring'
где $substring -- регулярное выражение.
stringZ=abcABC123ABCabc
# |------|
echo `expr match "$stringZ" 'abc[A-Z]*.2'` # 8
echo `expr "$stringZ" : 'abc[A-Z]*.2'` # 8
Index.
expr index $string $substring
Номер позиции первого совпадения в $string c первым символом в $substring.
stringZ=abcABC123ABCabc
echo `expr index "$stringZ" C12` # 6
# позиция символа C.
50
echo `expr index "$stringZ" 1c` # 3
# символ 'c' (в #3 позиции) совпал раньше, чем '1'.
stringZ=abcABC123ABCabc
# 0123456789.....
# Индексация начинается с 0.
stringZ=abcABC123ABCabc
# 123456789......
# Индексация начинается с 1.
51
expr match "$string" '\($substring\)'
Находит и извлекает первое совпадение $substring в $string, где $substring --
это регулярное выражение.
expr "$string" : '\($substring\)'
Находит и извлекает первое совпадение $substring в $string, где $substring --
это регулярное выражение.
stringZ=abcABC123ABCabc
# =======
echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1
echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'` # abcABC1
echo `expr "$stringZ" : '\(.......\)'` # abcABC1
# Все вышеприведенные операции дают один и тот же результат.
${string%substring}
Удаление самой короткой, из найденных, подстроки $substring в строке $string.
Поиск ведется с конца строки
${string%%substring}
Удаление самой длинной, из найденных, подстроки $substring в строке $string.
Поиск ведется с конца строки
52
stringZ=abcABC123ABCabc
# ||
# |------------|
echo ${stringZ%%b*c} # a
# Удаляется самое длинное совпадение. Поиск ведется с конца $stringZ.
Замена подстроки.
${string/substring/replacement}
Замещает первое вхождение $substring строкой $replacement.
${string//substring/replacement}
Замещает все вхождения $substring строкой $replacement.
stringZ=abcABC123ABCabc
stringZ=abcABC123ABCabc
Подстановка параметров.
Работа с переменными и/или подстановка их значений.
${parameter}
То же самое, что и $parameter, т.е. значение переменной parameter. В отдельных
случаях, при возникновении неоднозначности интерпретации, корректно будет ра-
ботать только такая форма записи: ${parameter}.
Может использоваться для конкатенации (слияния) строковых переменных.
your_id=${USER}-on-${HOSTNAME}
echo "$your_id"
#
echo "Старый \$PATH = $PATH"
PATH=${PATH}:/opt/bin #Добавление /opt/bin в $PATH.
53
echo "Новый \$PATH = $PATH"
${parameter-default}, ${parameter:-default}
Если параметр отсутствует, то используется значение по-умолчанию.
echo ${username-`whoami`}
# Вывод результата работы команды `whoami`, если переменная $username
не установлена.
username0=
# переменная username0 объявлена, но инициализирована
# "пустым" значением.
echo "username0 = ${username0-`whoami`}"
# Вывод после символа "=" отсутствует.
username2=
# переменная username2 объявлена, но инициализирована "пустым" значени-
ем.
echo "username2 = ${username2:-`whoami`}"
# Выводится имя пользователя, выданное командой `whoami`, поскольку
#+здесь употребляется конструкция ":-" , а не "-".
DEFAULT_FILENAME=[Link]
filename=${1:-$DEFAULT_FILENAME}
# Если имя файла не задано явно, то последующие операторы будут рабо-
тать
#+ с файлом "[Link]".
#
${parameter=default}, ${parameter:=default}
Если значения параметров не задананы явно, то они принимают значения по-
умолчанию.
Оба метода задания значений по-умолчанию до определенной степени идентичны.
Символ : имеет значение только когда $parameter был инициализирован "пустым"
(null) значением, как показано выше.
echo ${username=`whoami`}
# Переменная "username" принимает значение, возвращаемое командой
`whoami`.
${parameter+alt_value}, ${parameter:+alt_value}
Если параметр имеет какое либо значение, то используется alt_value, иначе -- null
("пустая" строка).
Оба варианта до определенной степени идентичны. Символ : имеет значение толь-
ко если parameter объявлен и "пустой", см. ниже.
54
echo "###### \${parameter+alt_value} ########"
echo
a=${param1+xyz}
echo "a = $a" # a =
param2=
a=${param2+xyz}
echo "a = $a" # a = xyz
param3=123
a=${param3+xyz}
echo "a = $a" # a = xyz
echo
echo "###### \${parameter:+alt_value} ########"
echo
a=${param4:+xyz}
echo "a = $a" # a =
param5=
a=${param5:+xyz}
echo "a = $a" # a =
# Вывод отличается от a=${param5+xyz}
param6=123
a=${param6+xyz}
echo "a = $a" # a = xyz
${parameter?err_msg}, ${parameter:?err_msg}
Если parameter инициализирован, то используется его значение, в противном слу-
чае -- выводится err_msg.
Обе формы записи можно, до определенной степени, считать идентичными. Сим-
вол : имеет значение только когда parameter инициализирован "пустым" значени-
ем, см. ниже.
Пример . Подстановка параметров и сообщения об ошибках.
# ------------------------------------------------------
ThisVariable=Value-of-ThisVariable
55
# Обратите внимание, в строковые переменные могут быть записаны
#+ символы, которые запрещено использовать в именах переменных.
: ${ThisVariable?}
echo "Value of ThisVariable is $ThisVariable".
# То же самое: dummy_variable=${ZZXy23AB?}
# dummy_variable=${ZZXy23AB?"Переменная ZXy23AB не инициализи-
рована."}
#
# echo ${ZZXy23AB?} >/dev/null
HERE=0
exit $HERE # Сценарий завершит работу не здесь.
Длина переменной / Удаление подстроки.
${#var}
String length (число символов в переменной $var). В случае массивов, команда
${#array} возвращает длину первого элемента массива.
Исключения:
· ${#*} и ${#@} возвращает количество аргументов (позиционных параметров).
· Для массивов, ${#array[*]} и ${#array[@]} возвращает количество элементов в
массиве.
Пример. Длина переменной.
#!/bin/bash
# [Link]
E_NO_ARGS=65
if [ $# -eq 0 ] # Для работы скрипта необходим хотя бы один входной
параметр.
then
echo "Вызовите сценарий с одним или более параметром командной стро-
ки."
exit $E_NO_ARGS
fi
var01=abcdEFGH28ij
echo "var01 = ${var01}"
echo "Length of var01 = ${#var01}"
exit 0
#!/bin/bash
# Поиск по шаблону в операциях подстановки параметров # ## % %%.
56
var1=abcd12345abc6789
pattern1=a*c # * (символ шаблона), означает любые символы между a и c.
echo
echo "var1 = $var1" # abcd12345abc6789
echo "var1 = ${var1}" # abcd12345abc6789 (альтернативный вариант)
echo "Число символов в ${var1} = ${#var1}"
echo "pattern1 = $pattern1" # a*c (между 'a' и 'c' могут быть любые симво-
лы)
echo
echo; echo
ARGS=2
E_BADARGS=65
if [ $# -ne "$ARGS" ]
then
echo "Порядок использования: `basename $0` old_file_suffix new_file_suffix"
exit $E_BADARGS
fi
57
Эти конструкции перекочевали в Bash из ksh.
${var:pos}
Подстанавливается значение переменной var, начиная с позиции pos.
${var:pos:len}
Подстанавливается значение переменной var, начиная с позиции pos, не более len
символов.
${var/Pattern/Replacement}
Первое совпадение с шаблоном Pattern, в переменной var замещается подстрокой
Replacement.
Если подстрока Replacement отсутствует, то найденное совпадение будет удалено.
${var//Pattern/Replacement}
Глобальная замена. Все найденные совпадения с шаблоном Pattern, в перемен-
ной var, будут замещены подстрокой Replacement.
Как и в первом случае, если подстрока Replacement отсутствует, то все найденные
совпадения будут удалены.
${var/#Pattern/Replacement}
Если в переменной var найдено совпадение с Pattern, причем совпадающая под-
строка расположена в начале строки (префикс), то оно заменяется на Replacement.
Поиск ведется с начала строки
${var/%Pattern/Replacement}
Если в переменной var найдено совпадение с Pattern, причем совпадающая под-
строка расположена в конце строки (суффикс), то оно заменяется на Replacement.
Поиск ведется с конца строки
${!varprefix*}, ${!varprefix@}
Поиск по шаблону всех, ранее объявленных переменных, имена которых начина-
ются с varprefix.
xyz23=whatever
xyz24=
#!/bin/bash
58
MAXCOUNT=10
count=1
echo
echo "$MAXCOUNT случайных чисел:"
echo "-----------------"
while [ "$count" -le $MAXCOUNT ] # Генерация 10 ($MAXCOUNT) случайных
чисел.
do
number=$RANDOM
echo $number
let "count += 1" # Нарастить счетчик.
done
echo "-----------------"
RANGE=500
FLOOR=200
number=0 #initialize
while [ "$number" -le $FLOOR ]
do
number=$RANDOM
let "number %= $RANGE" # Ограничение "сверху" числом $RANGE.
done
echo "Случайное число в диапазоне от $FLOOR до $RANGE --- $number"
echo
59
#!/bin/bash
# Работа с переменными в стиле языка C.
Циклы и ветвления.
Управление ходом исполнения - один из ключевых моментов структурной органи-
зации сценариев на языке командной оболочки. Циклы и переходы являются теми инст-
рументальными средствами, которые обеспечивают управление порядком исполнения ко-
манд.
Циклы.
Цикл - это блок команд, который исполняется многократно до тех пор, пока не будет вы-
полнено условие выхода из цикла.
циклы for
for (in)
Это одна из основных разновидностей циклов. И она значительно отличается от
аналога в языке C.
for arg in [list]
do
команда(ы)...
done
for arg in "$var1" "$var2" "$var3" ... "$varN"
# На первом проходе, $arg = $var1
# На втором проходе, $arg = $var2
# На третьем проходе, $arg = $var3
# На N-ном проходе, $arg = $varN
60
Пример. Простой цикл for.
# Список планет.
for planet in Меркурий Венера Земля Марс Юпитер Сатурн Уран Нептун Плу-
тон
do
echo $planet
done
echo
for a
do
echo -n "$a "
done
# Список аргументов не задан, поэтому цикл работает с переменной '$@'
#+ (список аргументов командной строки, включая пробельные символы).
while
Оператор while проверяет условие перед началом каждой итерации и если условие
истинно (если код возврата равен 0), то управление передается в тело цикла. В от-
личие от циклов for, циклы while используются в тех случаях, когда количество
итераций заранее не известно.
while [condition]
61
do
command...
done
Как и в случае с циклами for/in, при размещении ключевого слова do в одной стро-
ке с объявлением цикла, необходимо вставлять символ ";" перед do.
while [condition] ; do
Пример. Простой цикл while.
var0=0
LIMIT=10
Оператор while может иметь несколько условий. Но только последнее из них опре-
деляет возможность продолжения цикла. В этом случае синтаксис оператора цикла
должен быть несколько иным.
Пример. Цикл while с несколькими условиями.
var1=unset
previous=$var1
Как и в случае с for, цикл while может быть записан в C-подобной нотации, с ис-
пользованием двойных круглых скобок
Пример. C-подобный синтаксис оформления цикла while.
62
# [Link]: Цикл перебора от 1 до 10.
LIMIT=10
a=1
while [ "$a" -le $LIMIT ]
do
echo -n "$a "
let "a+=1"
done # Пока ничего особенного.
until
Оператор цикла until проверяет условие в начале каждой итерации, но в отличие от
while итерация возможна только в том случае, если условие ложно.
until [condition-is-true]
do
command...
done
Обратите внимание: оператор until проверяет условие завершения цикла ПЕРЕД
очередной итерацией, а не после, как это принято в некоторых языках программи-
рования.
Как и в случае с циклами for/in, при размещении ключевого слова do в одной стро-
ке с объявлением цикла, необходимо вставлять символ ";" перед do.
until [condition-is-true] ; do
Пример. Цикл until.
until [ "$var1" = end ] # Проверка условия производится
# в начале итерации.
do
echo "Введите значение переменной #1 "
echo "(end - выход)"
read var1
echo "значение переменной #1 = $var1"
done
Вложенные циклы.
Цикл называется вложенным, если он размещается внутри другого цикла. На пер-
вом проходе, внешний цикл вызывает внутренний, который исполняется до своего завер-
шения, после чего управление передается в тело внешнего цикла. На втором проходе
внешний цикл опять вызывает внутренний. И так до тех пор, пока не завершится внешний
цикл. Само собой, как внешний, так и внутренний циклы могут быть прерваны командой
break.
63
Пример. Вложенный цикл.
Операторы выбора.
Инструкции case и select технически не являются циклами, поскольку не преду-
64
сматривают многократное исполнение блока кода. Однако, они, как и циклы, управляют
ходом исполнения программы, в зависимости от начальных или конечных условий.
case (in) / esac
Конструкция case эквивалентна конструкции switch в языке C/C++. Она позволяет
выполнять тот или иной участок кода, в зависимости от результатов проверки ус-
ловий. Она является, своего рода, краткой формой записи большого количества
операторов if/then/else и может быть неплохим инструментом при создании разного
рода меню.
case "$variable" in
"$condition1" )
command...
;;
"$condition2" )
command...
;;
esac
· Заключать переменные в кавычки необязательно, поскольку здесь не производится
разбиения на отдельные слова.
· Каждая строка с условием должна завершаться правой (закрывающей) круглой
скобкой ).
· Каждый блок команд, отрабатывающих по заданному условию, должен завершать-
ся двумя символами точка-с-запятой ;;.
· Блок case должен завершаться ключевым словом esac (case записанное в обратном
порядке).
Пример. Использование case.
case "$Keypress" in
[a-z] ) echo "буква в нижнем регистре";;
[A-Z] ) echo "Буква в верхнем регистре";;
[0-9] ) echo "Цифра";;
* ) echo "Знак пунктуации, пробел или что-то другое";;
esac # Допускается указывать диапазоны
# символов в [квадратных скобках].
Очень хороший пример использования case для анализа аргументов, переданных из
командной строки.
case "$1" in
"") echo "Порядок использования: ${0##*/} <filename>"; exit 65;;
# Параметры командной строки отсутствуют,
# или первый параметр -- "пустой".
# Обратите внимание на ${0##*/} это подстановка параметра
${var##pattern}. В результате получается $0.
-*) FILENAME=./$1;; # Если имя файла (аргумент $1) начинается с "-",
# то заменить его на ./$1
# тогда параметр не будет восприниматься
# как ключ команды.
65
* ) FILENAME=$1;; # В противном случае -- $1.
esac
select
Оператор select был заимствован из Korn Shell, и является еще одним инструмен-
том, используемым при создании меню.
select variable [in list]
do
command...
break
done
Этот оператор предлагает пользователю выбрать один из представленных
вариантов. Примечательно, что select по-умолчанию использует в качестве при-
глашения к вводу (prompt) -- PS3 (#? ), который легко изменить.
Пример. Создание меню с помощью select.
Подстановка команд.
Подстановка команд -- это подстановка результатов выполнения команды или даже се-
рии команд; буквально, эта операция позволяет вызвать команду в другом окружении.
Классический пример подстановки команд -- использование обратных одиночных кавы-
чек (`...`). Команды внутри этих кавычек представляют собой текст командной строки.
script_name=`basename $0`
66
echo "Имя этого файла-сценария: $script_name."
Вывод от команд может использоваться: как аргумент другой команды, для уста-
новки значения переменной и даже для генерации списка аргументов цикла for.
textfile_listing=`ls *.txt`
# Переменная содержит имена всех файлов *.txt в текущем каталоге.
echo $textfile_listing
Даже когда не происходит разбиения на слова, операция подстановки команд может уда-
лять завершающие символы перевода строки.
cd "`pwd`" # Ошибка:
# bash: cd: /tmp/dir with trailing newline: No such file or directory
67
клавиш = 1."
# Нажмите RETURN, и получите: "Количество нажатых клавиш = 0."
# Символ перевода строки будет "съеден" операцией подстановки команды.
Подстановка команд позволяет даже записывать в переменные содержимое целых файлов,
с помощью перенаправления или команды cat.
# Замечание 1:
# Удаляются символы перевода строки.
#
# Замечание 2:
# В переменные можно записать даже управляющие символы.
if [ -f /fsckoptions ]; then
fsckoptions=`cat /fsckoptions`
...
fi
#
if [ -e "/proc/ide/${disk[$device]}/media" ] ; then
hdmedia=`cat /proc/ide/${disk[$device]}/media`
...
fi
#
if [ ! -n "`uname -r | grep -- "-"`" ]; then
ktag="`cat /proc/version`"
...
fi
#
if [ $usb = "1" ]; then
sleep 5
mouseoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E
"^I.*Cls=03.*Prot=02"`
kbdoutput=`cat /proc/bus/usb/devices 2>/dev/null|grep -E
"^I.*Cls=03.*Prot=01"`
...
fi
Подстановка команд, позволяет записать в переменную результаты выполнения цикла.
Ключевым моментом здесь является команда echo, в теле цикла.
Пример. Запись результатов выполнения цикла в переменную.
variable1=`for i in 1 2 3 4 5
do
echo -n "$i" # Здесь 'echo' -- это ключевой момент
done`
i=0
variable2=`while [ "$i" -lt 10 ]
do
echo -n "$i" # Опять же, команда 'echo' просто необходима.
let "i += 1" # Увеличение на 1.
68
done`
Арифметические подстановки.
Арифметические подстановки - это мощный инструмент, предназначенный для выполне-
ния арифметических операций в сценариях. Перевод строки в числовое выражение произ-
водится с помощью обратных одиночных кавычек, двойных круглых скобок или предло-
жения let.
Вариации.
Арифметические подстановки в обратных одиночных кавычках (часто используются со-
вместно с командой expr)
z=`expr $z + 3` # Команда 'expr' вычисляет значение выраже-
ния.
z=$(($z+3))
# $((EXPRESSION)) -- это подстановка арифметического выражения.
# Не путайте с подстановкой команд.
let z=z+3
let "z += 3" # Кавычки позволяют вставлять пробелы и специальные опе-
раторы.
# Оператор 'let' вычисляет арифметическое выражение,
# это не подстановка арифметического выражения.
Перенаправление ввода/вывода.
В системе по-умолчанию всегда открыты три "файла" -- stdin (клавиатура), stdout
(экран) и stderr (вывод сообщений об ошибках на экран). Эти, и любые другие открытые
файлы, могут быть перенаправлены. В данном случае, термин "перенаправление" означает
получить вывод из файла, команды, программы, сценария или даже отдельного блока в
сценарии и передать его на вход в другой файл, команду, программу или сценарий.
С каждым открытым файлом связан дескриптор файла. Дескрипторы файлов stdin,
stdout и stderr - 0, 1 и 2, соответственно. При открытии дополнительных файлов, деск-
рипторы с 3 по 9 остаются незанятыми. Иногда дополнительные дескрипторы могут со-
служить неплохую службу, временно сохраняя в себе ссылку на stdin, stdout или
stderr. Это упрощает возврат дескрипторов в нормальное состояние после сложных ма-
69
нипуляций с перенаправлением и перестановками.
COMMAND_OUTPUT >
# Перенаправление stdout (вывода) в файл.
# Если файл отсутствовал, то он создается,
# иначе -- перезаписывается.
: > filename
# Операция > усекает файл "filename" до нулевой длины.
# Если до выполнения операции файла не существовало,
# то создается новый файл с нулевой длиной
# (тот же эффект дает команда 'touch').
# Символ : выступает здесь в роли местозаполнителя,
# не выводя ничего.
> filename
# Операция > усекает файл "filename" до нулевой длины.
# Если до выполнения операции файла не существовало,
# то создается новый файл с нулевой длиной
# (тот же эффект дает команда 'touch').
# (тот же результат, что и выше -- ": >",
# но этот вариант неработоспособен
# в некоторых командных оболочках.)
COMMAND_OUTPUT >>
# Перенаправление stdout (вывода) в файл.
# Создает новый файл, если он отсутствовал,
# иначе -- дописывает в конец файла.
1>filename
# Перенаправление вывода (stdout) в файл "filename".
1>>filename
# Перенаправление вывода (stdout) в файл "filename",
# файл открывается в режиме добавления.
2>filename
# Перенаправление stderr в файл "filename".
2>>filename
# Перенаправление stderr в файл "filename",
# файл открывается в режиме добавления.
&>filename
# Перенаправление stdout и stderr в файл "filename".
#====================================================================
# Перенаправление stdout, только для одной строки.
LOGFILE=[Link]
70
# Перенаправление stderr, только для одной строки.
ERRORFILE=[Link]
bad_command1 2>$ERRORFILE
# Сообщение об ошибке запишется в $ERRORFILE.
bad_command2 2>>$ERRORFILE
# Сообщение об ошибке добавится в конец $ERRORFILE.
#======================================================================
2>&1
# Перенаправляется stderr на stdout.
# Сообщения об ошибках передаются туда же, куда и стандартный вы-
вод.
i>&j
# Перенаправляется файл с дескриптором i в j.
# Вывод в файл с дескриптором i передается в файл с дескриптором
j.
>&j
# Перенаправляется файл с дескриптором 1 (stdout) в файл с деск-
риптором j.
# Вывод на stdout передается в файл с дескриптором j.
0< FILENAME
< FILENAME
# Ввод из файла.
# Парная команде ">", часто встречается в комбинации с ней.
#
# grep search-word <filename
[j]<>filename
# Файл "filename" открывается на чтение и запись, и связывается с
дескриптором "j".
# Если "filename" отсутствует, то он создается.
# Если дескриптор "j" не указан, то, по-умолчанию, бередся деск-
риптор 0, stdin.
#
# Как одно из применений этого - запись в конкретную позицию в
файле.
echo 1234567890 > File # Записать строку в файл "File".
exec 3<> File # Открыть "File" и связать
# с дескриптором 3.
read -n 4 <&3 # Прочитать 4 символа.
echo -n . >&3 # Записать символ точки.
exec 3>&- # Закрыть дескриптор 3.
cat File # ==> 1234.67890
# Произвольный доступ, да и только!
|
# Конвейер (канал).
# Универсальное средство для объединения команд в одну цепочку.
# Похоже на ">", но на самом деле -- более обширная.
# Используется для объединения команд, сценариев,
# файлов и программ в одну цепочку (конвейер).
cat *.txt | sort | uniq > result-file
# Содержимое всех файлов .txt сортируется,
# удаляются повторяющиеся строки,
# результат сохраняется в файле "result-file".
Операции перенаправления и/или конвейеры могут комбинироваться в одной командной
строке.
71
command < input-file > output-file
Использование exec.
Команда exec <filename перенаправляет ввод со stdin на файл. С этого момента весь
ввод, вместо stdin (обычно это клавиатура), будет производиться из этого файла. Это да-
ет возможность читать содержимое файла, строку за строкой, и анализировать каждую
введенную строку с помощью sed и/или awk.
Пример. Перенаправление stdin с помощью exec
#!/bin/bash
# Перенаправление stdin с помощью 'exec'.
exec 6<&0 # Связать дескр. #6 со стандартным вводом (stdin).
# Сохраняя stdin.
echo
echo "Следующие строки были прочитаны из файла."
72
echo "-----------------------------------------"
echo $a1
echo $a2
LOGFILE=[Link]
# ----------------------------------------------------------- #
# Весь вывод от команд, в данном блоке, записывается в файл $LOGFILE.
# ----------------------------------------------------------- #
echo
echo "== stdout восстановлено в значение по-умолчанию == "
echo
ls -al
Внутренние команды.
Внутренняя команда - это команда, которая встроена непосредственно в Bash.
Команды делаются встроенными либо из соображений производительности - встроенные
команды исполняются быстрее, чем внешние, которые, как правило, запускаются в дочер-
73
нем процессе, либо из-за необходимости прямого доступа к внутренним структурам ко-
мандного интерпретатора.
Действие, когда какая либо команда или сама командная оболочка инициирует (по-
рождает) новый подпроцесс, что бы выполнить какую либо работу, называется ветвле-
нием (forking) процесса. Новый процесс называется "дочерним" (или "потомком"), а поро-
дивший его процесс - "родительским" (или "предком"). В результате и потомок и предок
продолжают исполняться одновременно -- параллельно друг другу.
В общем случае, встроенные команды Bash, при исполнении внутри сценария, не
порождают новый подпроцесс, в то время как вызов внешних команд, как правило, приво-
дит к созданию нового подпроцесса.
Внутренние команды могут иметь внешние аналоги. Например, внутренняя команда Bash
-- echo имеет внешний аналог /bin/echo и их поведение практически идентично.
echo "Эта строка выводится внутренней командой \"echo\"."
/bin/echo "А эта строка выводится внешней командой the /bin/echo."
Следует запомнить, что команда echo `command` удалит все символы пере-
вода строки, которые будут выведены командой command.
Переменная $IFS обычно содержит символ перевода строки \n, как один из вариан-
тов пробельного символа. Bash разобьет вывод команды command, по пробельным
символам, на аргументы и передаст их команде echo, которая выведет эти аргумен-
ты, разделенные пробелами.
74
bash$ ls -l /usr/share/apps/kjezz/sounds
-rw-r--r-- 1 root root 1407 Nov 7 2000 [Link]
-rw-r--r-- 1 root root 362 Nov 7 2000 [Link]
printf
printf -- команда форматированного вывода, расширенный вариант команды echo и
ограниченный вариант библиотечной функции printf() в языке C, к тому же син-
таксис их несколько отдичается друг от друга.
printf format-string... parameter...
Это встроенная команда Bash. Имеет внешний аналог /bin/printf или
/usr/bin/printf. За более подробной информацией обращайтесь к страницам
справочного руководства man 1 printf по системным командам.
Пример. printf в действии
PI=3,14159265358979
DecimalConstant=31373
Message1="Поздравляю,"
Message2="Землянин."
E_BADDIR=65
var=nonexistent_directory
error()
{
printf "$@" >&2
75
# Форматированный вывод аргументов на stderr.
echo
exit $E_BADDIR
}
read
"Читает" значение переменной с устройства стандартного ввода -- stdin, в инте-
рактивном режиме это означает клавиатуру. Ключ -a позволяет записывать значе-
ния в массивы.
Пример. Ввод значений переменных с помощью read
read var1
# Обратите внимание -- перед именем переменной отсутствует символ '$'.
76
Команда read может считывать значения для переменных из файла, перенаправ-
ленного на stdin. Если файл содержит не одну строку, то переменной будет при-
своена только первая строка. Если команде read будет передано несколько пере-
менных, то первая строка файла будет разбита, по пробелам, на несколько под-
строк, каждая из которых будет записана в свою переменную. Будьте осторожны!
Файловая система.
cd
Уже знакомая нам команда cd, изменяющая текущий каталог, может быть исполь-
зована в случаях, когда некоторую команду необходимо запустить только находясь
в определенном каталоге.
dir1=/usr/local
dir2=/var/spool
pushd $dir1
# Команда 'dirs' будет вызвана автоматически
# (на stdout будет выведено содержимое стека).
echo "Выполнен переход в каталог `pwd`." # Обратные одиночные кавычки.
77
# Теперь можно выполнить какие либо действия в каталоге 'dir2'.
echo "На вершине стека находится: $DIRSTACK."
popd
echo "Возврат в каталог `pwd`."
Переменные.
let
Команда let производит арифметические операции над переменными. В большин-
стве случаев, ее можно считать упрощенным вариантом команды expr.
Пример. Команда let, арифметические операции.
eval
eval arg1 [arg2] ... [argN]
Транслирует список аргументов, из списка, в команды.
Пример. Демонстрация команды eval
78
chmod 666 /dev/ttyS3
# Завершенный, по сигналу SIGKILL, ppp изменяет права доступа
# к последовательному порту. Вернуть их в первоначальное состояние.
# script "set-test"
# Вызовите сценарий с тремя аргументами командной строки,
# например: "./set-test one two three".
echo
echo "Аргументы перед вызовом set \`uname -a\` :"
echo "Аргумент #1 = $1"
echo "Аргумент #2 = $2"
echo "Аргумент #3 = $3"
bash$ set
AUTHORCOPY=/home/student/posts
BASH=/bin/bash
BASH_VERSION=$'2.05.8(1)-release'
...
XAUTHORITY=/home/student/.Xauthority
_=/etc/bashrc
variable22=abc
variable23=xzy
79
Если команда set используется с ключом "--", после которого следует переменная,
то значение переменной переносится в позиционные параметры (аргументы). Если
имя переменной отсутствует, то эта команда приводит к сбросу позиционных па-
раметров.
unset
Команда unset удаляет переменную, фактически - устанавливает ее значение в null.
Обратите внимание: эта команда не может сбрасывать позиционные параметры
(аргументы).
export
Команда export экспортирует переменную, делая ее доступной дочерним процес-
сам. К сожалению, невозможно экспортировать переменную родительскому про-
цессу. В качестве примера использования команды export можно привести сцена-
рии инициализации системы, вызываемые в процессе загрузки, которые инициали-
зируют и экспортируют переменные окружения, делая их доступными для пользо-
вательских процессов.
Допускается объединение инициализации и экспорта переменной в одну инструк-
цию: export var1=xxx.
Однако, как заметил Greg Keraunen, в некоторых ситуациях такая комбинация мо-
жет давать иной результат, нежели раздельная инициализация и экспорт.
80
ется на 1 скрытая переменная $OPTIND и выполняется переход к началу новой ите-
рации.
1. Опциям (ключам), передаваемым в сценарий из командной строки, должен пред-
шествовать символ "минус" (-) или "плюс" (+). Этот префикс (- или +) позволяет
getopts отличать опции (ключи) от прочих аргументов. Фактически, getopts не бу-
дет обрабатывать аргументы, если им не предшествует символ - или +, выделение
опций будет прекращено как только встретится первый аргумент.
2. Типичная конструкция цикла while с getopts несколько отличается от стандартной
из-за отсутствия квадратных скобок, проверяющих условие продолжения цикла.
3. Пример getopts, заменившей устаревшую, и не такую мощную, внешнюю команду
getopt.
Управление сценарием.
source, . (точка)
Когда эта команда вызывается из командной строки, то это приводит к запуску
указанного сценария. Внутри сценария, команда source file-name загружает файл
file-name. Таким образом она очень напоминает директиву препроцессора языка
C/C++ -- "#include". Может найти применение в ситуациях, когда несколько сцена-
риев пользуются одним файлом с данными или библиотекой функций.
exit
Безусловное завершение работы сценария. Команде exit можно передать целое
число, которое будет возвращено вызывающему процессу как код завершения. Во-
обще, считается хорошей практикой завершать работу сценария, за исключением
простейших случаев, командой exit 0, чтобы проинформировать родительский
процесс об успешном завершении.
Если сценарий завершается командой exit без аргументов, то в качестве кода за-
вершения сценария принимается код завершения последней выполненной коман-
ды, не считая самой команды exit.
exec
Это встроенная команда интерпретатора shell, заменяет текущий процесс новым
процессом, запускаемым командой exec. Обычно, когда командный интерпретатор
81
встречает эту команду, то он порождает дочерний процесс, чтобы исполнить ко-
манду. При использовании встроенной команды exec, оболочка не порождает еще
один процесс, а заменяет текущий процесс другим. Для сценария это означает его
завершение сразу после исполнения команды exec. По этой причине, если вам
встретится exec в сценарии, то, скорее всего это будет последняя команда в сцена-
рии.
Пример. Команда exec.
shopt -s cdspell
# Исправляет незначительные орфографические ошибки в именах
# каталогов в команде 'cd'
cd /hpme # Oops! Имелось ввиду '/home'.
pwd # /home
# Shell исправил опечатку.
Команды.
true
Команда возвращает код завершения - ноль, или успешное завершение, и ничего
больше.
# Бесконечный цикл
while true # вместо ":"
do
operation-1
operation-2
...
operation-n
# Следует предусмотреть способ завершения цикла.
Done
false
Возвращает код завершения, свидетельствующий о неудаче, и ничего более.
82
type [cmd]
Очень похожа на внешнюю команду which, type cmd выводит полный путь к
"cmd". В отличие от which, type является внутренней командой Bash. С опцией -a
не только различает ключевые слова и внутренние команды, но и определяет ме-
стоположение внешних команд с именами, идентичными внутренним.
disown
Удаляет задание из таблицы активных заданий командной оболочки.
fg, bg
Команда fg переводит задание из фона на передний план. Команда bg перезапуска-
ет приостановленное задание в фоновом режиме. Если эти команды были вызваны
без указания номера задания, то они воздействуют на текущее исполняющееся за-
дание.
wait
Останавливает работу сценария до тех пор пока не будут завершены все фоновые
задания или пока не будет завершено задание/процесс с указанным номером зада-
ния/PID процесса. Возвращает код завершения указанного задания/процесса.
Вы можете использовать команду wait для предотвращения преждевременного за-
вершения сценария до того, как завершит работу фоновое задание.
Пример. Ожидание завершения процесса перед тем как продолжить работу
83
if [ "$UID" -ne "$ROOT_UID" ]
then
echo "Для запуска этого сценария вы должны обладать привилегиями
root."
exit $E_NOTROOT
fi
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` имя-файла"
exit $E_NOPARAMS
fi
wait
# В этом месте сценарий приостанавливает свою работу до тех пор,
# пока не отработает 'updatedb'.
# Желательно обновить базу данных перед тем как выполнить поиск файла.
locate $1
0m0.020s 0m0.020s
Имеет весьма ограниченную сферу применения, так как сценарии крайне редко
подвергаются профилированию.
kill
Принудительное завершение процесса путем передачи ему соответствующего сиг-
нала.
Пример. Сценарий, завершающий себя сам с помощью команды kill.
84
autoload
Перенесена в Bash из ksh. Если функция объявлена как autoload, то она будет за-
гружена из внешнего файла в момент первого вызова. Такой прием помогает эко-
номить системные ресурсы.
Обратите внимание: autoload не является частью ядра Bash. Ее необходимо загру-
зить с помощью команды enable -f (см. выше).
Таблица Идентификация заданий
Нотация Описание
%N Номер задания [N]
%S Вызов (командная строка) задания, которая начинается со строки S
%?S Вызов (командная строка) задания, которая содержит строку S
%% "текущее" задание (последнее задание приостановленное на переднем плане
или запущенное в фоне)
%+ "текущее" задание (последнее задание приостановленное на переднем плане
или запущенное в фоне)
%- Последнее задание
$! Последний фоновый процесс
Базовые команды.
Первая команда, с которой сталкиваются новички.
ls
Команда вывода "списка" файлов. Многие недооценивают всю мощь этой скром-
ной команды. Например, с ключом -R, рекурсивный обход дерева каталогов, ко-
мандв ls выводит содержимое каталогов в виде древовидной структуры. Вот еще
ряд любопытных ключей (опций) команды ls: -S -- сортировка по размеру файлов,
-t -- сортировка по времени последней модификации файла и -i - выводит список
файлов с их inode.
Пример. Создание оглавления диска для записи CDR, с помощью команды ls.
85
# то необходимо установить флаг suid на cdrecord
# (chmod u+s /usr/bin/cdrecord,
# эта команда должна быть выполнена root-ом).
if [ -z "$1" ]
then
IMAGE_DIRECTORY=$DEFAULTDIR
# Каталог по-умолчанию, если иной каталог не задан из командной стро-
ки.
else
IMAGE_DIRECTORY=$1
fi
# Создать файл "table of contents".
ls -lRF $IMAGE_DIRECTORY > $IMAGE_DIRECTORY/$CONTENTSFILE
# Ключ "l" -- "расширенный" формат вывода списка файлов.
# Ключ "R" -- рекурсивный обход дерева каталогов.
# Ключ "F" -- добавляет дополнительные метки к именам файлов (к именам
каталогов добавдяет оконечный символ /).
echo "Создано оглавление."
# Создать iso-образ.
mkisofs -r -o $IMAGFILE $IMAGE_DIRECTORY
echo "Создан iso-образ файловой системы ISO9660 ($IMAGEFILE)."
# "Прожигание" CDR.
cdrecord -v -isosize speed=$SPEED dev=0,0 $IMAGEFILE
echo "Запись диска."
echo "Наберитесь терпения, это может потребовать некоторого времени."
cat, tac
cat -- это акроним от concatenate, выводит содержимое списка файлов на stdout.
Для объединения файлов в один файл может использоваться в комбинации с опе-
рациями перенаправления (> или >>).
86
Более сложные команды.
find
-exec COMMAND \;
Для каждого найденного файла, соответствующего заданному шаблону поиска,
выполняет команду COMMAND. Командная строка должна завершаться последова-
тельностью символов \; (здесь символ ";" экранирован обратным слэшем, чтобы
информировать командную оболочку о том, что символ ";" должен быть передан
команде find как обычный символ). Если COMMAND содержит {}, то find подставляет
полное имя найденного файла вместо "{}".
xargs
Команда передачи аргументов указанной команде. Она разбивает поток аргументов
на отдельные составляющие и поочередно передает их заданной команде для обра-
ботки. Эта команда может рассматриваться как мощная замена обратным одинич-
ным кавычкам. Зачастую, когда команды, заключенные в обратные одиночные ка-
вычки, завершаются с ошибкой too many arguments (слишком много аргументов),
использование xargs позволяет обойти это ограничение. Обычно, xargs считывает
список аргументов со стандартного устройства ввода stdin или из канала (конвей-
ера), но может считывать информацию и из файла.
Если команда не задана, то по-умолчанию выполняется echo. При передаче аргу-
ментов по конвейеру, xargs допускает наличие пробельных символов и символов
перевода строки, которые затем автоматически отбрасываются.
bash$ ls -l
total 0
-rw-rw-r-- 1 student student 0 Jan 29 23:58 file1
87
-rw-rw-r-- 1 student student 0 Jan 29 23:58 file2
bash$ ls -l | xargs
total 0 -rw-rw-r-- 1 student student 0 Jan 29 23:58 file1 -rw-rw-r-- 1
student student 0 Jan 29 23:58 file2
88
z=`expr substr $string $position $length`
Извлекает подстроку длиной $length символов, начиная с позиции $position.
Пример. Пример работы с expr.
# Арифметические операции
# -------------- --------
echo "Арифметические операции"
a=`expr 5 + 3`
echo "5 + 3 = $a"
a=`expr $a + 1`
echo "a + 1 = $a"
echo "(инкремент переменной)"
a=`expr 5 % 3`
# остаток от деления (деление по модулю)
echo "5 mod 3 = $a"
# Логические операции
# ---------- --------
# Возвращает 1 если выражение истинноо, 0 -- если ложно,
# в противоположность соглашениям, принятым в Bash.
x=24
y=25
b=`expr $x = $y` # Сравнение.
echo "b = $b" # 0 ( $x -ne $y )
a=3
b=`expr $a \> 10`
echo 'b=`expr $a \> 10`, поэтому...'
echo "Если a > 10, то b = 0 (ложь)"
echo "b = $b" # 0 ( 3 ! -gt 10 )
b=`expr $a \< 10`
echo "Если a < 10, то b = 1 (истина)"
echo "b = $b" # 1 ( 3 -lt 10 )
echo
# Обратите внимание на необходимость экранирования операторов.
b=`expr $a \<= 3`
echo "Если a <= 3, то b = 1 (истина)"
echo "b = $b" # 1 ( 3 -le 3 )
# Существует еще оператор "\>=" (больше или равно).
# Операции сравнения
# -------- ---------
echo "Операции сравнения"
echo
a=zipper
echo "a is $a"
if [ `expr $a = snap` ]
then
echo "a -- это не zipper"
fi
# Операции со строками
# -------- -- --------
a=1234zipper43231
echo "Строка над которой производятся операции: \"$a\"."
89
echo "Подстрока в строке \"$a\", начиная с позиции 2,\
и длиной в 6 символов: \"$b\"."
prefix=temp
suffix=`eval date +%s` # Ключ "+%s" характерен для GNU-версии 'date'.
filename=$prefix.$suffix
echo $filename
# Прекрасный способ получения "уникального"
# имени для временного файла,
# даже лучше, чем с использованием $$.
Ключ -u дает UTC время (Universal Coordinated Time -- время по Гринвичу).
touch
Утилита устанавливает время последнего обращения/изменения файла в текущее
системное время или в заданное время, но так же может использоваться для созда-
ния нового пустого файла. Команда touch zzz создаст новый пустой файл с име-
нем zzz, если перед этим файл zzz отсутствовал. Кроме того, такие пустые файлы
могут использоваться для индикации, например, времени последнего изменения в
проекте.
Эквивалентом команды touch могут служить : >> newfile или >> newfile (для
обычных файлов).
at
Команда at -- используется для запуска заданий в заданное время. В общих чертах
она напоминает crond, однако, at используется для однократного запуска набора
команд.
at 2pm January 15 -- попросит ввести набор команд, которые необходимо запус-
тить в указанное время. Эти команды должны быть совместимыми со сценариями
90
командной оболочки. Ввод завершается нажатием комбинации клавиш Ctl-D.
Ключ -f или операция перенаправления ввода (<), заставляет at прочитать список
команд из файла. Этот файл должен представлять из себя обычный сценарий, на
языке командной оболочки и, само собой разумеется, такой сценарий должен быть
неинтерактивным. Может использоваться совместно с командой run-parts для за-
пуска различных наборов сценариев.
batch
Команда batch, управляющая запуском заданий, напоминает команду at, но запус-
кает список команд только тогда, когда загруженность системы упадет ниже .8.
Подобно команде at, с ключом -f, может считывать набор команд из файла.
cal
Выводит на stdout аккуратно отформатированный календарь на текущий месяц.
Может выводить календарь за определенный год.
sleep
Приостанавливает исполнение сценария на заданное количество секунд, ничего не
делая. Может использоваться для синхронизации процессов, запущенных в фоне,
проверяя наступление ожидаемого события так часто, как это необходимо. Напри-
мер.
sleep 3
# Пауза, длительностью в 3 секунды.
Команда sleep по-умолчанию принимает количество секунд, но ей можно передать
и количество часов и минут и даже дней.
sleep 3 h
# Приостановка на 3 часа!
Для запуска команд через заданные интервалы времени лучше использовать watch
usleep
Microsleep (здесь символ "u" должен читаться как буква греческого алфавита --
"мю", или префикс микро). Это то же самое, что и sleep, только интервал времени
задается в микросекундах. Может использоваться для очень тонкой синхронизации
процессов.
usleep 30
# Приостановка на 30 микросекунд.
hwclock, clock
Команда hwclock используется для получения доступа или коррекции аппаратных
часов компьютера. С некоторыми ключами требует наличия привилегий root. Се-
нарий /etc/rc.d/[Link] использует команду hwclock для установки систем-
ного времени во время загрузки.
Команда clock -- это синоним команды hwclock.
Команды обработки текста.
sort
Сортирует содержимое файла, часто используется как промежуточный фильтр в
конвейерах. Эта команда сортирует поток текста в порядке убывания или возраста-
91
ния, в зависимости от заданных опций. Ключ -m используется для сортировки и
объединения входных файлов. В странице info перечислено большое количество
возможных вариантов ключей.
tsort
Топологическая сортировка, считывает пары строк, разделенных пробельными
символами, и выполняет сортировку, в зависимости от заданного шаблона.
uniq
Удаляет повторяющиеся строки из отсортированного файла. Эту команду часто
можно встретить в конвейере с командой sort.
expand, unexpand
Команда expand преобразует символы табуляции в пробелы. Часто используется в
конвейерной обработке текста.
Команда unexpand преобразует пробелы в символы табуляции. Т.е. она является
обратной по отношению к команде expand.
cut
Команда предназначена для извлечения отдельных полей из текстовых файлов. На-
поминает команду print $N в awk, но более ограничена в своих возможностях. В
простейших случаях может быть неплохой заменой awk в сценариях. Особую зна-
чимость, для команды cut, представляют ключи -d (разделитель полей) и -f (но-
мер(а) поля(ей)).
Использование команды cut для получения списка смонтированных файловых сис-
тем:
cat /etc/mtab | cut -d ' ' -f1,2
FILENAME=/etc/passwd
cut -d ' ' -f2,3 filename эквивалентно awk -F'[ ]' '{ print $2, $3 }'
filename
paste
92
Используется для объединения нескольких файлов в один многоколоночный файл.
join
Может рассматриваться как команда, родственная команде paste. Эта мощная ути-
лита позволяет объединять два файла по общему полю, что представляет собой уп-
рощенную версию реляционной базы данных.
Команда join оперирует только двумя файлами и объедияет только те строки, кото-
рые имеют общее поле (обычно числовое), результат объединения выводится на
stdout. Объединяемые файлы должны быть отсортированы по ключевому полю.
File: [Link]
100 Shoes
200 Laces
300 Socks
File: [Link]
100 $40.00
200 $1.00
300 $2.00
head
Выводит начальные строки из файла на stdout (по-умолчанию -- 10 строк, но это
число можно задать иным). Эта команда имеет ряд интересных ключей.
Пример. Какие из файлов являются сценариями?
93
Поиск участков текста в файле(ах), соответствующих шаблону pattern, где
pattern может быть как обычной строкой, так и регулярным выражением.
Если файл(ы) для поиска не задан, то команда grep работает как фильтр для уст-
ройства stdout, например в конвейере.
egrep -- то же самое, что и grep -E. Эта команда использует несколько отличаю-
щийся, расширенный набор регулярных выражений, что позволяет выполнять по-
иск более гибко.
fgrep -- то же самое, что и grep -F. Эта команда выполняет поиск строк символов
(не регулярных выражений), что несколько увеличивает скорость поиска.
Утилита agrep имеет более широкие возможности поиска приблизительных совпа-
дений. Образец поиска может отличаться от найденной строки на указанное число
символов.
Для поиска по сжатым файлам следует использовать утилиты zgrep, zegrep
или zfgrep. Они с успехом могут использоваться и для не сжатых файлов, но в этом
случае они уступают в скорости обычным grep, egrep и fgrep. Они очень удобны
при выполнении поиска по смешенному набору файлов - когда одни файлы сжаты,
а другие нет.
Для поиска по bzip-файлам используйте bzgrep.
sed, awk
Скриптовые языки, специально разработанные для анализа текстовых данных.
sed
Неинтерактивный "потоковый редактор". Широко используется в сценариях на
языке командной оболочки.
awk
Утилита контекстного поиска и преобразования текста, замечательный инструмент
для извлечения и/или обработки полей (колонок) в структурированных текстовых
файлах. Синтаксис awk напоминает язык C.
wc
wc -- "word count", счетчик слов в файле или в потоке:
94
bash $ wc /usr/doc/sed-3.02/README
20 127 838 /usr/doc/sed-3.02/README
[20 строк 127 слов 838 символов]
wc -w подсчитывает только слова.
wc -l подсчитывает только строки.
wc -c подсчитывает только символы.
wc -L возвращает длину наибольшей строки.
Подсчет количества .txt-файлов в текущем каталоге с помощью wc:
$ ls *.txt | wc -l
# Эта команда будет работать, если ни в одном из имен файлов "*.txt"
нет символа перевода строки.
# Альтернативный вариант:
find . -maxdepth 1 -name \*.txt -print0 | grep -cz .
# (shopt -s nullglob; set -- *.txt; echo $#)
Подсчет общего размера файлов, чьи имена начинаются с символов, в диапазоне d -
h
bash$ wc [d-h]* | grep total | awk '{print $3}'
71832
Использование wc для подсчета количества вхождений слова "Linux" в основной
исходный файл с текстом этого руководства.
bash$ grep Linux [Link] | wc -l
50
Отдельные команды располагают функциональностью wc в виде своих ключей.
tr
Замена одних символов на другие.
В отдельных случаях символы необходимо заключать в кавычки и/или квадратные
скобки. Кавычки предотвращают интерпретацию специальных символов команд-
ной оболочкой. Квадратные скобки должны заключаться в кавычки.
Команда tr "A-Z" "*" <filename или tr A-Z \* <filename заменяет все симво-
лы верхнего регистра в filename на звездочки (вывод производится на stdout). В
некоторых системах этот вариант может оказаться неработоспособным, тогда по-
пробуйте tr A-Z '[**]'.
Ключ -d удаляет символы из заданного диапазона.
95
+c+d+b++++
Обратите внимание: команда tr корректно распознает символьные классы POSIX.
bash$ echo "abcd2ef1" | tr '[:alpha:]' -
----2--1
Пример. Преобразование символов в верхний регистр.
E_BADARGS=65
#!/bin/bash
# [Link]: Преобразование текстового файла из формата DOS в формат UNIX.
E_WRONGARGS=65
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` filename-to-convert"
exit $E_WRONGARGS
fi
NEWFILENAME=$[Link]
fold
Выравнивает текст по ширине, разрывая, если это необходимо, слова. Особый ин-
терес представляет ключ -s, который производит перенос строк по пробелам, ста-
раясь не разрывать слова.
fmt
Очень простая утилита форматирования текста, чаще всего используемая как
фильтр в конвейерах для того, чтобы выполнить "перенос" длинных строк текста.
Пример. Отформатированный список файлов.
#!/bin/bash
WIDTH=40 # 40 символов в строке.
b=`ls /usr/local/bin` # Получить список файлов...
echo $b | fmt -w $WIDTH
# То же самое можно выполнить командой
# echo $b | fold - -s -w $WIDTH
column
Форматирование по столбцам. Эта утилита преобразует текст, например какой ли-
96
бо список, в табличное, более "удобочитаемое", представление, вставляя символы
табуляции по мере необходимости.
Пример. Пример форматирования списка файлов в каталоге.
pr
Подготовка файла к печати. Утилита производит разбивку файла на страницы,
приводя его в вид пригодный для печати или для вывода на экран. Разнообразные
ключи позволяют выполнять различные манипуляции над строками и колонками,
соединять строки, устанавливать поля, нумеровать строки, добавлять колонтитулы
и многое, многое другое. Утилита pr соединяет в себе функциональность таких ко-
манд, как nl, paste, fold, column и expand.
pr -o 5 --width=65 fileZZZ | more -- выдаст хорошо оформленное и разбитое
на страницы содержимое файла fileZZZ.
Хочу особо отметить ключ -d, который выводит строки с двойным интервалом (тот
же эффект, что и sed -G).
iconv
Утилита преобразования текста из одной кодировки в другую. В основном исполь-
зуется для нужд локализации.
97
recode
Может рассматриваться как разновилность утилиты iconv, описанной выше. Уни-
версальная утилита для преобразования текстовой информации в различные коди-
ровки.
groff, tbl, eqn
groff -- это еще один язык разметки текста и форматированного вывода. Является
расширенной GNU-версией пакета roff/troff в UNIX-системах.
tbl -- утилита обработки таблиц, должна рассматриваться как составная часть groff,
так как ее задачей является преобразование таблиц в команды groff.
eqn -- утилита преобразования математических выражений в команды groff.
lex, yacc
lex -- утилита лексического разбора текста. В Linux-системах заменена на свободно
распространяемую утилиту flex.
yacc -- утилита для создания синтаксических анализаторов, на основе набора грам-
матик, задаваемых разработчиком. В Linux-системах, эта утилита заменена на сво-
бодно распространяемую утилиту bison.
98
Стандартная GNU/UNIX утилита сжатия, заменившая более слабую, и к тому же
проприетарную, утилиту compress. Соответствующая утилита декомпрессии (раз-
жатия) -- gunzip, которая является эквивалентом команды gzip -d.
Для работы со сжатыми файлами в конвейере используется фильтр zcat, который
выводит результат своей работы на stdout, допускает перенаправление вывода.
Фактически это та же команда cat, только приспособленная для работы со сжатыми
файлами (включая файлы, сжатые утилитой compress). Эквивалент команды zcat --
gzip -dc.
В некоторых коммерческих версиях UNIX, команда zcat является синонимом ко-
манды uncompress -c, и не может работать с файлами, сжатыми с помощью gzip.
bzip2
Альтернативная утилита сжатия, обычно дает более высокую степень сжатия (но
при этом работает медленнее), чем gzip, особенно это проявляется на больших
файлах. Соответствующая утилита декомпрессии -- bunzip2.
В современные версии tar добавлена поддержка bzip2.
zip, unzip
Кроссплатформенная утилита архивирования и сжатия, совместимая, по формату
архивного файла, с утилитой DOS -- [Link].
unarc, unarj, unrar
Этот набор утилит предназначен для распаковки архивов, созданных с помощью
DOS архиваторов -- [Link], [Link] и [Link].
Получение сведений о файлах.
file
Утилита идентификации файлов. Команда file file-name верне тип файла file-
name, например, ascii text или data. Для этого она анализирует сигнатуру, или
магическое число и сопоставляет ее со списком известных сигнатур из
/usr/share/magic, /etc/magic или /usr/lib/magic
-f -- ключ пакетного режима работы утилиты file, в этом случае утилита принима-
ет список анализируемых имен файлов из заданного файла. Ключ -z используется
для анализа файлов в архиве.
which
Команда which command-xxx вернет полный путь к "command-xxx". Очень полезна
для того, чтобы узнать -- установлена ли та или иная утилита в системе.
$bash which rm
/usr/bin/rm
whereis
Очень похожа на which, упоминавшуюся выше. Команда whereis command-xxx
99
вернет полный путь к "command-xxx", но кроме того, еще и путь к manpage -- фай-
лу, странице справочника по заданной утилите.
$bash whereis rm
rm: /bin/rm /usr/share/man/man1/rm.1.bz2
whatis
Утилита whatis filexxx отыщет "filexxx" в своей базе данных. Может рассматри-
ваться как упрощенный вариант команды man.
$bash whatis whatis
whatis (1) - search the whatis database for complete
words
Пример. Исследование каталога /usr/X11R6/bin
# Что находится в каталоге /usr/X11R6/bin?
DIRECTORY="/usr/X11R6/bin"
# Попробуйте также "/bin", "/usr/bin", "/usr/local/bin", и т.д.
locate, slocate
Команда locate определяет местонахождение файла, используя свою базу данных,
создаваемую специально для этих целей. Команда slocate -- это защищенная версия
locate (которая может оказаться простым псевдонимом команды slocate).
$bash locate hickson
/usr/lib/xephem/catalogs/[Link]
readlink
Возвращает имя файла, на который указывает символическая ссылка.
bash$ readlink /usr/bin/awk
../../bin/gawk
strings
Команда strings используется для поиска печатаемых строк в двоичных файлах.
Она выводит последовательности печатаемых символов, обнаруженных в заданном
файле. Может использоваться для прикидочного анализа дамп-файлов (core dump)
или для отыскания информации о типе файла, например для графических файлов
неизвестного формата (например, strings image-file | more может вывести та-
кую строчку: JFIF, что говорит о том, что мы имеем дело с графическим файлом в
формате jpeg). В сценариях, вероятнее всего, вам придется использовать эту ко-
манду в связке с grep или sed.
Сравнение.
diff, patch
diff: очень гибкая утилита сравнения файлов. Она выполняет построчное сравнение
файлов. В отдельных случаях, таких как поиск по словарю, может оказаться полез-
ной фильтрация файлов с помощью sort и uniq перед тем как отдать поток данных
через конвейер утилите diff. diff file-1 file-2 -- выведет строки, имеющие от-
100
личия, указывая -- какому файлу, какая строка принадлежит.
С ключом --side-by-side, команда diff выведет сравниваемые файлы в две ко-
лонки, с указанием несовпадающих строк. Ключи -c и -u так же служат для облег-
чения интерпретации результатов работы diff.
Существует ряд интерфейсных оболочек для утилиты diff, среди них можно на-
звать: spiff, wdiff, xdiff и mgdiff.
Команда diff возвращает код завершения 0, если сравниваемые файлы идентичны и
1, если они отличаются. Это позволяет использовать diff в условных операторах
внутри сценариев на языке командной оболочки (см. ниже).
В общем случае, diff используется для генерации файла различий, который исполь-
зуется как аргумент команды patch. Ключ -e отвечает за вывод файла различий в
формате, пригодном для использования с ed или ex.
patch: гибкая утилита для "наложения заплат". С помощью файла различий, сгене-
рированного утилитой diff, утилита patch может использоваться для обновления
устаревших версий файлов. Это позволяет распространять относительно неболь-
шие "diff"-файлы вместо целых пакетов. Распространение "заплат" к ядру стало
наиболее предпочтительным методом распространения более новых версий ядра
Linux.
101
dirname
Отсекает basename от полного имени файла и выводит только путь к файлу.
Утилитам basename и dirname может быть передана любая строка, в качестве ар-
гумента. Этот аргумент необязательно должен быть именем существующего файла.
split
Утилита разбивает файл на несколько частей. Обычно используется для разбиения
больших файлов, чтобы их можно было записать на дискеты или передать по элек-
тронной почте по частям.
sum, cksum, md5sum
Эти утилиты предназначены для вычисления контрольных сумм. Контрольная
сумма -- это некоторое число, вычисляемое исходя из содержимого файла, и слу-
жит для контроля целостности информации в файле. Сценарий может выполнять
проверку контрольных сумм для того, чтобы убедиться, что файл не был изменен
или поврежден. Для большей безопасности, рекомендуется использовать 128-
битную сумму, генерируемую утилитой md5sum (message digest checksum).
shred
Надежное, с точки зрения безопасности, стирание файла, посредством предварительной,
многократной записи в файл случайной информации, перед тем как удалить его.
Кодирование и шифрование.
uuencode
Эта утилита используется для кодирования двоичных файлов в символы ASCII, по-
сле такого кодирования файлы могут, с достаточной степенью безопасности, пере-
даваться по сети, вкладываться в электронные письма и т.п..
uudecode
Утилита декодирования файлов, прошедших обработку утилитой uuencode.
mimencode, mmencode
Утилиты mimencode и mmencode предназначены для обработки закодированных
мультимедийных вложений в электронные письма. Хотя почтовые программы (та-
кие как pine или kmail) имеют возможность автоматической обработки таких вло-
жений, тем не менее эти утилиты позволяют обрабатывать вложения вручную, из
командной строки или в пакетном режиме, из сценария на языке командной обо-
лочки.
mktemp
Создает временный файл с "уникальным" именем.
PREFIX=filename
tempfile=`mktemp $[Link]`
# ^^^^^^ Необходимо по меньшей мере 6 заполните-
лей
echo "имя временного файла = $tempfile"
# имя временного файла = filename.QA2ZpY
# или нечто подобное...
make
Утилита для компиляции и сборки программ. Но может использоваться для выпол-
нения любых других операций, основанных на анализе наличия изменений в ис-
ходных файлах.
102
Команда make использует в своей работе Makefile, который содержит перечень
зависимостей и операций, которые необходимо выполнить для удовлетворения
этих зависимостей.
dos2unix
Предназначена для преобразования текстовых файлов из формата DOS (в котором
строки завершаются комбинацией символов CR-LF) в формат UNIX (в котором
строки завершаются одним символом LF) и обратно.
more, less
Команды постраничного просмотра текстовых файлов или потоков на stdout. Мо-
гут использоваться в сценариях в качестве фильтров.
ipcalc
Производит поиск IP адреса. С ключом -h, ipcalc выполняет поиск имени хоста в
DNS, по заданному IP адресу.
nslookup
Выполняет "поиск имени узла" Интернета по заданному IP адресу. По сути, экви-
валентна командам ipcalc -h и dig -x. Команда может исполняться как в интерак-
тивном, так и в неинтерактивном режиме, т.е. в пределах сценария.
bash$ nslookup -sil [Link]
nslookup [Link]
Server: [Link]
Address: [Link]#53
Non-authoritative answer:
Name: [Link]
traceroute
Утилита предназначена для исследования топологии сети посредством передачи
ICMP пакетов удаленному узлу. Эта программа может работать в LAN, WAN и в
Интернет. Удаленный узел может быть указан как по имени, так и по IP адресу.
Вывод команды traceroute может быть передан по конвейеру утилитам grep или sed,
для дальнейшего анализа.
103
bash$ traceroute [Link]
traceroute to [Link] ([Link]), 30 hops max, 38 byte packets
1 [Link] ([Link]) 191.303 ms 179.400 ms 179.767 ms
2 [Link] ([Link]) 179.536 ms 179.534 ms 169.685 ms
3 [Link] ([Link]) 189.471 ms 189.556 ms *
...
ping
Выполняет передачу пакета "ICMP ECHO_REQUEST" другой системе в сети. Чаще
всего служит в качестве инструмента диагностики соединений, должна использо-
ваться с большой осторожностью.
В случае успеха, ping возвращает код завершения 0, поэтому команда ping может
использоваться в условных операторах.
whois
Выполняет поиск в DNS (Domain Name System). Ключом -h можно указать какой
из whois серверов будет запрошен.
finger
Возвращает информацию о пользователях в сети. По желанию, эта команда может
выводить содержимое файлов ~/.plan, ~/.project и ~/.forward, указанного
пользователя.
bash$ finger
Login Name Tty Idle Login Time Office Office
Phone
student Student Bozeman tty1 8 Jun 25 16:59
student Student Bozeman ttyp0 Jun 25 16:59
student Student Bozeman ttyp1 Jun 25 17:07
wget -p [Link]
wget -r [Link] -o $SAVEFILE
rlogin
Remote login -- инициирует сессию с удаленной системой. Эта команда небезо-
пасна, вместо нее лучше использовать ssh.
rsh
Remote shell -- исполняет команду на удаленной системе. Эта команда небезопас-
на, вместо нее лучше использовать ssh.
rcp
Remote copy -- копирование файлов между двумя машинами через сеть. Подобно
прочим r* утилитам, команда rcp небезопасна и потому, использовать ее в сцена-
риях нежелательно. В качестве замены можно порекомендовать ssh или expect.
ssh
Secure shell -- устанавливает сеанс связи и выполняет команды на удаленной
системе. Выступает в качестве защищенной замены для telnet, rlogin, rcp и rsh.
Использует идентификацию, аутентификацию и шифрование информации, переда-
ваемой через сеть. Подробности вы найдете в man ssh.
105
вивалентно команде clear. tput reset -- эквивалентно команде reset. tput sgr0 -- так
же сбрасывет настройки терминал, но без очистки экрана.
bash$ tput longname
xterm terminal emulator (XFree86 4.0 Window System)
Команда tput cup X Y перемещает курсор в координаты (X,Y). Обычно этой ко-
манде предшествует clear, очищающая экран.
Обратите внимание: stty предлагает более широкий диапазон возможностей.
reset
Сбрасывает настройки терминала и очищает экран. Как и в случае команды clear,
курсор и приглашение к вводу (prompt) выводятся в верхнем левом углу терминала.
clear
Команда clear просто очищает экран терминала или окно xterm. Курсор и пригла-
шение к вводу (prompt) выводятся в верхнем левом углу терминала. Эта команда
может запускаться как из командной строки, так и из сценария.
script
Эта утилита позволяет сохранять в файле все символы, введенные пользователем c
клавиатуры (вывод тоже). Получая, фактически, подробнейший синхронный про-
токол сессии.
106
LIMIT_STRING
`
...или...
variable=$(bc << LIMIT_STRING
options
statements
operations
LIMIT_STRING
)
#!/bin/bash
# Комбинирование 'bc' с
# 'вложенным документом'.
var1=`bc << EOF
18.33 * 19.78
EOF
`
echo $var1 # 362.56
awk
107
Еще один способ выполнения математических операций, над числами с плавающей
запятой, состоит в создании сценария-обертки, использующего математические
функции awk.
Пример. Расчет гипотенузы прямоугольного треугольника.
#!/bin/bash
# [Link]: Возвращает "гипотенузу" прямоугольного треугольника.
# ( корень квадратный от суммы квадратов катетов)
ARGS=2 # В сценарий необходимо передать два катета.
E_BADARGS=65 # Ошибка в аргументах.
if [ $# -ne "$ARGS" ] # Проверка количества аргументов.
then
echo "Порядок использования: `basename $0` катет_1 катет_2"
exit $E_BADARGS
fi
AWKSCRIPT=' { printf( "%3.7f\n", sqrt($1*$1 + $2*$2) ) } '
# команды и параметры, передаваемые в awk
echo -n "Гипотенуза прямоугольного треугольника, с катетами $1 и $2, =
"
echo $1 $2 | awk "$AWKSCRIPT"
Прочие команды.
dd
Эта немного непонятная и "страшная" команда ("data duplicator") изначально ис-
пользовалась для переноса данных на магнитной ленте между микрокомпьютерами
с ОС UNIX и майнфреймами IBM. Команда dd просто создает копию файла (или
stdin/stdout), выполняя по пути некоторые преобразования. Один из вариантов:
преобразование из ASCII в EBCDIC, dd --help выведет список возможных вари-
антов преобразований и опций этой мощной утилиты.
# Изучаем 'dd'.
n=3
p=5
input_file=[Link]
output_file=[Link]
dd if=$input_file of=$output_file bs=1 skip=$((n-1)) count=$((p-n+1))
2> /dev/null
# Извлечет из $input_file символы с n-го по p-й.
108
десятичном виде или в виде ASCII. Эту команду, с массой оговорок, можно назвать
эквивалентом команды of od.
objdump
Отображает содержимое исполняемого или объектного файла либо в шестнадцати-
ричной форме, либо в виде дизассемблерного листинга (с ключом -d).
080490bc <.init>:
80490bc: 55 push %ebp
80490bd: 89 e5 mov %esp,%ebp
. . .
mcookie
Эта команда создает псевдослучайные шестнадцатиричные 128-битные числа, так
называемые "magic cookie", обычно используется X-сервером в качестве "сигнату-
ры" авторизации. В сценариях может использоваться как малоэффективный гене-
ратор случайных чисел.
109
501
chown, chgrp
Команда chown изменяет владельца файла или файлов. Эта команда полезна в слу-
чаях, когда root хочет передать монопольное право на файл от одного пользовате-
ля другому. Обычный пользователь не в состоянии изменить владельца файла, за
исключением своих собственных файлов.
root# chown student *.txt
Команда chgrp изменяет группу, которой принадлежит файл или файлы. Чтобы
изменить группу, вы должны быть владельцем файла (при этом должны входить в
состав указываемой группы) или привилегированным пользователем (root).
useradd, userdel
Команда useradd добавляет учетную запись нового пользователя в систему и соз-
дает домашний каталог для данного пользователя. Противоположная, по смыслу,
команда userdel удаляет учетную запись пользователя из системы, и удалит соот-
ветствующие файлы.
Команда adduser является синонимом для useradd и, как правило, является обыч-
ной символической ссылкой на useradd.
id
Команда id выводит идентификатор пользователя (реальный и эффективный) и
идентификаторы групп, в состав которых входит пользователь. По сути -- выводит
содержимое переменных $UID, $EUID и $GROUPS.
bash$ id
uid=501(student) gid=501(student)
groups=501(student),22(cdrom),80(cdwriter),81(audio)
bash$ echo $UID
501
who
Выводит список пользователей, работающих в настоящий момент в системе.
bash$ who
student tty1 Apr 27 17:45
student pts/0 Apr 27 17:46
student pts/1 Apr 27 17:47
student pts/2 Apr 27 17:49
С ключом -m -- выводит информацию только о текущем пользователе. Если число
аргументов, передаваемых команде, равно двум, то это эквивалентно вызову who -
m, например who am i или who The Man.
bash$ who -m
[Link]!student pts/2 Apr 27 17:49
whoami -- похожа на who -m, но выводит только имя пользователя.
bash$ whoami
student
su
Команда предназначена для запуска программы или сценария от имени другого
110
пользователя. su rjones -- запускает командную оболочку от имени пользователя
rjones. Запуск команды su без параметров означает запуск командной оболочки от
имени привилегированного пользователя root.
sudo
Исполняет заданную команду от имени пользователя root (или другого пользовате-
ля).
#!/bin/bash
# Доступ к "секретным" файлам.
sudo cp /root/secretfile /home/student/secret
Имена пользователей, которым разрешено использовать команду sudo, хранятся в
файле /etc/sudoers.
Терминалы.
tty
Выводит имя терминала текущего пользователя. Обратите внимание: каждое от-
дельное окно xterm считается отдельным терминалом.
bash$ tty
/dev/pts/1
stty
Выводит и/или изменяет настройки терминала. Эта сложная команда используется
в сценариях для управления поведением терминала.
getty, agetty
Программа getty или agetty запускается процессом init и обслуживает процедуру
входа пользователя в систему. Эти команды не используются в сценариях.
dmesg
Выводит все сообщения, выдаваемые системой во время загрузки на stdout. Очень
полезная утилита для отладочных целей. Вывод dmesg может анализироваться с
помощью grep, sed или awk внутри сценария.
bash$ uname -a
Linux [Link] 2.2.15-2.5.0 #1 Sat Feb 5 00:13:43 EST 2000
i686 unknown
bash$ uname -s
Linux
111
arch
Выводит тип аппаратной платформы компьютерв. Эквивалентна команде uname -
m.
bash$ arch
i686
bash$ uname -m
i686
du
Выводит сведения о занимаемом дисковом пространстве в каталоге и вложенных
подкаталогах. Если каталог не указан, то по-умолчанию выводятся сведения о те-
кущем каталоге.
bash$ du -ach
1.0k ./[Link]
1.0k ./[Link]
1.0k ./[Link]
6.0k .
6.0k total
df
Выводит в табличной форме сведения о смонтированных файловых системах.
bash$ df
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/hda5 273262 92607 166547 36% /
/dev/hda8 222525 123951 87085 59% /home
/dev/hda7 1408796 1075744 261488 80% /usr
stat
Дает подробную информацию о заданном файле (каталоге или файле устройства)
или наборе файлов.
netstat
Показывает сведения о сетевой подсистеме, такие как: таблицы маршрутизации и
активные соединения. Эта утилита получает сведения из /proc/net.
netstat -r -- эквивалентна команде route.
hostname
Выводит имя узла (сетевое имя системы). С помощью этой команды устанавлива-
ется сетевое имя системы в сценарии /etc/rc.d/[Link]. Эквивалентна коман-
де uname -n и внутренней переменной $HOSTNAME.
bash$ hostname
[Link]
112
bash$ echo $HOSTNAME
[Link]
Системный журнал.
logger
Добавляет в системный журнал (/var/log/messages) сообщение от пользователя.
Для добавления сообщения пользователь не должен обладать привилегиями супер-
пользователя.
logger Experiencing instability in network connection at 23:10, 05/21.
# Теперь попробуйте дать команду 'tail /var/log/messages'.
Встраивая вызов logger в сценарии, вы получаете возможность заносить отладоч-
ную информацию в системный журнал /var/log/messages.
# tail /var/log/message
# ...
# Jul 7 20:48:58 localhost ./[Link][1712]: Logging at line 3.
logrotate
Эта утилита производит манипуляции над системным журналом: ротация, сжатие,
удаление и/или отправляет его по электронной почте, по мере необходимости. Как
правило, утилита logrotate вызывается демоном crond ежедневно.
Добавляя соответствующие строки в /etc/[Link], можно заставить
logrotate обрабатывать не только системный журнал, но и ваш личный.
Управление заданиями.
ps
Process Statistics: Список исполняющихся в данный момент процессов. Обычно вы-
зывается с ключами ax, вывод команды может быть обработан командами grep или
sed, с целью поиска требуемого процесса.
#!/bin/bash
# [Link]
NOPROCESS=2
process=xxxyyyzzz # Несуществующий процесс.
# Только в демонстрационных целях...
# ... чтобы не уничтожить этим сценарием какой-нибудь процесс.
#
# Если с помощью этого сценария вы задумаете разрыватть связь с
Internet, то
# process=pppd
exit 0
Команды управления процессами и загрузкой.
init
init -- предок (родитель) всех процессов в системе. Вызывается на последнем этапе
загрузки системы и определяет уровень загрузки (runlevel) из файла /etc/inittab.
telinit
Символическая ссылка на init - инструмент для смены уровня загрузки (runlevel),
114
как правило используется при обслуживании системы или восстановлении файло-
вой системы. Может быть вызвана только суперпользователем. Эта команда может
быть очень опасна, при неумелом обращении - прежде чем использовать ее, убеди-
тесь в том, что вы совершенно точно понимаете что делаете!
runlevel
Выводит предыдущий и текущий уровни загрузки (runlevel). Уровень загрузки мо-
жет иметь одно из 6 значений: 0 -- остановка системы, 1 -- однопользовательский
режим, 2 или 3 -- многопользовательский режим, 5 -- многопользовательский ре-
жим и запуск X Window, 6 -- перезагрузка. Уровни загрузки определяются из файла
/var/run/utmp.
halt, shutdown, reboot
Набор команд для остановки системы, обычно перед выключением питания.
Команды для работы с сетью.
ifconfig
Утилита конфигурирования и запуска сетевых интерфейсов. Чаще всего использу-
ется в сценариях начальной загрузки системы, для настройки и запуска сетевых ин-
терфейсов или для их остановки перед остановкой или перезагрузкой.
route
Выводит сведения о таблице маршрутизации ядра или вносит туда изменения.
tcpdump
"Сниффер" ("sniffer") сетевых пакетов. Инструмент для перехвата и анализа сетево-
го трафика по определенным критериям.
Дамп трафика ip-пакетов между двумя узлами сети -- studentville и caduceus:
bash$ tcpdump ip host studentville and caduceus
Конечно же, вывод команды tcpdump может быть проанализирован с помощью
команд обработки текста, обсуждавшихся выше.
Команды для работы с файловыми системами.
mount
Выполняет монтирование файловой системы, обычно на устройстве со сменными
носителями, такими как дискеты или CDROM. Файл /etc/fstab содержит пере-
чень доступных для монтирования файловых систем, разделов и устройств, вклю-
чая опции монтирования, благодаря этому файлу, монтирование может произво-
диться автоматически или вручеую. Файл /etc/mtab содержит список смонтиро-
ванных файловых систем и разделов (включая виртуальные, такие как /proc).
mount -a -- монтирует все (all) файловые системы и разделы, перечисленные в
/etc/fstab, за исключением тех, которые имеют флаг noauto. Эту команду можно
встретить в сценариях начальной загрузки системы из /etc/rc.d ([Link] или
нечто похожее).
mount -t iso9660 /dev/cdrom /mnt/cdrom
# Монтирование CDROM-а
mount /mnt/cdrom
# Более короткий и удобный вариант, если точка монтирования /mnt/cdrom
описана в /etc/fstab
Эта команда может даже смонтировать обычный файл как блочное устройство.
Достигается это за счет связывания файла с loopback-устройством. Эту возмож-
ность можно использовать для проверки ISO9660 образа компакт-диска перед его
115
записью на болванку.
Пример. Проверка образа CD.
# С правами root...
mkdir /mnt/cdtest # Подготовка точки монтирования.
mount -r -t iso9660 -o loop [Link] /mnt/cdtest # Монтирование
образа диска.
# ключ "-o loop" эквивалентен "losetup /dev/loop0"
cd /mnt/cdtest # Теперь проверим образ диска.
ls -alR # Вывод списка файлов
umount
Отмонтирует смонтированную файловую систему. Перед тем как физически вы-
нуть компакт-диск или дискету из устройства, это устройство должно быть отмон-
тировано командой umount, иначе файловая система может оказаться поврежден-
ной (особенно это относится к накопителям на гибких магнитных дисках, прим.
перев.).
umount /mnt/cdrom
# Теперь вы можете извлечь диск из привода.
sync
Принудительный сброс содержимого буферов на жесткий диск (синхронизация со-
держимого буферов ввода-вывода и устройства-носителя). Несмотря на то, что нет
такой уж острой необходимости в этой утилите, тем не менее sync придает уверен-
ности системным администраторам или пользователям в том, что их данные будут
сохранены на жестком диске, и не будут потеряны в случае какого-либо сбоя. В
былые дни, команда sync; sync (дважды - для абсолютной уверенности) была уп-
реждающей мерой перед перезагрузкой системы.
Иногда возникает необходимость принудительной синхронизации буферов ввода-
вывода с содержимым на магнитном носителе, как, например, при надежном уда-
лении файла или когда наблюдаются скачки напряжения в сети электроснабжения.
chroot
CHange ROOT -- смена корневого каталога. Обычно, команды и утилиты ориенти-
руются в файловой системе посредством переменной $PATH, относительно корне-
вого каталога /. Команда chroot изменяет корневой каталог по-умолчанию на дру-
гой (рабочий каталог также изменяется). Эта утилита, как правило, используется с
целью защиты системы, например, с ее помощью можно ограничить доступ к раз-
делам файловой системы для пользователей, подключающихся к системе с помо-
щью telnet (это называется - "поместить пользователя в chroot окружение"). Обра-
тите внимание: после выполнения команды chroot изменяется путь к исполняемым
файлам системы.
Команда chroot /opt приведет к тому, что все обращения к каталогу /usr/bin бу-
дут переводиться на каталог /opt/usr/bin. Аналогично, chroot /aaa/bbb
/bin/ls будет пытаться вызвать команду ls из каталога /aaa/bbb/bin, при этом,
корневым каталогом для ls станет каталог /aaa/bbb. Поместив строчку alias XX
'chroot /aaa/bbb ls' в пользовательский ~/.bashrc, можно эффективно ограничить
доступ команде "XX", запускаемой пользователем, к разделам файловой системы.
Команды резервного копирования.
dump, restore
Команда dump создает резервные копии целых файловых систем, обычно исполь-
зуется в крупных системах и сетях. Она считывает дисковые разделы и сохраняет
их в файле, в двоичном формате. Созданные таким образом файлы, могут быть со-
116
хранены на каком-либо носителе - на жестком диске или магнитной ленте. Команда
restore -- "разворачивает" файлы, созданные утилитой dump.
fdformat
Выполняет низкоуровневое форматирование дискет.
Команды для работы с модулями ядра.
lsmod
Выводит список загруженных модулей.
bash$ lsmod
Module Size Used by
autofs 9456 2 (autoclean)
opl3 11376 0
serial_cs 5456 0 (unused)
sb 34752 0
uart401 6384 0 [sb]
sound 58368 0 [opl3 sb uart401]
soundlow 464 0 [sound]
soundcore 2800 6 [sb sound]
ds 6448 2 [serial_cs]
i82365 22928 2
pcmcia_core 45984 0 [serial_cs ds i82365]
insmod
Принудительная загрузка модуля ядра (старайтесь вместо insmod использовать ко-
манду modprobe). Должна вызываться с привилегиями пользователя root.
rmmod
Выгружает модуль ядра. Должна вызываться с привилегиями пользователя root.
modprobe
Загрузчик модулей, который обычно вызывается из сценариев начальной загрузки
системы. Должна вызываться с привилегиями пользователя root.
depmod
Создает файл зависимостей между модулями, обычно вызывается из сценариев на-
чальной загрузки системы.
Прочие команды.
env
Запускает указанную программу или сценарий с модифицированными переменны-
ми окружения (не изменяя среду системы в целом, изменения касаются только ок-
ружения запускаемой программы/сценария). Посредством [varname=xxx], уста-
навливает значение переменной окружения varname, которая будет доступна из за-
пускаемой программы/сценария. Без параметров -- просто выводит список пере-
менных окружения с их значениями.
watch
Периодически запускает указанную программу с заданным интервалом времени.
По-умолчанию интервал между запусками принимается равным 2 секундам, но
может быть изменен ключом -n.
117
Регулярные выражения.
Для того, чтобы полностью реализовать потенциал командной оболочки, вам придется ов-
ладеть Регулярными Выражениями. Многие команды и утилиты, обычно используемые в
сценариях, такие как grep, expr, sed и awk, используют Регулярные Выражения.
118
YES, yEs и так далее.
· Обратный слэш -- \ -- служит для экранирования специальных символов, это озна-
чает, что экранированные символы должны интерпретироваться буквально, т.е. как
простые символы.
Комбинация "\$" указывает на то, что символ "$" трактуется как обычный символ,
а не как признак конца строки в регулярных выражениях. Аналогично, комбинация
"\\" соответствует простому символу "\".
· Экранированные "угловые скобки" -- \<...\> -- отмечают границы слова.
Угловые скобки должны экранироваться, иначе они будут интерпретироваться как
простые символы.
Выражение "\<the\>" соответствует слову "the", и не соответствует словам "them",
"there", "other" и т.п.
119
цифр (символов из диапазона от 0 до 9, включительно).
§ Круглые скобки -- ( ) -- предназначены для выделения групп регулярных выраже-
ний. Они полезны при использовании с оператором "|" и при извлечении подстроки
с помощью команды expr.
§ Вертикальная черта -- | -- выполняет роль логического оператора "ИЛИ" в регуляр-
ных выражениях и служит для задания набора альтернатив.
bash$ ls -l ?[[:digit:]][[:digit:]]?
-rw-rw-r-- 1 student student 0 Aug 21 14:47 a33b
120
Globbing -- Подстановка имен файлов.
Bash, сам по себе, не распознает регулярные выражения. Но в сценариях можно ис-
пользовать команды и утилиты, такие как sed и awk, которые прекрасно справляются с
обработкой регулярных выражений.
Фактически, Bash может выполнять подстановку имен файлов, этот процесс назы-
вается "globbing", но при этом не используется стандартный набор регулярных выраже-
ний. Вместо этого, при выполнении подстановки имен файлов, производится распознава-
ние и интерпретация шаблонных символов. В число интерпретируемых шаблонов входят
символы * и ?, списки символов в квадратных скобках и некоторые специальные символы
(например ^, используемый для выполнения операции отрицания). Применение шаблон-
ных символов имеет ряд важных ограничений. Например, если имена файлов начинаются
с точки (например так: .bashrc), то они не будут соответствовать шаблону, содержащему
символ *. Аналогично, символ ? в операции подстановки имен файлов имеет иной смысл,
нежели в регулярных выражениях.
bash$ ls -l
total 2
-rw-rw-r-- 1 student student 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 student student 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 student student 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 student student 466 Aug 6 17:48 [Link]
-rw-rw-r-- 1 student student 758 Jul 30 09:02 [Link]
bash$ ls -l t?.sh
-rw-rw-r-- 1 student student 466 Aug 6 17:48 [Link]
bash$ ls -l [ab]*
-rw-rw-r-- 1 student student 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 student student 0 Aug 6 18:42 b.1
bash$ ls -l [a-c]*
-rw-rw-r-- 1 student student 0 Aug 6 18:42 a.1
-rw-rw-r-- 1 student student 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 student student 0 Aug 6 18:42 c.1
bash$ ls -l [^ab]*
-rw-rw-r-- 1 student student 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 student student 466 Aug 6 17:48 [Link]
-rw-rw-r-- 1 student student 758 Jul 30 09:02 [Link]
bash$ ls -l {b*,c*,*est*}
-rw-rw-r-- 1 student student 0 Aug 6 18:42 b.1
-rw-rw-r-- 1 student student 0 Aug 6 18:42 c.1
-rw-rw-r-- 1 student student 758 Jul 30 09:02 [Link]
bash$ echo *
a.1 b.1 c.1 [Link] [Link]
bash$ echo t*
[Link] [Link]
outer_variable=Outer
(
inner_variable=Inner
echo "Дочерний процесс, \"inner_variable\" = $inner_variable"
echo "Дочерний процесс, \"outer\" = $outer_variable"
)
if [ -z "$inner_variable" ]
then
echo "Переменная inner_variable не определена в родительской оболочке"
else
echo "Переменная inner_variable определена в родительской оболочке"
fi
exit 0
122
Подоболочка может использоваться для задания "специфического окружения" для группы
команд.
COMMAND1
COMMAND2
COMMAND3
(
IFS=:
PATH=/bin
unset TERMINFO
set -C
shift 5
COMMAND4
COMMAND5
exit 3 # Выход только из подоболочки.
)
# Изменение переменных окружения не коснется родительской оболочки.
COMMAND6
COMMAND7
Как вариант использования подоболочки - проверка переменных.
if (set -u; : $variable) 2> /dev/null
then
echo "Переменная определена."
fi
123
Функции.
Подобно "настоящим" языкам программирования, Bash тоже имеет функции, хотя
и в несколько ограниченном варианте. Функция -- это подпрограмма, блок кода который
реализует набор операций, своего рода "черный ящик", предназначенный для выполнения
конкретной задачи. Функции могут использоваться везде, где имеются участки повто-
ряющегося кода.
function function_name {
command...
}
или
function_name () {
command...
}
Вторая форма записи ближе к сердцу C-программистам (она же более переносимая).
Как и в языке C, скобка, открывающая тело функции, может помещаться на следующей
строке.
function_name ()
{
command...
}
Вызов функции осуществляется простым указанием ее имени в тексте сценария.
Пример. Простая функция.
funky ()
{
echo "Это обычная функция."
} # Функция должна быть объявлена раньше, чем ее можно будет использовать.
# Вызов функции.
funky
Функция должна быть объявлена раньше, чем ее можно будет использовать. К сожале-
нию, в Bash нет возможности "опережающего объявления" функции, как например в C.
Допускается даже создание вложенных функций, хотя пользы от этого немного.
DEFAULT=default
# Значение аргумента по-умолчанию.
func2 () {
if [ -z "$1" ]
# Длина аргумента #1 равна нулю?
124
then
echo "-Аргумент #1 имеет нулевую длину.-"
# Или аргумент не был передан функции.
else
echo "-Аргумент #1: \"$1\".-"
fi
if [ "$2" ]
then
echo "-Аргумент #2: \"$2\".-"
fi
return 0
}
echo
Exit и Return.
код завершения
Функции возвращают значение в виде кода завершения. Код завершения может
быть задан явно, с помощью команды return, в противном случае будет возвращен
код завершения последней команды в функции (0 -- в случае успеха, иначе -- нену-
левой код ошибки). Код завершения в сценарии может быть получен через пере-
менную $?.
return
Завершает исполнение функции. Команда return может иметь необязательный ар-
гумент типа integer, который возвращается в вызывающий сценарий как "код за-
125
вершения" функции, это значение так же записывается в переменную $?.
Пример. Наибольшее из двух чисел.
max2 33 34
return_val=$?
LIMIT=200
E_ARG_ERR=65
E_OUT_OF_RANGE=66
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` number-to-convert"
exit $E_ARG_ERR
fi
num=$1
if [ "$num" -gt $LIMIT ]
then
126
echo "Выход за границы диапазона!"
exit $E_OUT_OF_RANGE
fi
to_roman () # Функция должна быть объявлена до того как она будет вы-
звана.
{
number=$1
factor=$2
rchar=$3
let "remainder = number - factor"
while [ "$remainder" -ge 0 ]
do
echo -n $rchar
let "number -= factor"
let "remainder = number - factor"
done
return $number
}
Перенаправление.
Перенаправление ввода для функций
Функции - суть есть блок кода, а это означает, что устройство stdin для функций
может быть переопределено (перенаправление stdin).
Пример. Настоящее имя пользователя.
file=/etc/passwd
pattern=$1
if [ $# -ne "$ARGCOUNT" ]
then
echo "Порядок использования: `basename $0` USERNAME"
exit $E_WRONGARGS
fi
file_excerpt () # Производит поиск в файле по заданному шаблону, выво-
дит требуемую часть строки.
{
while read line
127
do
echo "$line" | grep $1 | awk -F":" '{ print $5 }' # Указывет awk ис-
пользовать ":" как разделитель полей.
done
} <$file # Подменить stdin для функции.
file_excerpt $pattern
# Вместо:
Function ()
{
...
} < file
# Попробуйте так:
Function ()
{
{
...
} < file
}
# Похожий вариант,
Локальные переменные.
Что такое "локальная" переменная?
локальные переменные
Переменные, объявленные как локальные, имеют ограниченную область видимо-
сти, и доступны только в пределах блока, в котором они были объявлены. Для
функций это означает, что локальная переменная "видна" только в теле самой
функции.
Пример. Область видимости локальных переменных.
func ()
128
{
local loc_var=23 # Объявление локальной переменной.
echo
echo "\"loc_var\" в функции = $loc_var"
global_var=999 # Эта переменная не была объявлена локальной.
echo "\"global_var\" в функции = $global_var"
}
func
echo
echo "\"loc_var\" за пределами функции = $loc_var"
# "loc_var" за пределами функции
=
# Итак, $loc_var не видна в гло-
бальном контексте.
echo "\"global_var\" за пределами функции = $global_var"
# "global_var" за пределами функ-
ции = 999
# $global_var имеет глобальную
область видимости.
#!/bin/bash
func ()
{
global_var=37 # Эта переменная будет считаться необъявленной
#+ до тех пор, пока функция не будет вызвана.
} # КОНЕЦ ФУНКЦИИ
func
echo "global_var = $global_var" # global_var = 37
# Переменная была инициализирована в
функции.
Списки команд.
Средством обработки последовательности из нескольких команд служат списки:
"И-списки" и "ИЛИ-списки". Они эффективно могут заменить сложную последователь-
ность вложенных if/then или даже case.
Объединение команд в цепочки.
И-список
command-1 && command-2 && command-3 && ... command-n
Каждая последующая команда, в таком списке, выполняется только тогда, когда
предыдущая команда вернула код завершения true (ноль). Если какая-либо из ко-
манд возвращает false (не ноль), то исполнение списка команд в этом месте завер-
шается, т.е. следующие далее команды не выполняются.
129
Пример. Проверка аргументов командной строки с помощью "И-списка".
# "И-список"
if [ ! -z "$1" ] && echo "Аргумент #1 = $1" && [ ! -z "$2" ] && echo
"Аргумент #2 = $2"
then
echo "Сценарию передано не менее 2 аргументов."
# Все команды в цепочке возвращают true.
else
echo "Сценарию передано менее 2 аргументов."
# Одна из команд в списке вернула false.
fi
# Обратите внимание: "if [ ! -z $1 ]" тоже работает, но, казалось бы
эквивалентный вариант
# if [ -n $1 ] -- нет. Однако, если добавить кавычки
# if [ -n "$1" ] то все работает. Будьте внимательны!
# Проверяемые переменные лучше всегда заключать в кавычки.
ИЛИ-список
command-1 || command-2 || command-3 || ... command-n
Каждая последующая команда, в таком списке, выполняется только тогда, когда
предыдущая команда вернула код завершения false (не ноль). Если какая-либо из
команд возвращает true (ноль), то исполнение списка команд в этом месте заверша-
ется, т.е. следующие далее команды не выполняются. Очевидно, что "ИЛИ-списки"
имеют смысл обратный, по отношению к "И-спискам"
#!/bin/bash
E_BADARGS=65
if [ -z "$1" ]
then
echo "Порядок использования: `basename $0` имя_файла"
exit $E_BADARGS # Если не задано имя файла.
else
file=$1 # Запомнить имя файла.
fi
130
Робкий отказ удаления несуществующего файла."
# И-СПИСОК, выдать сообщение об ошибке, если файл не существует.
# Обратите внимание: выводимое сообщение продолжается во второй строке,
# благодаря экранированию символа перевода строки.
Массивы.
Bash поддерживает одномерные массивы. Инициализация элементов массива мо-
жет быть произведена в виде: variable[xx]. Можно явно объявить массив в сценарии, с
помощью директивы declare: declare -a variable. Обращаться к отдельным элементам
массива можно с помощью фигурных скобок, т.е.: ${variable[xx]}.
Пример. Простой массив.
area[11]=23
area[13]=37
area[51]=UFOs
131
echo "area[6] = area[11] + area[51]"
echo -n "area[6] = "
echo ${area[6]}
# Эта попытка закончится неудачей, поскольку сложение целого
# числа со строкой не допускается.
# -----------------------------------------------------------------
# Другой массив, "area2".
# И другой способ инициализации массива...
# array_name=( XXX YYY ZZZ ... )
area3=([17]=семнадцать [21]=двадцать_один)
Bash позволяет оперировать переменными, как массивами, даже если они не были явно
объявлены таковыми.
string=abcABC123ABCabc
echo ${string[@]} # abcABC123ABCabc
echo ${string[*]} # abcABC123ABCabc
echo ${string[0]} # abcABC123ABCabc
echo ${string[1]} # Ничего не выводится!
# Почему?
echo ${#string[@]} # 1
# Количество элементов в массиве.
/dev и /proc.
Как правило, Linux или UNIX система имеет два каталога специального назначе-
ния: /dev и /proc.
/dev
Каталог /dev содержит файлы физических устройств, которые могут входить в состав
аппаратного обеспечения компьютера. Каждому из разделов не жестком диске соответст-
132
вует свой файл-устройство в каталоге /dev, информация о которых может быть получена
простой командой df.
bash$ df
Filesystem 1k-blocks Used Available Use%
Mounted on
/dev/hda6 495876 222748 247527 48% /
/dev/hda1 50755 3887 44248 9% /boot
/dev/hda8 367013 13262 334803 4% /home
/dev/hda5 1714416 1123624 503704 70% /usr
. /proc
Фактически, каталог /proc - это виртуальная файловая система. Файлы, в каталоге /proc,
содержат информацию о процессах, о состоянии и конфигурации ядра и системы.
Block devices:
1 ramdisk
2 fd
3 ide0
9 md
133
ERR: 0
3 0 3007872 hda 4472 22260 114520 94240 3551 18703 50384 549710 0
111550 644030
3 1 52416 hda1 27 395 844 960 4 2 14 180 0 800 1140
3 2 1 hda2 0 0 0 0 0 0 0 0 0 0 0
3 4 165280 hda4 10 0 20 210 0 0 0 0 0 210 210
...
if [ $CPU = Pentium ]
then
выполнить_ряд_специфичных_команд
...
else
выполнить_ряд_других_специфичных_команд
...
fi
В каталоге /proc вы наверняка заметите большое количество подкаталогов, с не совсем
обычными именами, состоящими только из цифр. Каждый из них соответствует испол-
няющемуся процессу, а имя каталога - это ID (идентификатор) процесса. Внутри каждого
такого подкаталога находится ряд файлов, в которых содержится полезная информация о
соответствующих процессах. Файлы stat и status хранят статистику работы процесса,
cmdline -- команда, которой был запущен процесс, exe -- символическая ссылка на испол-
няемый файл программы. Здесь же вы найдете ряд других файлов, но, с точки зрения на-
писания сценариев, они не так интересны, как эти четыре.
Пример. Поиск файла программы по идентификатору процесса.
#!/bin/bash
# [Link]: Возвращает полный путь к исполняемому файлу программы по
идентификатору процесса (pid).
if [ $# -ne $ARGNO ]
then
134
echo "Порядок использования: `basename $0` PID-процесса" >&2 # Сообщение
об ошибке на >stderr.
exit $E_WRONGARGS
fi
ps ax
# Альтернативный вариант:
# if ! ps $1 > /dev/null 2>&1
# then # в системе нет процесса с заданым pid.
# echo "Нет такого процесса."
# exit $E_NOSUCHPROCESS
# fi
135
# lsof -aFn -p $1 -d txt | sed -ne 's/^n//p'
/dev/zero и /dev/null
/dev/null
Псевдоустройство /dev/null -- это, своего рода, "черная дыра" в системе. Это, по-
жалуй, самый близкий смысловой эквивалент. Все, что записывается в этот файл,
"исчезает" навсегда. Попытки записи или чтения из этого файла не дают, ровным
счетом, никакого результата. Тем не менее, псевдоустройство /dev/null вполне
может пригодиться.
Подавление вывода на stdout.
ln -s /dev/null ~/.netscape/cookies
# Теперь, все cookie-файлы, вместо того, чтобы сохраняться на диске,
будут "вылетать в трубу".
/dev/zero
Подобно псевдоустройству /dev/null, /dev/zero так же является псевдоустройст-
136
вом, с той лишь разницей, что содержит нули. Информация, выводимая в этот
файл, так же бесследно исчезает. Чтение нулей из этого файла может вызвать неко-
торые затруднения, однако это можно сделать, к примеру, с помощью команды od
или шестнадцатиричного редактора. В основном, /dev/zero используется для соз-
дания заготовки файла с заданой длиной.
Пример. Создание файла подкачки (swapfile), с помощью /dev/zero.
#!/bin/bash
# Создание файла подкачки.
# Этот сценарий должен запускаться с правами root.
ROOT_UID=0 # Для root -- $UID 0.
E_WRONG_USER=65 # Не root?
FILE=/swap
BLOCKSIZE=1024
MINBLOCKS=40
SUCCESS=0
if [ "$UID" -ne "$ROOT_UID" ]
then
echo; echo "Этот сценарий должен запускаться с правами root."; echo
exit $E_WRONG_USER
fi
Отладка сценариев.
Командная оболочка Bash не имеет своего отладчика, и не имеет даже каких либо
отладочных команд или конструкций. Синтаксические ошибки или опечатки часто вызы-
вают сообщения об ошибках, которые практически никак не помогают при отладке.
Пример. Сценарий, содержащий ошибку.
137
#!/bin/bash
# [Link]
# Этот сценарий содержит ошибку.
a=37
if [$a -gt 27 ]
then
echo $a
fi
В результате исполнения этого сценария вы получите такое сообщение:
#!/bin/bash
# [Link]:
# Какое сообщение об ошибке будет выведено, при попытке запустить этот сцена-
рий?
for a in 1 2 3
do
echo "$a"
# done # Необходимое ключевое слово 'done' закомментировано.
exit 0
На экране появится сообщение:
Обратите внимание, сообщение об ошибке будет содержать номер не той строки, в кото-
рой возникла ошибка, а той, в которой Bash точно установил наличие ошибочной ситуа-
ции.
Сообщения об ошибках могут вообще не содержать номера строки, при исполнении кото-
рой эта ошибка появилась.
А что делать, если сценарий работает, но не так как ожидалось? Вот пример весьма рас-
пространенной логической ошибки.
Пример. test24.
# Ожидается, что этот сценарий будет удалять в текущем каталоге
#+ все файлы, имена которых содержат пробелы.
# Но он не работает. Почему?
badname=`ls | grep ' '`
# echo "$badname"
rm "$badname"
exit 0
Попробуйте найти ошибку, раскомментарив строку echo "$badname". Инструкция echo
очень полезна при отладке сценариев, она позволяет узнать - действительно ли вы полу-
чаете то, что ожидали получить.
В данном конкретном случае, команда rm "$badname" не дает желаемого результата по-
тому, что переменная $badname взята в кавычки. В результате, rm получает единственный
аргумент (т.е. команда будет считать, что получила имя одного файла). Частично эта про-
блема может быть решена за счет удаления кавычек вокруг $badname и установки пере-
менной $IFS так, чтобы она содержала только символ перевода строки, IFS=$'\n'. Одна-
138
ко, существует более простой способ выполнить эту задачу.
# Правильный способ удаления файлов, в чьих именах содержатся пробелы.
rm *\ *
rm *" "*
rm *' '*
#!/bin/bash
# [Link]
assert () # Если условие ложно,
{ #+ выход из сценария с сообщением об ошибке.
E_PARAM_ERR=98
E_ASSERT_FAILED=99
if [ -z "$2" ] # Недостаточное количество входных парамет-
ров.
then
return $E_PARAM_ERR
fi
lineno=$2
if [ ! $1 ]
139
then
echo "Утверждение ложно: \"$1\""
echo "Файл: \"$0\", строка: $lineno"
exit $E_ASSERT_FAILED
# else
# return
# и продолжить исполнение сценария.
fi
}
a=5
b=4
condition="$a -lt $b" # Сообщение об ощибке и завершение сценария.
# Попробуйте поменять условие "condition"
#+ на что нибудь другое и
#+ посмотреть -- что получится.
assert "$condition" $LINENO
# Сценарий продолжит работу только в том случае, если утверждение ис-
тинно.
# Прочие команды.
# ...
echo "Эта строка появится на экране только если утверждение истинно."
# ...
# Прочие команды.
# ...
Команда exit, в сценарии, порождает сигнал 0, по которому процесс завершает ра-
боту, т.е. - сам сценарий. Часто бывает полезным по выходу из сценария выдать
"распечатку" переменных.
trap '' 2
# Игнорировать прерывание 2 (Control-C), действие по сигналу не указа-
но.
140
Необязательные параметры - это дополнительные ключи (опции), которые оказывают
влияние на поведение сценария и/или командной оболочки.
Команда set позволяет задавать дополнительные опции прямо внутри сценария. В том
месте сценария, где необходимо, чтобы та или иная опция вступила в силу, вставьте такую
конструкцию set -o option-name, или в более короткой форме -- set -option-abbrev. Эти
две формы записи совершенно идентичны по своему действию.
#!/bin/bash
set -o verbose
# Вывод команд перед их исполнением.
#!/bin/bash
set -v
# Имеет тот же эффект, что и выше.
Для того, чтобы отключить действие той или иной опции, следует вставить конструкцию
set +o option-name, или set +option-abbrev.
set -o verbose
# Вывод команд перед их исполнением.
set +o verbose
# Запретить вывод команд перед их исполнением.
command
# команда не выводится.
set -v
# Вывод команд перед их исполнением.
command
set +v
# Запретить вывод команд перед их исполнением.
command
#!/bin/bash -x
# Далее следует текст сценария.
Так же можно указывать дополнительные ключи в командной строке, при запуске сцена-
рия. Некоторые из опций работают только если они заданы из командной строки, напри-
мер -i -- ключ интерактивного режима работы скрипта.
bash -v script-name
bash -o verbose script-name
Ниже приводится список некоторых полезных опций, которые могут быть указаны как в
полной форме так и в сокращенной.
Таблица. Ключи Bash
Краткое
Полное имя Описание
имя
-C noclobber Предотвращает перезапись файла в операциях перенаправле-
ния вывода (не распространяется на конвейеры (каналы) -- >|)
-D (нет) Выводит список строк в двойных кавычках, которым пред-
шествует символ $, сам сценарий не исполняется
-a allexport Экспорт всех, определенных в сценарии, переменных
-b notify Выводит уведомление по завершении фоновой задачи (job)
(довольно редко используется в сценариях)
141
Краткое
Полное имя Описание
имя
-c ... (нет) Читает команды из ...
-f noglob Подстановка имен файлов (globbing) запрещена
-i interactive Сценарий запускается в интерактивном режиме
-p privileged Сценарий запускается как "suid" (осторожно!)
-r restricted Сценарий запускается в ограниченном режиме.
-u nounset При попытке обращения к неопределенным переменным, вы-
дает сообщение об ошибке и прерывает работу сценария
-v verbose Выводит на stdout каждую команду прежде, чем она будет
исполнена
-x xtrace Подобна -v, но выполняет подстановку команд
-e errexit Прерывает работу сценария при появлении первой же ошиб-
ки (когда команда возвращает ненулевой код завершения)
-n noexec Читает команды из сценария, но не исполняет их (проверка
синтаксиса)
-s stdin Читает команды с устройства stdin
-t (нет) Выход после исполнения первой команды
- (нет) Конец списка ключей (опций), последующие аргументы бу-
дут восприниматься как позиционные параметры.
-- (нет) Эквивалент предыдущей опции (-).
142