by William Shotts
У И Л Ь Я М Ш О Т Т С
КОМАНДНАЯ СТРОКА LINUX
П О Л Н ОЕ Р У К О В О Д С Т В О
2 - Е М Е Ж Д У Н А Р О Д Н О Е И З Д А Н И Е
ББК 32.973.2-018.2 УДК 004.451 Ш80
Ш80
Шоттс У. Командная строка Linux. Полное руководство. 2-е межд. изд. — СПб.: Питер, 2020. — 544 с.: ил. — (Серия «Для профессионалов»).
ISBN 978-5-4461-1430-6
Международный бестселлер «Командная строка Linux» поможет преодолеть путь от первых робких щелчков по клавишам до уверенного создания полноценных программ для последней версии bash — наиболее популярной командной оболочки Linux.
Второе издание рассказывает о новых возможностях bash 4.x, таких как новые операторы перена- правления и операции подстановки. В разделе, посвященном сценариям оболочки, рассматриваются современные надежные методы программирования, способы предотвращения типичных ошибок и потенциально опасных ситуаций.
Вы освоите неустаревающие навыки владения командной строкой: навигацию по файловой системе, настройку окружения, объединение команд в цепочки и сопоставление с регулярными выражениями. Вы постигнете философию, лежащую в основе многих инструментов командной строки, разберетесь с богатым наследием, полученным от суперкомпьютеров с Unix, и приобщитесь к знаниям, накоплен- ным поколениями гуру, исключивших мышь из своего арсенала инструментов.
16+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)
ББК 32.973.2-018.2 УДК 004.451
Права на издание получены по соглашению с No Starch Press. Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как на- дежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные ошибки, связанные с использованием книги. Издательство не несет ответственности за доступность материалов, ссылки на которые вы можете найти в этой книге. На момент подготовки книги к изданию все ссылки на интернет- ресурсы были действующими.
ISBN 978-1593279523 англ.
ISBN 978-5-4461-1430-6
© 2019 by William Shotts. The Linux Command Line, 2nd Edition: A Complete Introduction ISBN 978-1-59327-952-3, published by No Starch Press. © Перевод на русский язык ООО Издательство «Питер», 2020 © Издание на русском языке, оформление ООО Издательство
«Питер», 2020
© Серия «Для профессионалов», 2020
Краткое содержание
Об авторе ………………………………………………………………………………………………. 24
О научном редакторе……………………………………………………………………………… 25
Благодарности ………………………………………………………………………………………. 26
Введение ………………………………………………………………………………………………. 28
Часть I. КОМАНДНАЯ ОБОЛОЧКА
Глава 1. Что такое командная оболочка ………………………………………………………….. 36
Глава 2. Навигация ……………………………………………………………………………………… 41
Глава 3. Исследование системы …………………………………………………………………….. 47
Глава 4. Операции с файлами и каталогами …………………………………………………….. 59
Глава 5. Работа с командами …………………………………………………………………………. 76
Глава 6. Перенаправление ……………………………………………………………………………. 88
Глава 7. Взгляд на мир глазами командной оболочки ………………………………………. 102
Глава 8. Продвинутые приемы работы с клавиатурой ………………………………………. 114
Глава 9. Привилегии ………………………………………………………………………………….. 123
Глава 10. Процессы ……………………………………………………………………………………. 144
Часть II. ОКРУЖЕНИЕ И НАСТРОЙКА
Глава 11. Окружение …………………………………………………………………………………. 160
Глава 12. Плавное введение в vi ………………………………………………………………….. 173
Глава 13. Настройка приглашения к вводу …………………………………………………….. 192
6
Краткое содержание
Часть III. ТИПИЧНЫЕ ЗАДАЧИ И ОСНОВНЫЕ ИНСТРУМЕНТЫ
Глава 14. Управление пакетами …………………………………………………………………… 202
Глава 15. Устройства хранения ……………………………………………………………………. 212
Глава 16. Сети ………………………………………………………………………………………….. 231
Глава 17. Поиск файлов ……………………………………………………………………………… 247
Глава 18. Архивация и резервное копирование ………………………………………………. 264
Глава 19. Регулярные выражения ………………………………………………………………… 281
Глава 20. Обработка текста ………………………………………………………………………… 303
Глава 21. Форматирование вывода ………………………………………………………………. 344
Глава 22. Печать ……………………………………………………………………………………….. 365
Глава 23. Компиляция программ ………………………………………………………………….. 379
Часть IV. СЦЕНАРИИ КОМАНДНОЙ ОБОЛОЧКИ
Глава 24. Создание первого сценария командной оболочки ……………………………… 392
Глава 25. Начало проекта …………………………………………………………………………… 399
Глава 26. Проектирование сверху вниз …………………………………………………………. 410
Глава 27. Управление потоком выполнения: ветвление при помощи if ……………….. 420
Глава 28. Чтение ввода с клавиатуры …………………………………………………………… 437
Глава 29. Управление потоком выполнения: циклы while и until ………………………… 449
Глава 30. Поиск и устранение ошибок …………………………………………………………… 456
Глава 31. Управление потоком выполнения: ветвление с помощью case …………….. 470
Глава 32. Позиционные параметры ………………………………………………………………. 477
Глава 33. Управление потоком выполнения: цикл for ………………………………………. 491
Глава 34. Строки и числа ……………………………………………………………………………. 498
Глава 35. Массивы …………………………………………………………………………………….. 520
Глава 36. Экзотика …………………………………………………………………………………….. 530
Оглавление
Об авторе ………………………………………………………………………………………………. 24
О научном редакторе……………………………………………………………………………… 25
Благодарности ………………………………………………………………………………………. 26
К первому изданию ………………………………………………………………………………….. 26
Ко второму изданию …………………………………………………………………………………. 27
Введение ………………………………………………………………………………………………. 28
Зачем нужна командная строка? …………………………………………………………………. 29
О чем эта книга ……………………………………………………………………………………….. 29
Кому адресована эта книга ………………………………………………………………………… 30
Что дается в этой книге …………………………………………………………………………….. 31
Как читать эту книгу …………………………………………………………………………………. 31
Предварительные условия …………………………………………………………………… 32
Что нового во втором издании ……………………………………………………………………. 33
Ваши отзывы важны для нас! …………………………………………………………………….. 33
От издательства ………………………………………………………………………………………. 34
Часть I. КОМАНДНАЯ ОБОЛОЧКА ………………………………………………… 35
Глава 1. Что такое командная оболочка ………………………………………………….. 36
Эмуляторы терминалов……………………………………………………………………………… 36
Первые удары по клавишам ………………………………………………………………………. 36
История команд ………………………………………………………………………………….. 38
Управление курсором ………………………………………………………………………….. 38
Некоторые простые команды ……………………………………………………………………… 38
Завершение сеанса работы с терминалом …………………………………………………….. 39
Заключение …………………………………………………………………………………………….. 40
8
Оглавление
Глава 2. Навигация ………………………………………………………………………………… 41
Дерево каталогов файловой системы ………………………………………………………….. 41
Текущий рабочий каталог ………………………………………………………………………….. 42
Вывод содержимого каталога …………………………………………………………………….. 43
Смена текущего рабочего каталога …………………………………………………………….. 43
Абсолютные пути ……………………………………………………………………………….. 43
Относительные пути ……………………………………………………………………………. 44
Некоторые полезные сокращения …………………………………………………………. 46
Заключение …………………………………………………………………………………………….. 46
Глава 3. Исследование системы ……………………………………………………………… 47
Любопытные возможности ls ……………………………………………………………………… 47
Параметры и аргументы ………………………………………………………………………. 48
Пристальный взгляд на длинный формат ……………………………………………….. 49
Определение типов файлов командой file …………………………………………………….. 50
Просмотр содержимого файлов командой less ………………………………………………. 51
Обзорное путешествие ……………………………………………………………………………… 53
Символические ссылки ……………………………………………………………………………… 57
Жесткие ссылки ……………………………………………………………………………………….. 58
Заключение …………………………………………………………………………………………….. 58
Глава 4. Операции с файлами и каталогами ……………………………………………. 59
Групповые символы ………………………………………………………………………………….. 60
mkdir — создание каталогов ………………………………………………………………………. 62
cp — копирование файлов и каталогов ………………………………………………………… 63
Параметры команды cp и примеры ее использования ……………………………….. 63
mv — перемещение и переименование файлов …………………………………………….. 64
Параметры команды mv и примеры ее использования ………………………………. 64
rm — удаление файлов и каталогов ……………………………………………………………. 65
Параметры команды rm и примеры ее использования ………………………………. 65
ln — создание ссылок ……………………………………………………………………………….. 67
Жесткие ссылки …………………………………………………………………………………. 67
Символические ссылки ………………………………………………………………………… 68
Оглавление
9
Постройка песочницы ……………………………………………………………………………….. 68
Создание каталогов …………………………………………………………………………….. 68
Копирование файлов …………………………………………………………………………… 69
Перемещение и переименование файлов ……………………………………………….. 70
Создание жестких ссылок …………………………………………………………………….. 71
Создание символических ссылок …………………………………………………………… 72
Удаление файлов и каталогов ………………………………………………………………. 73
Заключение …………………………………………………………………………………………….. 75
Глава 5. Работа с командами ………………………………………………………………….. 76
Что такое команды? ………………………………………………………………………………….. 76
Идентификация команд …………………………………………………………………………….. 77
type — получение типа команды …………………………………………………………… 77
which — определение местоположения выполняемого файла ……………………. 77
Получение документации с описанием команд ……………………………………………… 78
help — получение справки для встроенных команд ………………………………….. 78
–help — вывод инструкции по использованию ………………………………………… 80
man — вывод страниц справочного руководства ……………………………………… 80
apropos — вывод списка подходящих команд ………………………………………….. 82
whatis — вывод очень краткого описания команды ………………………………….. 83
info — вывод записи из справочного руководства Info ………………………………. 83
README и другие файлы с описанием программ………………………………………. 85
Создание собственных команд с помощью alias …………………………………………….. 85
Заключение …………………………………………………………………………………………….. 87
Глава 6. Перенаправление ……………………………………………………………………… 88
Стандартный ввод, вывод и вывод ошибок …………………………………………………… 88
Перенаправление стандартного вывода ………………………………………………………. 89
Перенаправление стандартного вывода ошибок ……………………………………………. 91
Перенаправление стандартного вывода и стандартного вывода ошибок в один файл ………………………………………………………………………………………. 91
Удаление нежелательного вывода ………………………………………………………… 92
Перенаправление стандартного ввода …………………………………………………………. 93
cat — объединение файлов ………………………………………………………………….. 93
10
Оглавление
Конвейеры………………………………………………………………………………………………. 95
Фильтры ……………………………………………………………………………………………. 95
uniq — поиск или удаление повторяющихся строк ……………………………………. 96
wc — вывод числа строк, слов и байтов …………………………………………………. 97
grep — поиск строк, соответствующих шаблону ………………………………………. 97
head/tail — вывод первых/последних строк из файлов ……………………………… 98
tee — чтение со стандартного ввода и запись в стандартный вывод и в файлы ………………………………………………………………………………… 99
Заключение …………………………………………………………………………………………… 101
Глава 7. Взгляд на мир глазами командной оболочки ……………………………. 102
Подстановка ………………………………………………………………………………………….. 102
Подстановка путей ……………………………………………………………………………. 103
Подстановка тильды ………………………………………………………………………….. 104
Подстановка результатов арифметических выражений …………………………… 105
Подстановка фигурных скобок ……………………………………………………………. 106
Подстановка параметров ……………………………………………………………………. 107
Подстановка команд ………………………………………………………………………….. 108
Экранирование ………………………………………………………………………………………. 109
Двойные кавычки ……………………………………………………………………………… 109
Одиночные кавычки ………………………………………………………………………….. 111
Экранирование символов …………………………………………………………………… 111
Управляющие последовательности………………………………………………………. 112
Заключение …………………………………………………………………………………………… 113
Глава 8. Продвинутые приемы работы с клавиатурой ……………………………. 114
Редактирование командной строки ……………………………………………………………. 114
Перемещение курсора ……………………………………………………………………….. 115
Изменение текста ……………………………………………………………………………… 115
Вырезание и вставка (удаление и возврат) текста ………………………………….. 117
Дополнение …………………………………………………………………………………………… 117
Использование истории …………………………………………………………………………… 119
Поиск в истории ……………………………………………………………………………….. 119
Подстановка записей истории …………………………………………………………….. 121
Заключение …………………………………………………………………………………………… 122
Оглавление
11
Глава 9. Привилегии …………………………………………………………………………….. 123
Владельцы, члены группы и все остальные ………………………………………………… 124
Чтение, запись и выполнение …………………………………………………………………… 126
chmod — изменение режима доступа к файлу ……………………………………….. 128
Установка режима доступа к файлу с помощью графического интерфейса ………………………………………………………………….. 131
umask — определение разрешений доступа к файлам по умолчанию ………… 132
Некоторые специальные разрешения …………………………………………………… 134
Изменение идентичности …………………………………………………………………………. 135
su — запуск командной оболочки с подстановкой идентификаторов пользователя и группы ………………………………………………………………………. 136
sudo — выполнение команды от имени другого пользователя ………………….. 137
chown — изменение владельца и группы файла …………………………………….. 139
chgrp — изменение группы файла ……………………………………………………….. 140
Использование привилегий ……………………………………………………………………… 140
Изменение своего пароля ………………………………………………………………………… 142
Заключение …………………………………………………………………………………………… 143
Глава 10. Процессы ……………………………………………………………………………… 144
Как действует процесс …………………………………………………………………………….. 144
Просмотр списка процессов ……………………………………………………………………… 145
Просмотр состояния процессов в динамике с помощью top ……………………… 148
Управление процессами ………………………………………………………………………….. 150
Прерывание процесса ……………………………………………………………………….. 151
Перевод процессов в фоновый режим ………………………………………………….. 151
Возврат процесса на передний план…………………………………………………….. 152
Приостановка процесса ……………………………………………………………………… 152
Сигналы ……………………………………………………………………………………………….. 153
Посылка сигналов процессам командой kill ……………………………………………. 154
Посылка сигналов нескольким процессам с помощью killall ……………………… 156
Остановка системы …………………………………………………………………………………. 157
Другие команды управления процессами ……………………………………………………. 158
Заключение …………………………………………………………………………………………… 158
12
Оглавление
Часть II. ОКРУЖЕНИЕ И НАСТРОЙКА …………………………………………. 159
Глава 11. Окружение ……………………………………………………………………………. 160
Что хранится в окружении? ……………………………………………………………………… 160
Исследование окружения …………………………………………………………………… 161
Некоторые интересные переменные …………………………………………………….. 162
Как устанавливается окружение? ……………………………………………………………… 163
Что находится в файлах запуска? ………………………………………………………… 165
Изменение окружения …………………………………………………………………………….. 167
Какие файлы следует изменять? …………………………………………………………. 167
Текстовые редакторы ………………………………………………………………………… 167
Использование текстового редактора …………………………………………………… 168
Активация изменений ………………………………………………………………………… 172
Заключение …………………………………………………………………………………………… 172
Глава 12. Плавное введение в vi …………………………………………………………… 173
Зачем осваивать vi …………………………………………………………………………………. 173
Немного предыстории …………………………………………………………………………….. 174
Запуск и завершение vi …………………………………………………………………………… 174
Режимы редактирования …………………………………………………………………………. 176
Переход в режим вставки …………………………………………………………………… 177
Сохранение изменений ………………………………………………………………………. 177
Перемещение курсора …………………………………………………………………………….. 178
Основы редактирования ………………………………………………………………………….. 179
Добавление текста в конец ………………………………………………………………… 179
Вставка строки …………………………………………………………………………………. 180
Удаление текста ……………………………………………………………………………….. 181
Вырезание, копирование и вставка текста…………………………………………….. 182
Объединение строк …………………………………………………………………………… 183
Поиск и замена ………………………………………………………………………………………. 184
Поиск в пределах строки ……………………………………………………………………. 184
Поиск во всем файле …………………………………………………………………………. 184
Глобальный поиск и замена ……………………………………………………………….. 185
Оглавление
13
Редактирование нескольких файлов ………………………………………………………….. 186
Переключение между файлами …………………………………………………………… 187
Открытие дополнительных файлов для редактирования …………………………. 188
Копирование содержимого из одного файла в другой …………………………….. 188
Вставка целого файла в другой файл …………………………………………………… 189
Сохранение результатов работы ……………………………………………………………….. 190
Заключение …………………………………………………………………………………………… 191
Глава 13. Настройка приглашения к вводу ……………………………………………. 192
Устройство строки приглашения к вводу ……………………………………………………. 192
Альтернативные варианты оформления приглашения ………………………………….. 194
Добавление цвета ………………………………………………………………………………….. 196
Перемещение курсора …………………………………………………………………………….. 198
Сохранение определения приглашения ……………………………………………………… 200
Заключение …………………………………………………………………………………………… 200
Часть III. ТИПИЧНЫЕ ЗАДАЧИ И ОСНОВНЫЕ ИНСТРУМЕНТЫ ………. 201
Глава 14. Управление пакетами ……………………………………………………………. 202
Системы пакетов ……………………………………………………………………………………. 203
Как действует система пакетов …………………………………………………………………. 203
Файлы пакетов …………………………………………………………………………………. 203
Репозитории …………………………………………………………………………………….. 204
Зависимости …………………………………………………………………………………….. 205
Высоко- и низкоуровневые инструменты управления пакетами ………………… 205
Типичные задачи управления пакетами……………………………………………………… 206
Поиск пакета в репозитории ………………………………………………………………. 206
Установка пакета из репозитория ………………………………………………………… 206
Установка пакета из файла пакета ………………………………………………………. 207
Удаление пакета ………………………………………………………………………………. 207
Обновление пакетов из репозитория ……………………………………………………. 208
Обновление пакета из файла пакета ……………………………………………………. 208
Список установленных пакетов …………………………………………………………… 209
Определение, установлен ли пакет ……………………………………………………… 209
14
Оглавление
Вывод информации об установленном пакете ……………………………………….. 209
Поиск пакета по установленному файлу ……………………………………………….. 210
Заключение …………………………………………………………………………………………… 210
Глава 15. Устройства хранения …………………………………………………………….. 212
Монтирование и размонтирование устройств хранения ………………………………… 213
Просмотр списка смонтированных файловых систем ………………………………. 214
Определение названий устройств………………………………………………………… 218
Создание новых файловых систем …………………………………………………………….. 221
Управление разделами с помощью fdisk ……………………………………………….. 221
Создание новой файловой системы с помощью mkfs ………………………………. 224
Проверка и восстановление файловой системы …………………………………………… 225
Непосредственное перемещение данных между устройствами ………………………. 226
Создание образа компакт-диска ………………………………………………………….. 226
Создание образа-копии компакт-диска …………………………………………………. 227
Создание образа из коллекции файлов ………………………………………………… 227
Запись образа компакт-диска …………………………………………………………………… 228
Непосредственное монтирование файла ISO-образа ………………………………. 228
Очистка перезаписываемых компакт-дисков …………………………………………. 228
Запись образа ………………………………………………………………………………….. 228
Заключительное замечание ……………………………………………………………………… 229
Дополнительные сведения ………………………………………………………………………. 229
Глава 16. Сети ……………………………………………………………………………………… 231
Исследование и мониторинг сети ……………………………………………………………… 232
ping ………………………………………………………………………………………………… 232
traceroute ………………………………………………………………………………………… 233
ip……………………………………………………………………………………………………. 234
netstat …………………………………………………………………………………………….. 235
Передача файлов по сети ………………………………………………………………………… 237
ftp ………………………………………………………………………………………………….. 237
lftp — более удачная версия ftp…………………………………………………………… 239
wget ……………………………………………………………………………………………….. 239
Оглавление
15
Безопасные взаимодействия с удаленными узлами ………………………………………. 240
ssh …………………………………………………………………………………………………. 240
scp и sftp …………………………………………………………………………………………. 245
Заключение …………………………………………………………………………………………… 246
Глава 17. Поиск файлов ……………………………………………………………………….. 247
locate — простой способ поиска файлов …………………………………………………….. 247
find — сложный способ поиска файлов ………………………………………………………. 249
Проверки ………………………………………………………………………………………… 250
Операторы ………………………………………………………………………………………. 253
Предопределенные операции ……………………………………………………………… 255
Операции, определяемые пользователем ……………………………………………… 257
Увеличение эффективности ……………………………………………………………….. 258
xargs ………………………………………………………………………………………………. 259
Возвращаемся в песочницу ………………………………………………………………… 260
Параметры ………………………………………………………………………………………. 262
Заключение …………………………………………………………………………………………… 263
Глава 18. Архивация и резервное копирование …………………………………….. 264
Сжатие файлов ………………………………………………………………………………………. 264
gzip ………………………………………………………………………………………………… 265
bzip2 ………………………………………………………………………………………………. 268
Архивирование файлов …………………………………………………………………………… 268
tar ………………………………………………………………………………………………….. 269
zip ………………………………………………………………………………………………….. 274
Синхронизация файлов и каталогов ………………………………………………………….. 276
Использование rsync для копирования по сети ………………………………………. 279
Заключение …………………………………………………………………………………………… 280
Глава 19. Регулярные выражения ………………………………………………………… 281
Что такое регулярные выражения? ……………………………………………………………. 281
grep ……………………………………………………………………………………………………… 282
Метасимволы и литералы ………………………………………………………………………… 284
Любой символ ……………………………………………………………………………………….. 284
16
Оглавление
Якоря …………………………………………………………………………………………………… 285
Выражения в квадратных скобках и классы символов …………………………………… 286
Отрицание ………………………………………………………………………………………. 287
Традиционные диапазоны символов …………………………………………………….. 287
Классы символов POSIX …………………………………………………………………………… 288
Простые и расширенные регулярные выражения POSIX ……………………………….. 291
Чередование …………………………………………………………………………………………. 293
Квантификаторы ……………………………………………………………………………………. 295
? — совпадение с элементом ноль или один раз …………………………………….. 295
- — совпадение с элементом ноль или более раз …………………………………… 295
- — совпадение с элементом один или более раз ………………………………….. 296
{ } — совпадение с элементом определенное число раз ………………………….. 297
Практические примеры применения регулярных выражений …………………………. 298
Проверка списка телефонов с помощью grep ………………………………………… 298
Поиск необычных имен файлов с помощью find …………………………………….. 299
Поиск файлов с помощью locate ………………………………………………………….. 299
Поиск текста в less и vim ……………………………………………………………………. 300
Заключение …………………………………………………………………………………………… 301
Глава 20. Обработка текста ………………………………………………………………….. 303
Области применения текста …………………………………………………………………….. 304
Документы ………………………………………………………………………………………. 304
Веб-страницы …………………………………………………………………………………… 304
Электронная почта ……………………………………………………………………………. 304
Вывод на принтер …………………………………………………………………………….. 304
Исходный код программ …………………………………………………………………….. 305
А вот и наши старые знакомые! ………………………………………………………………… 305
cat ……………………………………………………………………………………………………….. 305
sort…………………………………………………………………………………………………. 307
uniq — выявление или удаление повторяющихся строк …………………………… 314
Нарезка и перетасовка текста ………………………………………………………………….. 316
cut — удаление фрагментов из всех строк в файлах……………………………….. 316
paste — слияние строк из файлов ……………………………………………………….. 319
join — объединение строк из двух файлов по общему полю …………………….. 321
Оглавление
17
Сравнение текста …………………………………………………………………………………… 323
comm — построчное сравнение двух сортированных файлов …………………… 323
diff — построчное сравнение файлов …………………………………………………… 324
patch — применение diff-файла к оригиналу ………………………………………….. 327
Редактирование на лету ………………………………………………………………………….. 328
tr — перекодирование или удаление символов ………………………………………. 328
sed — потоковый редактор для фильтрации и преобразования текста ………. 331
aspell — интерактивная проверка орфографии ………………………………………. 339
Заключение …………………………………………………………………………………………… 343
Дополнительное задание …………………………………………………………………………. 343
Глава 21. Форматирование вывода ……………………………………………………….. 344
Инструменты простого форматирования …………………………………………………….. 344
nl — нумерация строк ………………………………………………………………………… 345
fold — перенос строк после указанной длины ………………………………………… 348
fmt — простое форматирование текста ………………………………………………… 349
pr — форматирование текста для печати ……………………………………………… 352
printf — форматирование и вывод данных …………………………………………….. 353
Системы форматирования документов ……………………………………………………….. 357
groff ……………………………………………………………………………………………….. 358
Заключение …………………………………………………………………………………………… 364
Глава 22. Печать ………………………………………………………………………………….. 365
Краткая история поддержки печати…………………………………………………………… 365
Печать в ночное время ………………………………………………………………………. 366
Символьные принтеры ………………………………………………………………………. 366
Графические принтеры ……………………………………………………………………… 367
Печать в Linux ……………………………………………………………………………………….. 369
Подготовка файлов к печати ……………………………………………………………………. 369
pr — преобразование текстовых файлов для печати ………………………………. 369
Отправка задания печати на принтер ………………………………………………………… 371
lpr — печать файлов (в стиле Berkeley) ………………………………………………… 371
lp — печать файлов (в стиле System V) ………………………………………………… 372
Еще одна возможность: a2ps ………………………………………………………………. 373
18
Оглавление
Наблюдение за заданиями печати и управление ими …………………………………… 376
lpstat — вывод информации о состоянии принтера …………………………………. 376
lpq — вывод информации о состоянии очереди печати …………………………… 377
lprm и cancel — отмена заданий печати ………………………………………………… 378
Заключение …………………………………………………………………………………………… 378
Глава 23. Компиляция программ …………………………………………………………… 379
Что такое компиляция? …………………………………………………………………………… 380
Все ли программы компилируются? ……………………………………………………… 381
Компиляция программ на C ……………………………………………………………………… 382
Получение исходного кода …………………………………………………………………. 382
Исследование дерева исходных текстов ……………………………………………….. 384
Сборка программ ………………………………………………………………………………. 386
Установка программ ………………………………………………………………………….. 390
Заключение …………………………………………………………………………………………… 390
Часть IV. СЦЕНАРИИ КОМАНДНОЙ ОБОЛОЧКИ …………………………… 391
Глава 24. Создание первого сценария командной оболочки …………………… 392
Что такое сценарии командной оболочки …………………………………………………… 392
Как написать сценарий командной оболочки ………………………………………………. 393
Формат файла сценария …………………………………………………………………….. 393
Разрешения на выполнение ……………………………………………………………….. 394
Местоположение файла сценария ……………………………………………………….. 394
Выбор местоположения для сценариев ………………………………………………… 396
Дополнительные хитрости по оформлению ………………………………………………… 397
Длинные имена параметров ……………………………………………………………….. 397
Отступы и продолжения строк ……………………………………………………………. 397
Заключение …………………………………………………………………………………………… 398
Глава 25. Начало проекта …………………………………………………………………….. 399
Этап первый: минимальный документ ……………………………………………………….. 399
Этап второй: добавление некоторых данных ………………………………………………. 401
Переменные и константы ………………………………………………………………………… 402
Присваивание значений переменным и константам ………………………………… 405
Оглавление
19
Встроенные документы ……………………………………………………………………………. 407
Заключение …………………………………………………………………………………………… 409
Глава 26. Проектирование сверху вниз …………………………………………………. 410
Функции командной оболочки ………………………………………………………………….. 411
Локальные переменные …………………………………………………………………………… 414
Постоянное опробование сценария …………………………………………………………… 416
Заключение …………………………………………………………………………………………… 419
Глава 27. Управление потоком выполнения: ветвление при помощи if ……………………………………………………………………… 420
Инструкция if …………………………………………………………………………………………. 420
Код завершения …………………………………………………………………………………….. 421
Команда test ………………………………………………………………………………………….. 423
Выражения для проверки файлов ……………………………………………………….. 423
Выражения для проверки строк …………………………………………………………… 426
Выражения для проверки целых чисел …………………………………………………. 428
Более современная версия команды test ……………………………………………………. 429
(( )) — для проверки целых чисел ……………………………………………………….. 430
Объединение выражений ………………………………………………………………………… 431
Операторы управления: еще один способ ветвления ……………………………………. 434
Заключение …………………………………………………………………………………………… 435
Глава 28. Чтение ввода с клавиатуры……………………………………………………. 437
read — чтение значений со стандартного ввода ………………………………………….. 438
Параметры ………………………………………………………………………………………. 440
Выделение полей в строке ввода с помощью IFS ……………………………………. 442
Проверка ввода ……………………………………………………………………………………… 444
Меню ……………………………………………………………………………………………………. 446
Заключение …………………………………………………………………………………………… 448
Дополнительные сведения ………………………………………………………………………. 448
Глава 29. Управление потоком выполнения: циклы while и until ……………. 449
Циклы ………………………………………………………………………………………………….. 449
while ……………………………………………………………………………………………….. 450
20
Оглавление
Прерывание цикла………………………………………………………………………………….. 452
until ………………………………………………………………………………………………… 454
Чтение файлов в циклах ………………………………………………………………………….. 454
Заключение …………………………………………………………………………………………… 455
Глава 30. Поиск и устранение ошибок …………………………………………………… 456
Синтаксические ошибки ………………………………………………………………………….. 456
Отсутствующие кавычки …………………………………………………………………….. 457
Отсутствующие или неожиданные лексемы …………………………………………… 458
Непредвиденная подстановка …………………………………………………………….. 458
Логические ошибки ………………………………………………………………………………… 460
Защитное программирование ……………………………………………………………… 460
Будьте внимательны к именам файлов …………………………………………………. 462
Проверка ввода ………………………………………………………………………………… 463
Тестирование ………………………………………………………………………………………… 464
Комплекты тестов …………………………………………………………………………….. 465
Отладка ………………………………………………………………………………………………… 465
Поиск проблемной области ………………………………………………………………… 465
Трассировка …………………………………………………………………………………….. 466
Исследование значений в процессе выполнения ……………………………………. 468
Заключение …………………………………………………………………………………………… 469
Глава 31. Управление потоком выполнения: ветвление с помощью case …………………………………………………………………… 470
Команда case …………………………………………………………………………………………. 470
Шаблоны …………………………………………………………………………………………. 472
Выполнение нескольких вариантов ……………………………………………………… 474
Заключение …………………………………………………………………………………………… 476
Глава 32. Позиционные параметры ………………………………………………………. 477
Доступ к командной строке ……………………………………………………………………… 477
Определение числа аргументов …………………………………………………………… 478
shift — доступ к множеству аргументов ………………………………………………… 479
Простые приложения ………………………………………………………………………… 480
Оглавление
21
Использование позиционных параметров в функциях …………………………….. 481
Обработка позиционных параметров скопом ………………………………………………. 482
Более сложное приложение …………………………………………………………………….. 484
Заключение …………………………………………………………………………………………… 487
Глава 33. Управление потоком выполнения: цикл for ……………………………. 491
for: традиционная форма …………………………………………………………………………. 491
for: форма в стиле языка C ………………………………………………………………………. 494
Заключение …………………………………………………………………………………………… 496
Глава 34. Строки и числа ……………………………………………………………………… 498
Подстановка параметров …………………………………………………………………………. 498
Простые параметры ………………………………………………………………………….. 499
Подстановка пустых переменных ………………………………………………………… 499
Получение имен переменных ……………………………………………………………… 501
Операции со строками ……………………………………………………………………….. 502
Преобразование регистра символов …………………………………………………….. 505
Вычисление и подстановка арифметических выражений …………………………. 507
Основание системы счисления ……………………………………………………………. 508
Унарные операторы ………………………………………………………………………….. 508
Простая арифметика …………………………………………………………………………. 508
Присваивание ………………………………………………………………………………….. 510
Битовые операции …………………………………………………………………………….. 512
Логические операторы ………………………………………………………………………. 513
bc — язык калькулятора для вычислений с произвольной точностью ……………… 516
Применение bc …………………………………………………………………………………. 517
Пример сценария ……………………………………………………………………………… 518
Заключение …………………………………………………………………………………………… 519
Дополнительные сведения ………………………………………………………………………. 519
- Массивы …………………………………………………………………………………………. 520
Что такое массивы? ………………………………………………………………………………… 520
Создание массива …………………………………………………………………………………… 521
Присваивание значений массиву ………………………………………………………………. 521
22
Оглавление
Доступ к элементам массива…………………………………………………………………….. 522
Операции с массивами ……………………………………………………………………………. 524
Вывод содержимого всего массива ………………………………………………………. 524
Определение числа элементов в массиве ……………………………………………… 525
Поиск используемых индексов…………………………………………………………….. 526
Добавление элементов в конец массива ……………………………………………….. 526
Сортировка массива ………………………………………………………………………….. 526
Удаление массива …………………………………………………………………………….. 527
Ассоциативные массивы ………………………………………………………………………….. 528
Заключение …………………………………………………………………………………………… 529
Глава 36. Экзотика ……………………………………………………………………………….. 530
Группы команд и подоболочки …………………………………………………………………. 530
Подстановка процессов …………………………………………………………………………… 534
Ловушки ……………………………………………………………………………………………….. 536
Асинхронное выполнение с командой wait ………………………………………………….. 540
Именованные каналы ……………………………………………………………………………… 541
Создание именованного канала …………………………………………………………… 542
Использование именованных каналов ………………………………………………….. 542
Заключение …………………………………………………………………………………………… 543
Посвящается Карен
Об авторе
Уильям Шоттс (William Shotts) — профессиональный разработчик программного обеспечения с более чем 30-летним стажем, который уже больше 20 лет активно пользуется операционной системой Linux. Имеет богатый опыт разработки про- граммного обеспечения, технической поддержки, контроля качества и написания документации. Также является создателем LinuxCommand.org, образовательного и информационно-просветительского сайта, посвященного Linux, где публикуют- ся новости, обзоры и оказывается поддержка пользующимся командной строкой Linux.
О научном редакторе
Жорди Гутьеррес Эрмосо (Jordi Gutiérrez Hermoso) — программист, математик и сторонник этичного хакерства. С 2002 года пользуется исключительно Debian GNU/Linux не только дома, но и на работе. Жорди участвует в разработке GNU Octave, бесплатной вычислительной среды, во многом совместимой с Matlab, а также Mercurial, распределенной системы управления версиями. Увлекается чистой и прикладной математикой, катанием на коньках, плаванием и вязанием. В последнее время много думает о проблемах выброса парниковых газов и уча- ствует в акциях по спасению носорогов.
Благодарности
Я хочу поблагодарить всех, кто помог появиться на свет этой книге.
К первому изданию
В первую очередь хочу поблагодарить тех, кто вдохновил меня:
Дженни Уотсон (Jenny Watson), рецензента издательства Wiley Publishing, — она первая предложила написать книгу о языке сценариев командной оболочки.
Джона Дворака (John C. Dvorak), известного колумниста и большого эрудита, — в эпизоде своего видеоподкаста «Cranky Geeks» он дал великолепный посыл твор- ческому процессу: «Черт! Пишите по 200 слов в день в течение года, и получите роман». Следуя его совету, я писал по одной странице в день до тех пор, пока не получил книгу.
Дмитрия Попова (Dmitri Popov), написавшего статью в журнале «Free Software Magazine» под названием «Creating a book template with Writer», — именно она вдохновила меня использовать OpenOffice.org Writer (позднее переименованный в LibreOffice Writer) для набора текста. Результат получился изумительным.
Далее, спасибо добровольцам, которые помогли выпустить оригинал, свободно распространяемую версию этой книги (доступна на LinuxCommand.org):
Марка Полески (Mark Polesky), который выполнил большую редакторскую рабо- ту и проверил текст книги.
Джесси Беккер (Jesse Becker), Томаш Хщонович (Tomasz Chrzczonowicz), Майкл Левин (Michael Levin) и Спенс Майнер (Spence Miner) также проверили отдель- ные фрагменты книги и представили свои рецензии.
Ко второму изданию
27
Карен М. Шоттс (Karen M. Shotts) много часов посвятила правке моей оригиналь- ной рукописи.
Наконец, спасибо добрым людям из No Starch Press, которые серьезно потруди- лись над созданием коммерческой версии книги:
Серене Янг (Serena Yang), управляющей производством; Киту Фанчеру (Keith Fancher), моему редактору; и остальным сотрудникам No Starch Press.
Ко второму изданию
Особое спасибо добровольцам, приславшими свои ценные отзывы о свободно распространяемой версии книги и ко второму изданию, выпущенному No Starch Press: Адриану Арпидесу (Adrian Arpidez), Ху Бо (Hu Bo), Стиву Брэггу (Steve Bragg), Джону Барнсу (John Burns), Паоло Казати (Paolo Casati), Хериберто Кан- ту (Heriberto Cantú), Энцо Кардиналу (Enzo Cardinal), Ликсину Дуану (Lixin Duan), Джошуа Эскамилле (Joshua Escamilla), Брюсу Фаулеру (Bruce Fowler), Двину Харперу (Devin Harper), Йоргену Хейтманну (Jørgen Heitmann), Джона- тану Джонсу (Jonathan Jones), Сунил Джоши (Sunil Joshi), Ма Джун (Ma Jun), Эрику Каммереру (Eric Kammerer), Сету Кингу (Seth King), Крису Найту (Chris Knight), Ярославу Колосовскому (Jaroslaw Kolosowski), Джиму Ковачу (Jim Kovacs), Бартломею Майку (Bartłomiej Majka), Башару Мари (Bashar Maree), Френку Мактипсу (Frank McTipps), Майку О’Доннеллу (Mike O’Donnell), Джа- стину Пейджу (Justin Page), Парвизу Расулипуру (Parviz Rasoulipour), Уолдо Рибейро (Waldo Ribeiro), Нику Роузу (Nick Rose), Сатею Кумару Саху (Satej Kumar Sahu), Михаилу Сизову (Mikhail Sizov), Бену Слатеру (Ben Slater), Пик- лсу Спиллу (Pickles Spill), Габриэлю Штутцману (Gabriel Stutzman), Франче- ско Турко (Francesco Turco), Вольфраму Вольпи (Wolfram Volpi), Боянгу Вану (Boyang Wang), Вальтеру Вежбе (Valter Wierzba) и Кристиану Ветриху (Christian Wuethrich).
В работе над вторым изданием, выпущенным издательством No Starch Press, уча- ствовали: Серена Янг (Serena Yang), заведующая производством, и Мег Снирин- гер (Meg Sneeringer), выпускающий редактор; Крис Кливленд (Chris Cleveland), редактор; Жорди Гутьеррес Эрмосо (Jordi Gutiérrez Hermoso), научный редактор; и Ким Вимпсетт (Kim Wimpsett), редактор-корректор.
И наконец, спасибо читателям LinuxCommand.org, приславшим мне так много до- брых писем. Их поддержка помогла поверить, что я действительно чего-то стою!
Введение
Я хочу поведать вам историю. Нет, не о том, как в 1991-м Линус Торвальдс создал первую версию ядра Linux. Эту историю вы прочитаете в других книгах о Linux. Я не стану рассказывать вам, как несколькими годами ранее Ричард Столлман запустил проект GNU по созданию свободной Unix-подобной операционной си- стемы. И эту апокрифическую историю можно узнать из других книг о Linux.
Но я хочу рассказать, как можно вернуть управление своим компьютером.
В конце 1970-х, когда я, будучи студентом колледжа, только начинал заниматься компьютерами, еще продолжалась IT-революция. Появление микропроцессора сделало возможным приобретение компьютеров простыми людьми, такими как вы или я. Сегодня многим трудно представить себе мир, в котором компьютеры (огромные вычислительные машины) принадлежали только крупным компаниям и правительствам, а обычным людям они были недоступны.
Современный мир сильно изменился. Компьютеры повсюду, от крошечных наруч- ных часов до гигантских вычислительных центров, разбросанных по всему миру. Вдобавок к вездесущим компьютерам у нас появились и сети, связывающие их друг с другом, и благодаря всему этому для нас начался новый век небывалых лич- ных возможностей и творческой свободы. Но обратили ли вы внимание, что за по- следние два десятилетия стало происходить кое-что еще? Несколько гигантских корпораций захватили контроль над большинством компьютеров в мире и стали решать за вас, что можно и чего нельзя делать с ним. Множество людей по всему миру противостоят этому. Они борются за сохранение контроля над своими ком- пьютерами, создавая собственное программное обеспечение. Они строят Linux.
Сейчас принято употреблять термин «свобода» в отношении Linux, но я не ду- маю, что большинство знает, что в действительности подразумевается под свобо- дой. Свобода — это возможность самому решать, что будет делать ваш компьютер, и единственный путь к достижению этой свободы — знание того, что он делает.
О чем эта книга
29
Свобода — это компьютер без секретов, в котором все можно узнать, если только не лениться.
Зачем нужна командная строка?
Обращали ли вы внимание, что в фильмах, когда «суперхакер» — парень, спо- собный за 30 секунд взломать суперзащищенную военную систему, — садится за компьютер, он никогда не берется за мышь? Создатели фильмов инстинктивно понимают, что мы, будучи людьми, можем сделать за компьютером что-то дей- ствительно стоящее, только вводя команды с клавиатуры.
Большинство современных пользователей компьютеров знакомы только с гра- фическим интерфейсом (Graphical User Interface, GUI) и верят производителям и экспертам, что интерфейс командной строки (Command Line Interface, CLI) — это раннее средневековье. Открою тайну: интерфейс командной строки — удоб- ный и выразительный способ общения с компьютером, во многом напоминающий способ письменного общения между людьми. Как однажды было подмечено, «гра- фический пользовательский интерфейс делает простые задачи еще проще, а ин- терфейс командной строки делает сложные задачи выполнимыми», — это выска- зывание остается истинным и по сей день.
Поскольку операционная система Linux создавалась на основе семейства опера- ционных систем Unix, она унаследовала богатое разнообразие инструментов ко- мандной строки Unix. ОС Unix заняла ведущее положение в начале 1980-х (хотя появилась на десяток лет раньше), еще до повсеместного распространения графи- ческого интерфейса, и, соответственно, широко использовала интерфейс команд- ной строки. Фактически одной из основных причин, по которой первопроходцы Linux выбрали эту ОС, а не, скажем, Windows NT, была мощная поддержка интер- фейса командной строки, который «делает сложные задачи выполнимыми».
О чем эта книга
Эта книга представляет обширный обзор «жизни» в командной строке Linux. В отличие от других книг, посвященных одной программе, такой как командный интерпретатор bash, в этой книге я попытаюсь рассказать, как поладить с интер- фейсом командной строки в более широком аспекте. Как он работает? Что можно сделать с его помощью? Как лучше его использовать?
Эта книга не об администрировании системы Linux. Даже притом, что любое се- рьезное обсуждение командной строки неизменно ведет к рассмотрению тем адми- нистрирования системы, эта книга затрагивает лишь узкий круг задач, имеющих
30
Введение
отношение к администрированию. Но она готовит читателя к дополнительным исследованиям, закладывая основы знаний, необходимых для использования ко- мандной строки как основного инструмента для решения любых серьезных задач системного администрирования.
Эта книга исключительно о Linux. Многие книги пытаются расширить свою целевую аудиторию, включая в содержание другие платформы, такие как Unix и macOS. По этой причине в них рассматриваются лишь общие темы. Эта книга, напротив, посвящена исключительно современным дистрибутивам Linux. И хотя девяносто пять процентов сведений будут полезны пользователями других Unix- подобных систем, основной целевой аудиторией этой книги являются пользовате- ли командной строки современных версий Linux.
Кому адресована эта книга
Эта книга адресована новым пользователям Linux, мигрирующим с других плат- форм. Весьма вероятно, что вы — «опытный пользователь» определенной версии Microsoft Windows. Возможно, руководитель дал вам задание освоить админи- стрирование Linux-сервера или вы вступаете в новый захватывающий мир одно- платных компьютеров (Single Board Computers, SBC), таких как Raspberry Pi. А может быть, вы обычный пользователь, уставший от нескончаемых проблем безопасности и решивший попробовать Linux. Кем бы вы ни были, здесь вас ждет радушный прием.
Однако следует отметить, что в освоении Linux нет простых путей. Изучение командной строки — непростая задача, требующая определенных усилий. Не то чтобы это чересчур сложно, скорее очень многообразно. Обычная система Linux содержит тысячи программ, которые можно использовать в командной строке. Поэтому имейте в виду, что желание изучить командную строку должно быть осознанным и целенаправленным.
С другой стороны, изучение командной строки Linux чрезвычайно полезно. Если вы считаете себя «опытным пользователем», подождите немного, и вы узнаете, что такое действительно опытный пользователь. Кроме того, в отличие от других навыков работы с компьютером, умение работать в командной строке еще долго будет оставаться полезным. Навыки, приобретенные сегодня, останутся полезны- ми и через 10 лет. Командная строка выдержала испытание временем.
Если у вас нет опыта программирования — не волнуйтесь, мы поможем в его при- обретении.
Как читать эту книгу
31
Что дается в этой книге
В этой книге материал излагается в тщательно выверенной последовательности, как в школе, где учитель руководит вами и направляет вас по правильному пути. Многие авторы грешат тем, что подают материал в «систематическом» порядке, имеющем определенный смысл для писателя, но способном вызывать путаницу у начинающих пользователей.
Цель данной книги — познакомить вас с идеологией Unix, которая отличается от идеологии Windows. По пути мы иногда будем отклоняться в сторону, чтобы по- пытаться понять, почему то или иное работает именно так, а не иначе. Linux — это не просто программное обеспечение, это также часть обширной культуры Unix, имеющей свой язык и историю. Здесь я мог бы добавить еще пару напыщенных фраз. Но воздержусь от этого.
Книга делится на четыре части, каждая из которых охватывает определенный аспект владения командной строкой:
Часть I «Командная оболочка» вводит в курс основ языка командной строки: структура команд, приемы навигации в файловой системе, редактирование ко- мандной строки и поиск справочной информации с описанием команд.
Часть II «Окружение и настройка» посвящена редактированию конфигура-
ционных файлов, управляющих работой командной строки.
Часть III «Типичные задачи и основные инструменты» исследует множество типовых задач, часто выполняемых в командной строке. Unix-подобные опе- рационные системы, такие как Linux, имеют множество «классических» про- грамм командной строки, помогающих выполнять различные операции с дан- ными.
Часть IV «Сценарии командной оболочки» знакомит с программированием на языке командной оболочки, который, по общему мнению, обладает не слиш- ком широкими возможностями, но прост в изучении и позволяет автоматизи- ровать многие вычислительные задачи. Изучая программирование на языке командной оболочки, вы познакомитесь с идеями, которые сможете применять в других языках программирования.
Как читать эту книгу
Начните с начала и последовательно двигайтесь в направлении последней страницы. Это не справочник; книга действительно имеет начало, середину и конец.
32
Введение
ПОЧЕМУ Я НЕ ИСПОЛЬЗУЮ НАЗВАНИЕ «GNU/LINUX»
В некоторых кругах операционную систему Linux принято называть «операционной системой GNU/Linux». Проблема в том, что не существует абсолютно правильного названия, так как эта система создавалась множеством разных людей по всему миру. С технической точки зрения Linux — это название ядра операционной систе- мы, и ничего более. Ядро играет важную роль, конечно, потому что обеспечивает работу операционной системы, но одного его, разумеется, недостаточно.
Ричард Столлман (Richard Stallman), гениальный философ, основатель движения свободного программного обеспечения (Free Software), фонда свободных программ (Free Software Foundation) и проекта GNU, автор первой версии компилятора GNU C Compiler (GCC) и общественной лицензии GNU General Public License (GPL) и прочая и прочая, настаивает на названии «GNU/Linux» как отражающем вклад проекта GNU. Но даже при том, что проект GNU предшествовал появлению ядра Linux и вклад проекта заслуживает самой высокой оценки, использование его назва- ния в названии операционной системы можно расценивать как несправедливость в отношении всех остальных. Кроме того, я считаю, что технически более точным было бы название «Linux/GNU», потому что сначала загружается ядро, а потом все остальное выполняется на его основе.
Под общепринятым названием «Linux» подразумевается ядро и все остальное бесплатное и открытое программное обеспечение, которое можно найти в типич- ном дистрибутиве Linux, — то есть вся экосистема Linux, а не только компоненты GNU. Кроме того, на рынке операционных систем чаще отдается предпочтение названиям из одного слова, например: DOS, Windows, macOS, Solaris, Irix, AIX. Я решил использовать популярную форму. Но если вы предпочитаете название «GNU/Linux», подставляйте мысленно недостающую часть, когда будете читать эту книгу. Я не буду возражать.
Предварительные условия
Для работы с книгой вам понадобится действующая система Linux. Вы можете получить ее двумя способами:
Установить Linux на (пусть и не самый новый) компьютер. Выбор дистрибу- тива не играет большой роли, однако многие в наши дни начинают с Ubuntu, Fedora или OpenSUSE. Если не знаете, на чем остановить свой выбор, попро- буйте сначала Ubuntu. Установка современного дистрибутива Linux может быть смехотворно простой или чрезвычайно сложной, все зависит от комплек- тации вашего компьютера. Я бы рекомендовал выбрать не слишком пожилой настольный компьютер, имеющий хотя бы 2 Гбайт ОЗУ и 6 Гбайт свободного дискового пространства. Не советую использовать ноутбуки с беспроводным
Ваши отзывы важны для нас!
33
подключением к сети, если это возможно, потому что часто они сложнее в на- стройке.
Использовать Live CD. Одна из самых удобных возможностей, которой обла- дают многие дистрибутивы Linux, — загрузка и запуск системы непосредствен- но с компакт-диска или флешки, без необходимости устанавливать ее. Просто включите возможность загрузки с компакт-диска или флешки в настройках BIOS, вставьте Live CD в CD-ROM или флешку в порт USB и перезагрузитесь. Этот способ дает отличную возможность проверить совместимость компьюте- ра с Linux перед установкой. Недостаток Live CD — очень медленная работа в сравнении c установкой Linux на жесткий диск. Оба дистрибутива, Ubuntu и Fedora (среди прочих), имеют версии Live CD.
Вне зависимости от того, как вы установите Linux, чтобы выполнять примеры из книги, вам будут нужны привилегии суперпользователя (то есть администра- тора).
Получив действующую систему, начинайте знакомиться с материалами книги и выполняйте предлагаемые примеры на своем компьютере. Книга носит сугубо практический характер, поэтому устраивайтесь поудобнее, и вперед!
Что нового во втором издании
Базовая структура и содержание книги остались практически неизменными, но в этом издании «Командной строки Linux» появилось множество новых уточне- ний, пояснений и улучшений, большинство из которых основано на отзывах чита- телей. Кроме того, в книгу внесено два особенно заметных улучшения. Во-первых, теперь в книге используется версия bash 4.x, которая еще не получила широкого распространения к моменту, когда я работал над первым изданием. В этой чет- вертой версии bash появилось несколько интересных особенностей, описываемых в этом издании. Во-вторых, внесены улучшения в четвертую часть «Сценарии ко- мандной оболочки» — теперь в ней представлены более удачные примеры и улуч- шенные приемы программирования. Сценарии, включенные в четвертую часть, были пересмотрены с целью повышения их надежности и исправления некоторых ошибок ;-).
Ваши отзывы важны для нас!
Эта книга постоянно дорабатывается и обновляется, подобно многим проектам с открытым исходным кодом. Если вы обнаружите техническую ошибку, черкните мне пару строк по адресу bshotts@users.sourceforge.net.
34
Введение
Обязательно укажите издание книги, где вы нашли ошибку. Ваши исправления и предложения вполне могут попасть в будущие издания.
От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информацию о на- ших книгах.
Часть I КОМАНДНАЯ ОБОЛОЧКА
1 Что такое командная
оболочка
Говоря о командной строке, на самом деле мы имеем в виду командную оболочку (shell). Командная оболочка — это программа, которая принимает команды, введен- ные с клавиатуры, и передает их операционной системе для выполнения. Практиче- ски все дистрибутивы Linux поставляются с командной оболочкой из проекта GNU, которая называется bash. Имя bash — это аббревиатура от названия Bourne Again Shell, отражающего тот факт, что bash является улучшенной заменой sh, первона- чальной командной оболочки для Unix, написанной Стивом Борном (Steve Bourne).
Эмуляторы терминалов
При использовании графического интерфейса для взаимодействия с командной оболочкой нам понадобится еще одна программа — эмулятор терминала. Загля- нув в меню рабочего стола, вы наверняка обнаружите такую программу. В KDE используется konsole, в GNOME — gnome-terminal, однако соответствующий пункт в меню часто называется просто «Terminal» (или «Терминал»). Для Linux существует также множество других эмуляторов терминала, но все они решают одну и ту же задачу: предоставляют доступ к командной оболочке. Со временем у вас наверняка появятся свои предпочтения, в зависимости от «рюшечек и бан- тиков», которые они имеют.
Первые удары по клавишам
Итак, приступим. Запустите эмулятор терминала. После появления окна на экра- не вы увидите в нем нечто подобное:
[me@linuxbox ~]$
Первые удары по клавишам
37
НЕСКОЛЬКО СЛОВ О МЫШИ И ФОКУСЕ ВВОДА
Для работы с командной оболочкой достаточно одной клавиатуры, однако эмуля- тор терминала позволяет также использовать мышь. X Window System (механизм, который воспроизводит графический интерфейс на экране) поддерживает при- ем быстрого копирования с помощью мыши. Если выделить текст, нажав левую кнопку и переместив указатель мыши над ним (или выполнив двойной щелчок на слове), он будет скопирован в специальный буфер, которым управляет X. Нажатие средней кнопки мыши вызовет вставку текста в позицию курсора. Попробуйте проделать этот фокус.
Не пытайтесь использовать комбинации CTRL+C и CTRL+V для копирования и встав- ки в окне терминала. Эти команды там не работают. В командной оболочке эти комбинации клавиш имеют другие значения, присвоенные им задолго до появления Microsoft Windows.
Графическое окружение вашего рабочего стола (скорее всего, KDE или GNOME) работает очень похоже на графическое окружение Windows и, вероятнее всего, реализует политику «щелкни, чтобы передать фокус ввода». Это означает, что для передачи фокуса ввода в окно (его активизации) на нем нужно щелкнуть мышью. Это противоречит традиционному поведению X «фокус следует за мы- шью», когда для передачи фокуса ввода в окно достаточно просто навести на него указатель мыши. Окно не поднимется на передний план, пока вы не щелкнете на нем мышью, но способно принять фокус ввода. Настройка политики «фокус следует за мышью» упростит работу с окном терминала. Попробуйте, я думаю, вам понравится. Соответствующие параметры находятся в программе настройки вашего диспетчера окон.
Это называется приглашением к вводу (shell prompt) и появляется всякий раз, когда командная оболочка готова принять ввод. В разных дистрибутивах приглашение выглядит по-разному, но обычно включает строку имя_пользователя@имя_ком- пьютера, за которой следует имя текущего каталога (подробнее об этом чуть ниже) и знак доллара.
Если последний символ в приглашении — знак решетки (#), а не знак доллара, это означает, что сеанс в терминале обладает привилегиями суперпользователя. То есть либо вы зарегистрировались как пользователь root, либо запустили эму- лятор терминала, который автоматически устанавливает привилегии суперполь- зователя (администратора).
Будем считать, что пока все идет хорошо, и попробуем что-нибудь ввести. Набе- рите на клавиатуре какую-нибудь бессмыслицу, например:
[me@linuxbox ~]$ kaekfjaeifj
38
Глава 1. Что такое командная оболочка
Поскольку это бессмыслица, командная оболочка немедленно сообщит об этом и даст вам второй шанс:
bash: kaekfjaeifj: команда не найдена [me@linuxbox ~]$
История команд
Если теперь нажать клавишу со стрелкой вверх, после приглашения к вводу по- явится предыдущая команда kaekfjaeifj. Это называется историей команд. Боль- шинство дистрибутивов Linux по умолчанию запоминают последние 1000 команд. Нажмите клавишу со стрелкой вниз, и предыдущая команда исчезнет.
Управление курсором
Вызовите предыдущую команду, еще раз нажав клавишу со стрелкой вверх. Те- перь попробуйте понажимать клавиши со стрелками влево и вправо. Видите, как меняется позиция курсора в командной строке? Благодаря этому легко можно ре- дактировать команды.
Некоторые простые команды1
Теперь, когда вы понажимали клавиши, попробуем ввести несколько простых ко- манд. Первая команда date. Она выводит текущие время и дату:
[me@linuxbox ~]$ date Fri Feb 2 15:09:41 EST 2018
Родственная ей команда cal по умолчанию выводит календарь текущего месяца:
[me@linuxbox ~]$ cal February 2018 Su Mo Tu We Th Fr Sa 1 2 3
1 Часть вывода команд в Linux переведена на русский язык, часть — нет. В дальнейшем те системные сообщения и результаты выполнения команд, выводимые на консоль, которые в локализованной версии Linux переведены, мы будем приводить на рус- ском, остальные — на английском. В тех случаях, когда русский перевод консольного вывода важен для изложения, он будет приводиться в сносках внизу страницы. — При- меч. ред.
Завершение сеанса работы с терминалом
39
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
Чтобы увидеть объем свободного пространства на дисках, введите df:
[me@linuxbox ~]$ df Файл.система 1K-блоков Использовано Доступно Использовано% Cмонтировано в /dev/sda2 15115452 5012392 9949716 34% / /dev/sda5 59631908 26545424 30008432 47% /home /dev/sda1 147764 17370 122765 13% /boot tmpfs 256856 0 256856 0% /dev/shm
Аналогично, чтобы увидеть объем свободного пространства в памяти, введите коман ду free:
[me@linuxbox ~]$ free total used free shared buffers available Mem: 513712 503976 9736 0 5312 122916 -/+ buffers/cache: 375748 137964 Swap: 1052248 104712 947536
Завершение сеанса работы с терминалом
Завершить сеанс работы с терминалом можно, либо закрыв окно эмулятора тер- минала, либо введя команду exit:
[me@linuxbox ~]$ exit
КОНСОЛЬ ЗА КУЛИСАМИ
Даже если не запущен ни один эмулятор терминала, за ширмой графического рабочего стола продолжают выполняться несколько сеансов терминалов. Полу- чить доступ к этим сеансам, или виртуальным консолям, в большинстве дистри- бутивов Linux можно с помощью комбинаций клавиш, начиная с CTRL+ALT+F1 до CTRL+ALT+F6. После перехода к сеансу вы увидите приглашение к регистрации в системе, где нужно ввести имя пользователя и пароль. Для переключения из одной виртуальной консоли в другую используйте клавиши ALT и F1–F6. Чтобы вернуться в графическое окружение рабочего стола, в большинстве систем нужно нажать ALT+F7.
40
Глава 1. Что такое командная оболочка
Заключение
Эта глава ознаменовала начало нашего путешествия по командной строке Linux введением в командную оболочку, показав, как запустить и завершить сеанс рабо- ты с терминалом. Мы также увидели работу нескольких простых команд и узнали, как выполняются простейшие операции редактирования в командной строке. Это было совсем не страшно, правда?
В следующей главе мы познакомимся еще с несколькими командами и побродим по файловой системе Linux.
2 Навигация
Первое, что мы попробуем изучить (после пробных нажатий на клавиши), — на- вигацию в файловой системе Linux. В этой главе введем в обиход следующие команды:
pwd — выводит название текущего рабочего каталога;
cd — выполняет переход в другой каталог;
ls — выводит список содержимого каталога.
Дерево каталогов файловой системы
Так же как Windows, Unix-подобная операционная система, такая как Linux, ор- ганизует свои файлы в иерархическую структуру каталогов. То есть каталоги (в других системах их иногда называют папками) имеют древовидную органи- зацию и могут содержать файлы и другие каталоги. Первый каталог в файловой системе называется корневым каталогом. Корневой каталог содержит файлы и подкаталоги, которые в свою очередь также содержат файлы и каталоги, и т. д.
Обратите внимание, что в отличие от Windows, где для каждого устройства хране- ния создается отдельная файловая система, в Unix-подобных системах, таких как Linux, всегда имеется только одна файловая система, независимо от числа при- водов или устройств хранения, подключенных к компьютеру. Устройства хране- ния подключаются (или, как принято говорить, монтируются) к разным точкам дерева в соответствии с желанием системного администратора, человека (или не- скольких человек), ответственного за обслуживание системы.
42
Глава 2. Навигация
Текущий рабочий каталог
Многие из нас наверняка знакомы с графическими диспетчерами файлов, пред- ставляющими дерево каталогов файловой системы, как показано на рис. 2.1.
Рис. 2.1. Дерево каталогов файловой системы в диспетчере файлов с графическим интерфейсом
Обратите внимание, что обычно дерево отображается в перевернутом виде, то есть с корнем наверху и ветвями, направленными вниз.
Однако командная строка не имеет графического интерфейса, поэтому для пере- мещения по дереву файловой системы его следует представлять иначе.
Представьте файловую систему в виде лабиринта в форме перевернутого дерева и себя в середине. В любой конкретный момент времени мы можем находиться только в одном каталоге, видеть файлы в этом каталоге и перемещаться в выше- лежащий каталог (называется родительским каталогом) и в любые нижележа- щие каталоги. Каталог, в котором мы находимся, называется текущим рабочим каталогом. Название текущего рабочего каталога выводится командой pwd (print working directory — вывести рабочий каталог):
[me@linuxbox ~]$ pwd /home/me
Сразу после входа в систему (или запуска сеанса в эмуляторе терминала) текущим рабочим каталогом становится наш домашний каталог. Каждый пользователь
Смена текущего рабочего каталога
43
имеет свой домашний каталог, который является единственным, где пользовате- лю позволено осуществлять запись в файлы, когда он действует с привилегиями обычного пользователя.
Вывод содержимого каталога
Чтобы вывести список файлов и каталогов в текущем рабочем каталоге, восполь- зуйтесь командой ls:
[me@linuxbox ~]$ ls Desktop Documents Music Pictures Public Templates Videos
В действительности командой ls можно вывести содержимое любого, не только текущего, рабочего каталога, а также получить массу дополнительной любопыт- ной информации, но об этом мы поговорим в главе 3.
Смена текущего рабочего каталога
Чтобы сменить рабочий каталог (в котором мы находимся в середине древовид- ного лабиринта), можно воспользоваться командой cd: введите cd и добавьте путь к желаемому рабочему каталогу. Путь (pathname) — это маршрут, перечисляющий ветви дерева, по которым нужно пройти, чтобы достигнуть желаемого каталога. Пути могут определяться двумя способами: как абсолютные или как относитель- ные. Рассмотрим сначала абсолютные пути.
Абсолютные пути
Абсолютный путь начинается с корневого каталога и перечисляет ветви дерева, отделяющие корень от желаемого каталога или файла. Например, в системе име- ется каталог, в который устанавливается большинство программ. Путь к этому каталогу имеет вид /usr/bin. То есть в корневом каталоге (представлен первым символом «слеш» в пути) имеется каталог с названием usr, содержащий каталог с названием bin.
[me@linuxbox ~]$ cd /usr/bin [me@linuxbox bin]$ pwd /usr/bin [me@linuxbox bin]$ ls …Длинный, очень длинный список файлов…
44
Глава 2. Навигация
Как видите, мы сменили текущий рабочий каталог на /usr/bin, и он полон файлов. Обратите внимание, как изменилось приглашение командной оболочки к вводу. Для удобства оно обычно настраивается так, чтобы автоматически показывать на- звание рабочего каталога.
Относительные пути
В отличие от абсолютного пути, начинающегося в корневом каталоге и ведущего к каталогу назначения, относительный путь начинается в рабочем каталоге. Для обозначения относительных позиций в дереве файловой системы используется пара специальных символов: . (точка) и .. (точка-точка).
Символ . (точка) обозначает рабочий каталог, а символ .. (точка-точка) обозна- чает каталог, родительский по отношению к рабочему. Ниже показано, как ими пользоваться. Давайте снова сменим рабочий каталог на /usr/bin:
[me@linuxbox ~]$ cd /usr/bin [me@linuxbox bin]$ pwd /usr/bin
Отлично, а теперь допустим, что мы хотим сменить рабочий каталог на родитель- ский для каталога /usr/bin, которым является /usr. Сделать это можно двумя спо- собами: пойти по абсолютному пути:
[me@linuxbox bin]$ cd /usr [me@linuxbox usr]$ pwd /usr
или по относительному:
[me@linuxbox bin]$ cd .. [me@linuxbox usr]$ pwd /usr
Два разных способа дают идентичные результаты. И каким же лучше пользовать- ся? Конечно, тем, который требует нажимать меньше клавиш!
Аналогично, существуют два способа сменить рабочий каталог с /usr на /usr/bin. Указать абсолютный путь:
[me@linuxbox usr]$ cd /usr/bin [me@linuxbox bin]$ pwd /usr/bin
Смена текущего рабочего каталога
45
или относительный:
[me@linuxbox usr]$ cd ./bin [me@linuxbox bin]$ pwd /usr/bin
А теперь я хочу сделать важное замечание. Практически во всех случаях мож- но опустить пару символов ./, потому что они подразумеваются по умолчанию. Команда
[me@linuxbox usr]$ cd bin
даст тот же результат. Вообще если путь к чему-либо не указан явно, подразумева- ется текущий рабочий каталог.
ЧТО СЛЕДУЕТ ЗНАТЬ ОБ ИМЕНАХ ФАЙЛОВ
В системе Linux файлы именуются так же, как в других системах, таких как Windows, но есть некоторые важные отличия.
• Файлы, имена которых начинаются с точки, считаются скрытыми. Это означа- ет, что команда ls не будет выводить их, если не вызвать ее с параметром: ls -a. В момент создания учетной записи пользователя в его домашний каталог помещается несколько скрытых файлов, где хранятся различные настройки учетной записи. В главе 11 мы еще вернемся к подобным файлам, когда будем рассматривать возможные настройки своего окружения. Кроме того, некоторые приложения помещают в домашний каталог свои скрытые файлы с настройками.
• Linux, как это принято в Unix, различает регистр символов в именах файлов
и командах. Файлы с именами File1 и file1 — это разные файлы.
• Хотя Linux поддерживает длинные имена файлов с пробелами и знаками пунк- туации, старайтесь не использовать в именах файлов другие знаки пунктуации, кроме точки, дефиса и подчеркивания. И самое главное, не используйте про- белы в именах файлов. Если необходимо отделить друг от друга слова в имени файла, используйте символы подчеркивания. Потом вы не раз скажете себе спасибо за это.
• В Linux не поддерживается понятие «расширения файла», как в некоторых других операционных системах. Вы можете давать своим файлам любые имена. Тип и/или назначение файла определяется другими средствами. Но даже при том, что Unix-подобные операционные системы не используют расширения файлов для определения типа/назначения файлов, некоторые прикладные программы все же используют их для этой цели.
46
Глава 2. Навигация
Некоторые полезные сокращения
В табл. 2.1 перечислены некоторые способы быстрой смены рабочего каталога.
Таблица 2.1. Сокращенные варианты команды cd
Сокращение
Результат
cd
cd -
cd ~username
Сменить рабочий каталог на домашний
Сменить рабочий каталог на предыдущий рабочий каталог
Сменить рабочий каталог на домашний каталог пользователя username. Например, cd ~bob выполнит переход в домашний каталог пользователя bob
Заключение
Эта глава рассказала, как командная оболочка представляет структуру каталогов системе. Мы узнали об абсолютных и относительных путях и познакомились с ос- новными командами для перемещения по этой структуре. В следующей главе мы используем эти знания в процессе знакомства с современной системой Linux.
3 Исследование системы
Теперь, когда мы знаем, как перемещаться по файловой системе, совершим об- зорное путешествие по системе Linux. Но прежде чем отправиться, познакомимся еще с несколькими командами, которые пригодятся в пути:
ls — выводит список содержимого каталога;
file — определяет тип файла;
less — выводит содержимое файла.
Любопытные возможности ls
Команда ls является, пожалуй, одной из самых часто используемых команд, и не без оснований. С ее помощью можно увидеть, что находится в каталоге, и узнать некоторые важные атрибуты файлов и каталогов. Как мы уже видели, чтобы по- лучить список файлов и подкаталогов в текущем рабочем каталоге, достаточно ввести команду ls:
[me@linuxbox ~]$ ls Desktop Documents Music Pictures Public Templates Videos
Команде можно явно указать каталог, содержимое которого требуется вывести:
me@linuxbox ~]$ ls /usr bin games include lib local sbin share src
и даже несколько каталогов. Следующая команда выведет содержимое домашнего каталога пользователя (обозначен символом ~) и каталога /usr:
48
Глава 3. Исследование системы
[me@linuxbox ~]$ ls ~ /usr /home/me: Desktop Documents Music Pictures Public Templates Videos /usr: bin games include lib local sbin share src
Можно также изменить формат вывода, чтобы получить больше информации:
[me@linuxbox ~]$ ls -l total 56 drwxrwxr-x 2 me me 4096 2017-10-26 17:20 Desktop drwxrwxr-x 2 me me 4096 2017-10-26 17:20 Documents drwxrwxr-x 2 me me 4096 2017-10-26 17:20 Music drwxrwxr-x 2 me me 4096 2017-10-26 17:20 Pictures drwxrwxr-x 2 me me 4096 2017-10-26 17:20 Public drwxrwxr-x 2 me me 4096 2017-10-26 17:20 Templates drwxrwxr-x 2 me me 4096 2017-10-26 17:20 Videos
Параметр -l, добавленный в команду, требует использования «длинного» (long) формата вывода.
Параметры и аргументы
Мы подошли к очень важному моменту, касающемуся особенностей работы боль- шинства команд. Команды часто сопровождаются одним или несколькими пара- метрами, изменяющими их поведение, и дополнительными, одним или несколь- кими, аргументами, на которые воздействует команда. Поэтому большинство команд выглядят примерно так:
команда -параметры аргументы
Большинство команд используют параметры, состоящие из одного символа, кото- рому предшествует дефис, например: -l. Но многие команды, в том числе коман- ды из проекта GNU, поддерживают параметры с длинными именами, состоящие из слова, которому предшествуют два дефиса. Кроме того, многие команды по- зволяют объединять вместе параметры с короткими именами. В следующем при- мере команде ls передаются два параметра: параметр l, требующий использовать длинный (long) формат вывода, и параметр t, требующий сортировать результаты по времени (time) изменения:
[me@linuxbox ~]$ ls -lt
Добавим параметр с длинным именем –reverse, чтобы изменить порядок сорти- ровки на обратный:
[me@linuxbox ~]$ ls -lt –reverse
Любопытные возможности ls
49
ПРИМЕЧАНИЕ
Параметры команд, подобно именам файлов в Linux, чувствительны к регистру символов.
Команда ls имеет огромное число допустимых параметров. Наиболее популяр- ные из них перечислены в табл. 3.1.
Таблица 3.1. Наиболее популярные параметры команды ls
Параметр
Длинный параметр Описание
-a
-A
-d
-F
-h
-l
-r
-S
-t
–all
–almost-all
–directory
Список всех (all) файлов, даже с именами, начина- ющимися с точки, которые обычно не выводятся (то есть скрытых)
Действует подобно параметру -a, но не выводит каталоги . (текущий рабочий каталог) и .. (роди- тельский каталог)
Обычно в присутствии этого параметра команда ls выводит информацию о самом каталоге, а не его содержимое. Используйте этот параметр в сочетании с параметром -l, чтобы получить дополнительную информацию о каталоге, а не о его содержимом
–classify
Добавляет в конец каждого имени символ-индикатор (например, прямой слеш, если это имя каталога)
–human-readable
При использовании длинного формата вывода ото- бражает размеры файлов не в байтах, а в величинах с единицами измерения
Выводит результаты с использованием длинного формата
–reverse
Выводит результаты в обратном порядке. Обычно ко- манда ls выводит результаты в алфавитном порядке
Сортировать результаты по размеру (size)
Сортировать результаты по времени (time) последне- го изменения
Пристальный взгляд на длинный формат
Как было показано выше, параметр -l заставляет команду ls выводить резуль- таты с использованием длинного формата. Этот формат предусматривает вывод
50
Глава 3. Исследование системы
большого количества полезной информации. Ниже приводится пример вывода содержимого каталога Examples в системе Ubuntu:
-rw-r–r– 1 root root 3576296 2017-04-03 11:05 Experience ubuntu.ogg -rw-r–r– 1 root root 1186219 2017-04-03 11:05 kubuntu-leaflet.png -rw-r–r– 1 root root 47584 2017-04-03 11:05 logo-Edubuntu.png -rw-r–r– 1 root root 44355 2017-04-03 11:05 logo-Kubuntu.png -rw-r–r– 1 root root 34391 2017-04-03 11:05 logo-Ubuntu.png -rw-r–r– 1 root root 32059 2017-04-03 11:05 oo-cd-cover.odf -rw-r–r– 1 root root 159744 2017-04-03 11:05 oo-derivatives.doc -rw-r–r– 1 root root 27837 2017-04-03 11:05 oo-maxwell.odt -rw-r–r– 1 root root 98816 2017-04-03 11:05 oo-trig.xls -rw-r–r– 1 root root 453764 2017-04-03 11:05 oo-welcome.odt -rw-r–r– 1 root root 358374 2017-04-03 11:05 ubuntu Sax.ogg
Рассмотрим различные поля для одного из файлов и их назначение (табл. 3.2).
Таблица 3.2. Поля длинного формата вывода команды ls
Поле
Назначение
-rw-r-r–
1
root
root
32059
Права доступа к файлу. Первый символ указывает тип файла. Например, символом дефиса обозначаются обычные файлы, а символом d — каталоги. Следующие три символа сообщают о правах доступа для владельца файла, следующие три — для членов группы, которой принадлежит файл, и последние три — для всех остальных. Более полный обзор прав доступа приводится в главе 9
Число жестких ссылок на файл. Подробнее о ссылках рассказывает- ся в разделах «Символические ссылки» и «Жесткие ссылки» далее в этой главе
Имя пользователя, владеющего файлом
Имя группы, владеющей файлом
Размер файла в байтах
2017-04-03 11:05
Дата и время последнего изменения файла
oo-cd-cover.odf
Имя файла
Определение типов файлов командой file
Занимаясь исследованием системы, полезно иметь возможность определять тип содержимого файлов. В этом нам поможет команда file. Как отмечалось выше,
Просмотр содержимого файлов командой less
51
имена файлов в Linux не обязаны отражать тип содержимого файлов. Например, увидев имя файла picture.jpg, можно предположить, что он содержит изображение в формате JPEG, но в Linux такие предположения могут не оправдываться. Вы- звать команду file можно так:
file имя_файла
Команда file выводит краткое описание содержимого файла. Например:
[me@linuxbox ~]$ file picture.jpg picture.jpg: JPEG image data, JFIF standard 1.01
Существует множество разных типов файлов. Одна из известных идей в Unix- подобных системах, таких как Linux, гласит: «Все сущее есть файл». По мере чте- ния книги вы убедитесь в истинности этого утверждения.
Типы многих файлов в вашей системе будут вам знакомы, например файлы MP3 и JPEG, но иногда будут попадаться файлы с малоизвестными и даже странными типами.
Просмотр содержимого файлов командой less
Команда less — это программа для просмотра текстовых файлов. В системе Linux присутствует множество файлов, содержащих обычный удобочитаемый текст. Программа less предоставляет удобный способ исследовать их содержимое.
Зачем может понадобиться исследовать текстовые файлы? Дело в том, что многие файлы с системными настройками (их называют конфигурационными файлами) хранят информацию в этом формате, что дает возможность прочитать их и вник- нуть в особенности работы системы. Кроме того, в этом формате хранятся многие программы в системе (их называют сценариями). В последующих главах мы узна- ем, как редактировать файлы с настройками системы и как писать свои сценарии, а пока просто просматривайте их содержимое.
Команда less используется так:
less имя_файла
После запуска программа less позволяет прокручивать текстовый файл взад и вперед. Например, просмотреть содержимое файла со всеми известными систе- ме учетными записями пользователей можно с помощью следующей команды:
[me@linuxbox ~]$ less /etc/passwd
52
Глава 3. Исследование системы
ЧТО ЕСТЬ «ТЕКСТ»
Существует множество способов представления информации в компьютере. Все способы связаны с определением отношения между смысловой информацией и числами, которые применяются для ее представления. В конце концов, компью- теры могут работать только с числами, и все данные в компьютере преобразуются в числовое представление.
Некоторые из этих систем представления очень сложны (например, сжатые видео- файлы), другие, напротив, очень просты. Одна из самых ранних и простых систем называется ASCII-текст. ASCII (произносится «ас-ки») — это аббревиатура названия «American Standard Code for Information Interchange» (американский стандартный код для обмена информацией). Эта простая система кодирования впервые была использована в телетайпах.
Текст — это простое отображение «один в один» символов в числа. Это очень компактный формат. Пятьдесят символов текста преобразуются в пятьдесят байт данных. Но это не то же самое, что текст в документе, созданном текстовым процессором, таким как Microsoft Word или LibreOffice Writer. Файлы документов, в отличие от простых файлов с ASCII-текстом, содержат множество нетекстовых элементов, используемых для описания их структуры и форматирования. Файлы с простым ASCII-текстом содержат только сами символы и очень небольшое ко- личество простейших кодов управления, таких как символы табуляции, возврата каретки и перевода строки.
В системе Linux многие файлы хранятся в текстовом формате, и многие инстру- менты работают с текстовыми файлами. Даже Windows признает важность этого формата. Хорошо известная программа Notepad (Блокнот) — это редактор для простых файлов с ASCII-текстом.
После запуска программа less выведет содержимое файла. Если файл занимает больше одной страницы, его можно прокручивать вверх и вниз. Чтобы выйти из программы less, нажмите клавишу Q.
В табл. 3.3 перечислены клавиатурные команды, наиболее часто используемые в программе less.
Таблица 3.3. Команды программы less
Команда
Действие
Page Up или b
Прокрутка к началу на одну страницу
Page Down или ПРОБЕЛ Прокрутка к концу на одну страницу
Обзорное путешествие
53
Команда
Действие
СТРЕЛКА ВВЕРХ
Прокрутка к началу на одну строку
СТРЕЛКА ВНИЗ
Прокрутка к концу на одну строку
G
1G или g
/символы
n
h
q
Переход в конец текстового файла
Переход в начало текстового файла
Поиск вниз по тексту до ближайшего вхождения указанной по- следовательности символов
Поиск следующего вхождения искомой последовательности символов
Вывод экрана со справкой
Завершить less
МЕНЬШЕ ЗНАЧИТ БОЛЬШЕ
Программа less создавалась как улучшенная замена более ранней Unix-программы с именем more. Ее имя — это игра слов «less is more» (меньше значит больше) — девиз архитекторов-модернистов и проектировщиков.
less относится к категории программ постраничного просмотра текстовых до- кументов, которые называют пейджерами (pagers). В отличие от программы more, которая может листать страницы только вперед, программа less способна листать текст в обоих направлениях, вперед и назад, и имеет множество других особенностей.
Обзорное путешествие
Файловая система в Linux имеет практически ту же компоновку, что и в других Unix-подобных системах. Фактически ее структура определяется опубликован- ным стандартом с названием «Linux Filesystem Hierarchy Standard». Не все дис- трибутивы Linux следуют этому стандарту, но большинство придерживаются его достаточно близко.
А теперь немного попутешествуем по файловой системе и познакомимся с основ- ными достопримечательностями системы Linux. Это даст нам шанс попрактико- ваться в навигации. Первое, что мы обнаружим: многие интересные файлы имеют простой текстовый формат. В ходе путешествия пробуйте выполнить следующие действия:
54
Глава 3. Исследование системы
с помощью команды cd перейдите в указанный каталог;
выведите содержимое каталога командой ls -l;
если увидите заинтересовавший вас файл, определите его тип командой file;
если файл выглядит как текстовый, попробуйте просмотреть его коман-
дой less.
ВСПОМНИТЕ ТРЮК С КОПИРОВАНИЕМ И ВСТАВКОЙ!
Если вы пользуетесь мышью, выполните двойной щелчок на имени файла, чтобы скопировать его, и щелчок средней кнопкой, чтобы вставить в команду.
Если по ошибке вы попробовали просмотреть содержимое нетекстового файла и полностью нарушили нормальную работу окна терминала, вы можете вернуть его в исходное состояние, выполнив команду reset.
В ходе путешествия не бойтесь заглядывать внутрь системы. Обычные пользо- ватели практически ничего не смогут испортить. Это работа системного админи- стратора! Если команда пожалуется на что-то, просто перейдите к чему-нибудь другому. Потратьте некоторое время на знакомство с окрестностями. Это наша система, и мы вправе заниматься ее исследованием. Помните, что в Linux нет се- кретов!
В табл. 3.4 перечислены несколько каталогов для исследования. Но вы можете заняться исследованием любых других каталогов! Не бойтесь заглядывать внутрь и экспериментировать!
Таблица 3.4. Каталоги в системе Linux
Каталог
Описание
/
/bin
/boot
Корневой каталог, откуда все начинается
Содержит двоичные (binaries) файлы (программы), необходимые для за- грузки и функционирования системы
Содержит ядро Linux, образ начального RAM-диска (с драйверами, необхо- димыми на этапе загрузки) и сам загрузчик. Интересные файлы:
•
•
/boot/grub/grub.conf или menu.lst, используются для настройки загруз- чика /boot/vmlinuz (или с похожим именем), ядро Linux
Обзорное путешествие
55
Каталог
Описание
/dev
/etc
/home
Специальный каталог, содержащий узлы устройств. «Все сущее есть файл» применяется также к устройствам. Здесь ядро хранит список всех известных ему устройств
Каталог /etc содержит все системные конфигурационные файлы. Здесь же хранится коллекция сценариев командной оболочки, запускающих систем- ные службы во время загрузки. Практически все файлы в этом каталоге содержат обычный читаемый текст. В /etc все файлы интересны, но, на мой взгляд, особенный интерес пред- ставляют:
•
•
•
/etc/crontab, файл, определяющий время запуска автоматизированных заданий; /etc/fstab, таблица устройств хранения и соответствующих им точек монтирования; /etc/passwd, список всех учетных записей пользователей
В обычных конфигурациях каждому пользователю выделяется свой до- машний каталог в каталоге /home. Простые пользователи могут записы- вать что-нибудь только в файлы, находящиеся в их домашних каталогах. Это ограничение защищает систему от необдуманных действий пользова- телей
/lib
Содержит файлы разделяемых библиотек, используемых основными си- стемными программами. Они напоминают библиотеки DLL в Windows
/lost+found
Каждый раздел или устройство, отформатированные с использованием файловой системы Linux, такой как ext3, будут иметь этот каталог. Он используется на случай частичного восстановления повреждений в фай- ловой системе. Если с системой ничего страшного не происходило, этот каталог будет оставаться пустым
/media
/mnt
/opt
/proc
В современных системах Linux каталог /media будет содержать точки монтирования съемных носителей, таких как USB-диски, CD-ROM и т. д., которые монтируются в момент подключения
В старых системах Linux каталог /mnt содержал точки монтирования съем- ных носителей, монтируемых вручную
Каталог /opt используется для установки «необязательного» (optional) программного обеспечения, в основном для установки коммерческого про- граммного обеспечения
Специальный каталог. Не является фактической файловой системой, в том смысле, что файлы в этом каталоге не хранятся на жестком диске. Это виртуальная файловая система, поддерживаемая ядром Linux. Файлы в ней являются «глазками», через которые можно заглянуть в ядро. Эти файлы доступны для чтения и помогают «увидеть» компьютер глазами ядра
56
Глава 3. Исследование системы
Таблица 3.4 (окончание)
Каталог
Описание
/root
/sbin
/tmp
/usr
Домашний каталог пользователя root
Каталог содержит системные двоичные файлы (system binaries). Эти программы выполняют жизненно важные задачи и обычно запускаются только суперпользователем
Каталог /tmp играет роль временного хранилища для временных файлов, создаваемых разными программами. В некоторых конфигурациях этот каталог принудительно очищается при каждой перезагрузке системы
Дерево каталогов /usr является, пожалуй, самым объемным в системе Linux. В нем хранятся все программы и файлы поддержки, используемые обычными пользователями
/usr/bin
Каталог /usr/bin содержит выполняемые программы, установленные дис- трибутивом Linux. Очень часто в этом каталоге хранятся тысячи программ
/usr/lib
Содержит разделяемые библиотеки для программ в /usr/bin
/usr/local
/usr/sbin
/usr/share
/usr/share/ doc
/var
/var/log
Дерево каталогов /usr/local используется для установки тех программ, которые не входят в состав дистрибутива, но должны быть доступны всем пользователям в системе. Программы, собираемые из исходных текстов, обычно устанавливаются в /usr/local/bin. В новейших версиях системы Linux это дерево каталогов присутствует, но остается пустым, пока систем- ный администратор не добавит туда что-нибудь
Содержит дополнительные программы для администрирования
Каталог /usr/share содержит все разделяемые данные, используемые программами в /usr/bin, в том числе конфигурационные файлы с настрой- ками по умолчанию, ярлыки, фоновые изображения для рабочего стола, звуковые файлы и т. д
Большинство пакетов, установленных в системе, содержат документа- цию. Вся эта документация, организованная по пакетам, хранится в ката- логе /usr/share/doc
За исключением /tmp и /home, все упоминавшиеся выше каталоги остают- ся относительно статичными; то есть их содержимое почти не меняется. Дерево каталогов /var — как раз то место, где хранятся часто изменяемые данные: различные базы данных, буферные файлы, почта пользователей и пр.
Каталог /var/log содержит файлы журналов с записями о различных действиях, выполнявшихся в системе. Они очень важны и должны про- веряться время от времени. Наиболее полезными являются файлы /var/ log/messages и /var/log/syslog. Обратите внимание, что из соображений безопасности некоторые системы требуют привилегий суперпользователя для просмотра файлов журналов
Символические ссылки
57
Символические ссылки
Просматривая содержимое каталогов (например, /lib), нередко можно увидеть такие записи:
lrwxrwxrwx 1 root root 11 2018-08-11 07:34 libc.so.6 -> libc-2.6.so
Обратили внимание на первую букву l и на присутствие двух имен файлов в кон- це? Это специальный файл, который называется символической ссылкой (иногда их называют мягкими ссылками или, на жаргоне, симлинками). В большинстве Unix-подобных систем можно дать одному и тому же файлу несколько имен. Даже притом, что на данный момент ценность такого приема может быть не очевидна, в действительности это очень удобная возможность.
Вообразите следующий сценарий: программе требуется некий разделяемый ре- сурс (например, библиотека), хранящийся в файле с именем «foo», но номер вер- сии «foo» меняется очень часто. Было бы хорошо включить номер версии в имя файла, чтобы администратор или другое заинтересованное лицо могли видеть, какая версия «foo» установлена. И здесь возникает проблема. Если изменить имя разделяемого ресурса, нам придется проверять каждую программу, использую- щую этот ресурс, и изменять в ней имя ресурса на новое после установки новой версии ресурса. Если честно, такая перспектива не выглядит привлекательной.
Символические ссылки помогут спасти положение. Допустим, мы установили «foo» версии 2.6 с именем файла «foo-2.6» и затем создали символическую ссыл- ку с простым именем «foo», указывающую на ресурс «foo-2.6». То есть когда про- грамма откроет файл «foo», в действительности она откроет файл «foo-2.6». И все будут счастливы. Программы, полагающиеся на имя «foo», найдут нужный файл, а мы сможем увидеть фактическую версию ресурса. Когда придет время обновить ресурс до версии «foo-2.7», мы просто добавим файл в систему, удалим символи- ческую ссылку «foo» и создадим новую символическую ссылку, указывающую на новую версию. Такой подход не только решает проблему обновления версий, но также позволяет сохранить на компьютере обе версии ресурса. Представьте, что в версии «foo-2.7» обнаружилась ошибка (ох уж эти разработчики!) и нужно вер- нуть старую версию. В этом случае достаточно просто вновь удалить символиче- скую ссылку, указывающую на новую версию, и создать новую символическую ссылку, указывающую на старую версию.
Запись выше (получена в каталоге /lib в системе Fedora) соответствует символи- ческой ссылке с именем libc.so.6, указывающей на файл разделяемой библиоте- ки с именем libc-2.6.so. Это означает, что программа, ищущая libc.so.6, фактически получит файл libc-2.6.so. Как создавать символические ссылки, мы узнаем в сле- дующей главе.
58
Глава 3. Исследование системы
Жесткие ссылки
Пока мы не ушли далеко от темы ссылок, нужно упомянуть, что существует вто- рой тип ссылок, которые называют жесткими ссылками (hard link). Жесткие ссыл- ки также позволяют присвоить одному файлу несколько имен, но они действуют иначе. Подробнее о различиях между жесткими и символическими ссылками рас- сказывается в следующей главе.
Заключение
Совершив обзорное путешествие, мы узнали много нового о нашей системе. Мы увидели разные файлы и каталоги и их содержимое. Один из важнейших уроков, которые вы должны вынести, — это насколько открыта система. В Linux содер- жится много важных файлов, содержащих обычный легкочитаемый текст. В отли- чие от многих коммерческих систем, Linux полностью открыта для исследования и изучения.
4 Операции с файлами
и каталогами
Теперь мы готовы приступить к настоящей работе! В этой главе будут представ- лены пять наиболее часто используемых команд Linux. Следующие команды ис- пользуются для работы и с файлами, и с каталогами:
cp — копирует файлы и каталоги;
mv — перемещает/переименовывает файлы и каталоги;
mkdir — создает каталоги;
rm — удаляет файлы и каталоги;
ln — создает жесткие и символические ссылки.
Справедливости ради следует заметить, что некоторые задачи, выполняемые эти- ми командами, гораздо проще решаются с помощью графического диспетчера файлов. В диспетчере файлов можно мышью перетаскивать файлы из одного ка- талога в другой, вырезать и вставлять файлы, удалять файлы и т. д. Но зачем тогда использовать эти старые программы командной строки?
Ответ прост: потому что они обладают мощностью и гибкостью. Несмотря на то что простые операции с файлами легко выполняются в диспетчере файлов с гра- фическим интерфейсом, сложные задачи проще решать с помощью программ ко- мандной строки. Например, как скопировать файлы HTML из одного каталога в другой, причем только те, что отсутствуют в каталоге назначения или имеют бо- лее позднюю дату последнего изменения? Сделать это в диспетчере файлов очень сложно, но легко в командной строке:
cp -u *.html destination
60
Глава 4. Операции с файлами и каталогами
Групповые символы
Прежде чем приступать к использованию обсуждаемых команд, необходимо сна- чала поговорить об одной особенности командной оболочки, которая делает эти команды такими мощными. Так как имена файлов используются в командной обо- лочке повсеместно, она поддерживает специальные символы, помогающие быстро определять группы имен файлов. Эти специальные символы называют групповы- ми символами (wildcards). Групповые символы (также известны как символы под- становки (globbing)) позволяют выбирать имена файлов по шаблону. В табл. 4.1 перечислены групповые символы и их соответствия.
Таблица 4.1. Групповые символы
Групповой символ
Соответствует
?
[символы]
[!символы]
Любая последовательность любых символов
Любой один символ
Любой один символ из указанного множества символов
Любой один символ, не принадлежащий указанному множеству символов
[[:класс:]]
Любой один символ, принадлежащий указанному классу
В табл. 4.2 перечислены наиболее часто используемые классы символов.
Таблица 4.2. Наиболее часто используемые классы символов
Класс символов
Соответствует
[:alnum:]
[:alpha:]
[:digit:]
[:lower:]
[:upper:]
Любой алфавитно-цифровой символ
Любой алфавитный символ
Любой цифровой символ
Любая буква в нижнем регистре
Любая буква в верхнем регистре
Групповые символы позволяют конструировать сложные критерии выбора имен файлов. В табл. 4.3 перечислены некоторые примеры шаблонов и их соответ- ствия.
Групповые символы
61
ГРУППОВЫЕ СИМВОЛЫ ТАКЖЕ ДЕЙСТВУЮТ В ГРАФИЧЕСКОМ ИНТЕРФЕЙСЕ
Групповые символы имеют особую ценность не только потому, что часто ис- пользуются в командной строке, но и потому, что поддерживаются некоторыми диспетчерами с графическим интерфейсом.
• В Nautilus (диспетчер файлов для GNOME) можно выбирать файлы с помощью диалога Edit (Правка) ► Select Pattern (Выделить по шаблону). Просто введите шаблон для выбора файлов с групповыми символами, и в текущем каталоге будут выделены файлы, соответствующие шаблону.
• В некоторых версиях Dolphin и Konqueror (диспетчеры файлов для KDE) груп- повые символы можно вводить непосредственно в адресную строку. Например, если понадобится увидеть все файлы с именами, начинающимися с буквы «u» в нижнем регистре, в каталоге /usr/bin, просто введите в адресной строке текст: /usr/bin/u*, и вы получите желаемый результат.
Большинство идей, первоначально реализованных в интерфейсе командной строки, перекочевали и в графический интерфейс. Это одно из множества обстоятельств, которые делают настольный компьютер с Linux таким мощным инструментом.
Таблица 4.3. Примеры использования групповых символов
Шаблон
Соответствует
g*
b*.txt
Data???
[abc]*
Все имена файлов
Все имена файлов, начинающиеся с символа «g»
Все имена файлов, начинающиеся с символа «b», за кото- рым следует любое число других символов, и заканчиваю- щиеся на «.txt»
Все имена файлов, начинающиеся с символов «Data», за которыми следуют ровно три любых символа
Все имена файлов, начинающиеся с символа «a», «b» или «c»
BACKUP.[0-9][0-9][0-9]
Все имена файлов, начинающиеся с символов «BACKUP.», за которыми следуют ровно три цифровых символа
[[:upper:]]*
Все имена файлов, начинающиеся с буквы в верхнем регистре
62
Глава 4. Операции с файлами и каталогами
Таблица 4.3 (Окончание)
Шаблон
Соответствует
[![:digit:]]*
Все имена файлов, не начинающиеся с цифры
*[[:lower:]123]
Все имена файлов, заканчивающиеся буквой в нижнем регистре или цифрой «1», «2» или «3»
Групповые символы можно использовать с любыми командами, принимающими имена файлов в виде аргументов, но подробнее об этом мы поговорим в главе 7.
ДИАПАЗОНЫ СИМВОЛОВ
Если у вас уже есть опыт работы с другим Unix-подобным окружением или вам приходилось читать другие книги по этой теме, вы встречали форму записи диапа- зонов символов [A-Z] или [a-z]. Это традиционные для Unix формы записи, и они прекрасно работают в старых версиях Linux. Более того, они все еще работают в новых версиях, но будьте очень осторожны при их использовании, потому что они не всегда дают ожидаемый результат без правильной настройки. А вообще, старайтесь избегать их и использовать классы символов.
mkdir — создание каталогов
Команда mkdir используется для создания каталогов. Вызывается она следующим образом:
mkdir каталог…
Примечание к форме записи: в этой книге три точки в описании команды, следу- ющие за аргументом (как в примере выше), говорят о том, что аргументов может быть несколько; то есть в данном случае команда
mkdir dir1
создаст один каталог с именем dir1, а команда
mkdir dir1 dir2 dir3
создаст три каталога с именами dir1, dir2 и dir3.
cp — копирование файлов и каталогов
63
cp — копирование файлов и каталогов
Команда cp копирует файлы и каталоги. Ее можно использовать двумя разными способами:
cp item1 item2
чтобы скопировать один файл или каталог item1 в файл или каталог item2, и
cp элемент… каталог
чтобы скопировать несколько элементов (файлов или каталогов) в указанный ка- талог.
Параметры команды cp и примеры ее использования
В табл. 4.4 перечислены некоторые параметры (короткие и эквивалентные им длинные), наиболее часто используемые с командой cp.
Таблица 4.4. Параметры команды cp
Параметр
Значение
-a, –archive
-i, –interactive
-r, –recursive
-u, –update
Скопировать файлы и каталоги со всеми атрибутами, вклю- чая идентификаторы владельцев и права доступа. Без этого параметра копии обычно получают значения атрибутов по умолчанию, определенных для пользователя, выполняющего копирование. Подробнее об атрибутах файлов рассказывает- ся в главе 9
Запрашивать у пользователя подтверждение перед переза- писью существующего файла. Если этот параметр отсут- ствует, команда cp просто перезапишет существую- щие файлы (без предупреждения)
Рекурсивно копировать каталоги и их содержимое. Это обязательный параметр (или параметр -a) при копировании каталогов
При копировании файлов из одного каталога в другой копи- ровать только файлы, отсутствующие в каталоге назначения или более новые. Этот параметр удобно использовать при копировании большого числа файлов, так как он позволяет пропустить файлы, которые не требуется копировать
-v, –verbose
Выводить информационные сообщения в процессе копиро- вания
64
Глава 4. Операции с файлами и каталогами
В табл. 4.5 приводится несколько примеров использования этих параметров.
Таблица 4.5. Примеры использования команды cp
Команда
Результат
cp file1 file2
cp -i file1 file2
Скопирует file1 в file2. Если file2 существует, он будет затерт новым файлом file1. Если file2 отсутствует, он будет создан
То же, что и выше, но если файл file2 существует, у пользо- вателя будет запрошено подтверждение перед перезаписью файла
cp file1 file2 dir1
Скопирует file1 и file2 в каталог dir1. Каталог dir1 должен существовать
cp dir1/* dir2
cp -r dir1 dir2
С использованием группового символа. Скопирует все файлы из каталога dir1 в каталог dir2. Каталог dir2 должен существо- вать
Скопирует содержимое каталога dir1 в каталог dir2. Если каталог dir2 не существует, он будет создан и заполнен содержимым каталога dir1. Если каталог dir2 существует, тогда в него будет скопирован сам каталог dir1 (вместе с его содержимым)
mv — перемещение и переименование файлов
Команда mv выполняет операции перемещения и переименования файлов в зави- симости от особенностей использования. В любом случае исходный файл исчеза- ет после операции. Команда mv используется почти так же, как команда cp:
mv item1 item2
перемещает или переименовывает файл или каталог item1 в item2.
mv элемент… каталог
перемещает один или более элементов из одного каталога в другой.
Параметры команды mv и примеры ее использования
Команда mv поддерживает множество тех же параметров, что и команда cp, как показано в табл. 4.6.
rm — удаление файлов и каталогов
65
Таблица 4.6. Параметры команды mv
Параметр
Значение
-i, –interactive
Запрашивать у пользователя подтверждение перед перезапи- сью существующего файла. Если этот параметр отсутствует, коман да mv просто перезапишет существующие файлы
-u, –update
При перемещении файлов из одного каталога в другой переме- щать только файлы, отсутствующие в каталоге назначения или более новые
-v, –verbose
Выводить информационные сообщения в процессе перемещения
В табл. 4.7 приводится несколько примеров использования команды mv.
Таблица 4.7. Примеры использования команды mv
Команда
Результат
mv file1 file2
Переместит file1 в file2. Если file2 существует, он будет заме- нен на новый файл file1. Если file2 отсутствует, он будет создан. В любом случае файл file1 исчезнет
mv -i file1 file2
То же, что и выше, но если файл file2 существует, у пользователя будет запрошено подтверждение перед перезаписью файла
mv file1 file2 dir1
Переместит file1 и file2 в каталог dir1. Каталог dir1 должен суще- ствовать
mv dir1 dir2
Если каталог dir2 отсутствует, команда mv создаст его и переместит содержимое каталога dir1 в каталог dir2, после чего удалит dir1. Если каталог dir2 существует, команда mv переместит каталог dir1 (вместе с его содержимым) в каталог dir2
rm — удаление файлов и каталогов
Команда rm используется для удаления (remove) файлов и каталогов, например:
rm элемент…
где элемент — это один или несколько файлов или каталогов.
Параметры команды rm и примеры ее использования
В табл. 4.8 перечислены некоторые параметры, наиболее часто используемые с коман дой rm.
66
Глава 4. Операции с файлами и каталогами
Таблица 4.8. Параметры команды rm
Параметр
Значение
-i, –interactive
Запрашивать у пользователя подтверждение перед удалением существующего файла. Если этот параметр отсутствует, команда rm просто удалит существующие файлы
-r, –recursive
-f, –force
Рекурсивно удалить каталоги. То есть вместе с каталогом будут удалены все его подкаталоги. Это обязательный параметр при удалении каталогов
Игнорировать отсутствующие файлы и не запрашивать под- тверждения. Этот параметр отменяет действие параметра –interactive
-v, –verbose
Выводить информационные сообщения в процессе удаления
БУДЬТЕ ОСТОРОЖНЫ С КОМАНДОЙ RM!
Unix-подобные операционные системы, такие как Linux, не имеют команды, от- меняющей удаление. Если вы что-то удалили командой rm, это исчезнет навсегда. Linux считает вас достаточно ответственным человеком, отдающим себе отчет в своих действиях.
Будьте особенно осторожны с групповыми символами. Рассмотрим следующий классический пример. Допустим, вы захотели удалить все файлы HTML в каталоге. Для этого вы вводите команду:
rm *.html
которая сделает именно то, что вам нужно, но если вы случайно вставите пробел между * и .html, как в следующей команде:
rm * .html
rm удалит все файлы в каталоге и затем сообщит, что не нашла файла .html.
Полезный совет: всякий раз, используя групповые символы с командой rm (по- мимо внимательной проверки ввода!), проверьте сначала аргумент с групповым символом с командой ls. Это позволит увидеть, какие файлы будут удалены. За- тем нажмите клавишу со стрелкой вверх, чтобы восстановить команду из истории, и замените ls на rm.
В табл. 4.9 приводится несколько примеров использования команды rm.
ln — создание ссылок
67
Таблица 4.9. Примеры использования команды rm
Команда
rm file1
Результат
Просто удалит файл file1
rm -i file1
Перед удалением file1 запросит подтверждение у пользователя
rm -r file1 dir1
Удалит файл file1 и каталог dir1 со всем его содержимым
rm -rf file1 dir1
То же, что и выше, но в отсутствие file1 и/или dir1 просто продол- жит работу, не выводя никаких сообщений
ln — создание ссылок
Команда ln применяется для создания жесткой или символической ссылки. Ее можно использовать одним из двух способов:
ln файл ссылка
создает жесткую ссылку.
ln -s элемент ссылка
создает символическую ссылку, где элементом может быть файл или каталог.
Жесткие ссылки
Жесткие ссылки — это первоначальный способ создания ссылок в Unix; символи- ческие ссылки — более позднее изобретение. По умолчанию каждый файл имеет одну жесткую ссылку, определяющую его имя. Создавая жесткую ссылку, мы соз- даем дополнительную запись в каталоге для файла. Жесткие ссылки имеют два важных ограничения.
Жесткая ссылка не может указывать на файл за пределами собственной фай- ловой системы. Это означает, что ссылка не может указывать на файл, находя- щийся в другом разделе диска.
Жесткая ссылка не может указывать на каталог.
Жесткая ссылка неотличима от самого файла. В отличие от символических ссы- лок, при выводе списка с содержимым каталогов жесткие ссылки никак не вы- деляются. При удалении жесткой ссылки удаляется только сама ссылка, а файл остается на месте (то есть пространство, занимаемое файлом, не освобождается), пока не будут удалены все жесткие ссылки на файл.
68
Глава 4. Операции с файлами и каталогами
Знать о существовании жестких ссылок важно, потому что они будут встречаться вам время от времени, но в современной практике предпочтение отдается симво- лическим ссылкам, о которых рассказывается далее.
Символические ссылки
Символические ссылки были придуманы с целью преодолеть ограничения жест- ких ссылок. Когда создается символическая ссылка, в действительности создается файл особого типа, содержащий текстовый указатель на файл или каталог. В не- котором отношении они действуют подобно ярлыкам в Windows, но, конечно же, появились задолго до ярлыков Windows.
Файл, на который указывает символическая ссылка, и сама символическая ссыл- ка почти неотличимы друг от друга. Например, если попытаться что-то записать в символическую ссылку, запись будет выполнена в файл, на который она указы- вает. Однако при удалении символической ссылки удаляется только символиче- ская ссылка, но не файл. Если удалить файл до того, как будет удалена символи- ческая ссылка, ссылка останется на месте, но будет указывать в никуда. О таких ссылках говорят, что они «битые». Во многих реализациях команда ls выделяет битые ссылки цветом, например красным, чтобы обратить на них внимание.
Идея ссылок может показаться странной и непонятной, но оставьте ее пока. Мы опробуем их на практике, и многое, возможно, для вас прояснится.
Постройка песочницы
Поскольку мы собираемся на практике опробовать некоторые операции с файла- ми, давайте выделим безопасный уголок для «игр» с командами управления фай- лами. Прежде всего нам понадобится каталог, в котором мы будем практиковать- ся. Создайте такой каталог в своем домашнем каталоге и назовите его playground.
Создание каталогов
Для создания каталогов используется команда mkdir. Чтобы создать каталог playground, проверьте сначала, находитесь ли вы в домашнем каталоге, и только потом создайте новый каталог:
[me@linuxbox ~]$ cd [me@linuxbox ~]$ mkdir playground
Постройка песочницы
69
Чтобы немножко украсить вашу песочницу, создайте внутри playground пару каталогов с именами dir1 и dir2. Для этого смените текущий рабочий каталог на playground и выполните еще одну команду mkdir:
[me@linuxbox ~]$ cd playground [me@linuxbox playground]$ mkdir dir1 dir2
Обратите внимание, что команда mkdir может принимать несколько аргументов, это позволяет создать два каталога одной командой.
Копирование файлов
Далее, добавим немного данных в нашу песочницу. Для этого скопируем какие- нибудь файлы. Командой cp скопируйте файл passwd из каталога /etc в текущий рабочий каталог.
[me@linuxbox playground]$ cp /etc/passwd .
Обратите внимание на сокращение, обозначающее текущий рабочий каталог, — точку в конце команды. Если после этого выполнить команду ls, мы увидим наш файл:
[me@linuxbox playground]$ ls -l итого 12 drwxrwxr-x 2 me me 4096 2018-01-10 16:40 dir1 drwxrwxr-x 2 me me 4096 2018-01-10 16:40 dir2 -rw-r–r– 1 me me 1650 2018-01-10 16:07 passwd
Теперь ради развлечения повторите операцию копирования, но на этот раз с пара- метром -v, чтобы посмотреть, как она работает:
[me@linuxbox playground]$ cp -v /etc/passwd .
/etc/passwd' -> ./passwd'
Команда cp вновь скопировала файл, но на этот раз вывела короткое сообщение, указывающее, что операция была выполнена. Обратите внимание, что cp переза- писала первую копию без каких-либо предупреждений. Это как раз тот случай, когда cp полагает, что вы знаете, что делаете. Чтобы вывести предупреждение, включите параметр -i:
[me@linuxbox playground]$ cp -i /etc/passwd . cp: переписать `./passwd'?
70
Глава 4. Операции с файлами и каталогами
Если в ответ на запрос ввести y, команда перезапишет существующий файл; если ввести любой другой символ (например, n), cp оставит прежнюю копию файла нетронутой.
Перемещение и переименование файлов
Имя passwd не выглядит органичным в нашей песочнице, поэтому дадим этому файлу какое-нибудь другое имя:
[me@linuxbox playground]$ mv passwd fun
Теперь немножко позабавимся и переместим переименованный файл в каждый из каталогов и обратно:
[me@linuxbox playground]$ mv fun dir1
переместит файл в каталог dir1. Следующая команда
[me@linuxbox playground]$ mv dir1/fun dir2
переместит файл из каталога dir1 в каталог dir2. Следующая команда
[me@linuxbox playground]$ mv dir2/fun .
вернет его в текущий рабочий каталог. Теперь посмотрим, как mv влияет на катало- ги. Сначала переместите файл в каталог dir1:
[me@linuxbox playground]$ mv fun dir1
Затем переместите dir1 в dir2 и проверьте их содержимое командой ls:
[me@linuxbox playground]$ mv dir1 dir2 [me@linuxbox playground]$ ls -l dir2 итого 4 drwxrwxr-x 2 me me 4096 2018-01-11 06:06 dir1 [me@linuxbox playground]$ ls -l dir2/dir1 итого 4 -rw-r–r– 1 me me 1650 2018-01-10 16:33 fun
Обратите внимание: так как dir2 уже существует, mv переместит dir1 в dir2. Если бы каталога dir2 не было, mv просто переименовала бы dir1 в dir2. В заключение верните все на свои места:
[me@linuxbox playground]$ mv dir2/dir1 . [me@linuxbox playground]$ mv dir1/fun .
Постройка песочницы
71
Создание жестких ссылок
Теперь попробуем поиграть со ссылками. Сначала займемся жесткими ссылками: создайте несколько жестких ссылок для нашего файла:
[me@linuxbox playground]$ ln fun fun-hard [me@linuxbox playground]$ ln fun dir1/fun-hard [me@linuxbox playground]$ ln fun dir2/fun-hard
Теперь у нас есть четыре экземпляра файла fun. Посмотрим, что содержит наш каталог playground:
[me@linuxbox playground]$ ls -l итого 16 drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir1 drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir2 -rw-r–r– 4 me me 1650 2018-01-10 16:33 fun -rw-r–r– 4 me me 1650 2018-01-10 16:33 fun-hard
Прежде всего следует обратить внимание на второе поле в записях, соответствую- щих файлам fun и fun-hard. Оба содержат 4 — число жестких ссылок на файл, су- ществующих в данный момент. Как вы помните, файл всегда имеет хотя бы одну жесткую ссылку, потому что имя файла определяется ссылкой. Но как убедиться, что fun и fun-hard — это один и тот же файл? В этом случае команда ls нам не помощник. Вы, конечно, скажете, что fun и fun-hard имеют одинаковые размеры (поле 5), но по списку файлов нельзя уверенно утверждать, что это один и тот же файл. Чтобы решить эту задачу, заглянем поглубже.
Рассуждая о жестких ссылках, полезно представлять файлы состоящими из двух частей:
раздела с данными, где хранится содержимое файла;
раздела с именем, где хранится имя файла.
Создавая жесткую ссылку, мы фактически создаем дополнительный раздел с име- нем, ссылающийся на тот же раздел с данными. Цепочку дисковых блоков система присваивает тому, что называется индексным узлом (inode), который затем присва- ивается разделу с именем. То есть каждая жесткая ссылка ссылается на определен- ный индексный узел с содержимым файла.
Команда ls может извлекать эту информацию. Для этого ее нужно вызвать с па- раметром -i:
[me@linuxbox playground]$ ls -li итого 16 12353539 drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir1
72
Глава 4. Операции с файлами и каталогами
12353540 drwxrwxr-x 2 me me 4096 2018-01-14 16:17 dir2 12353538 -rw-r–r– 4 me me 1650 2018-01-10 16:33 fun 12353538 -rw-r–r– 4 me me 1650 2018-01-10 16:33 fun-hard
В этой версии списка в первом поле отображается номер индексного узла, и, как можно видеть, оба имени, fun и fun-hard, ссылаются на индексные узлы с одним и тем же номером, а это подтверждает, что они соответствуют одному и тому же файлу.
Создание символических ссылок
Символические ссылки были придуманы с целью преодолеть ограничения жест- ких ссылок:
жесткие ссылки не могут указывать на файлы, находящиеся на других физиче-
ских устройствах;
жесткие ссылки не могут указывать на каталоги — только на файлы.
Символическая ссылка — это файл особого типа, хранящий текстовый указатель на файл или каталог.
Создаются символические ссылки почти так же, как жесткие ссылки:
[me@linuxbox playground]$ ln -s fun fun-sym [me@linuxbox playground]$ ln -s ../fun dir1/fun-sym [me@linuxbox playground]$ ln -s ../fun dir2/fun-sym
Первый пример достаточно очевиден: мы просто добавили параметр -s, чтобы вместо жесткой ссылки создать символическую ссылку. Но два других выглядят несколько необычно. Не забывайте, что, создавая символическую ссылку, мы фак- тически определяем текст, описывающий местоположение целевого файла отно- сительно символической ссылки. В этом легко убедиться, если взглянуть на вы- вод команды ls:
[me@linuxbox playground]$ ls -l dir1 итого 4 -rw-r–r– 4 me me 1650 2018-01-10 16:33 fun-hard lrwxrwxrwx 1 me me 6 2018-01-15 15:17 fun-sym -> ../fun
Запись с информацией о fun-sym в dir1 сообщает, что это символическая ссыл- ка (первый символ l в первом поле), указывающая на ../fun, что правильно. От- носительно символической ссылки fun-sym файл fun находится в каталоге уров- нем выше. Обратите также внимание на размер файла символической ссылки,
Постройка песочницы
73
равный 6, — это число символов в строке ../fun, а не размер файла, на который она указывает.
При создании символических ссылок можно также указывать абсолютные пути, например:
[me@linuxbox playground]$ ln -s /home/me/playground/fun dir1/fun-sym
или относительные, как в более раннем примере. Но предпочтительнее использо- вать относительные пути, потому что это позволяет переименовывать и/или пере- мещать каталоги, содержащие символические ссылки, не разрушая их.
Помимо обычных файлов, символические ссылки могут указывать также на ка- талоги:
[me@linuxbox playground]$ ln -s dir1 dir1-sym [me@linuxbox playground]$ ls -l итого 16 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1 lrwxrwxrwx 1 me me 4 2018-01-16 14:45 dir1-sym -> dir1 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2 -rw-r–r– 4 me me 1650 2018-01-10 16:33 fun -rw-r–r– 4 me me 1650 2018-01-10 16:33 fun-hard lrwxrwxrwx 1 me me 3 2018-01-15 15:15 fun-sym -> fun
Удаление файлов и каталогов
Как уже говорилось ранее, удаление файлов и каталогов выполняется при помо- щи команды rm. Далее мы немного почистим нашу песочницу. Сначала удалите одну из жестких ссылок:
[me@linuxbox playground]$ rm fun-hard [me@linuxbox playground]$ ls -l итого 12 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1 lrwxrwxrwx 1 me me 4 2018-01-16 14:45 dir1-sym -> dir1 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2 -rw-r–r– 3 me me 1650 2018-01-10 16:33 fun lrwxrwxrwx 1 me me 3 2018-01-15 15:15 fun-sym -> fun
Результат получился вполне ожидаемым. Файл fun-hard исчез, и счетчик ссылок во втором поле в записи для файла fun уменьшился с четырех до трех. Далее, уда- лите файл fun и ради развлечения добавьте в команду параметр -i, чтобы посмо- треть, что происходит:
74
Глава 4. Операции с файлами и каталогами
[me@linuxbox playground]$ rm -i fun rm: удалить обычный файл `fun'?
Введите y в ответ на запрос, и файл будет удален. Но давайте посмотрим на вы- вод ls. Заметили, что произошло с fun-sym? Поскольку теперь символическая ссылка указывает на несуществующий файл, она стала битой:
[me@linuxbox playground]$ ls -l итого 8 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1 lrwxrwxrwx 1 me me 4 2018-01-16 14:45 dir1-sym -> dir1 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2 lrwxrwxrwx 1 me me 3 2018-01-15 15:15 fun-sym -> fun
В большинстве дистрибутивов Linux команда ls особым образом настраивается на отображение битых ссылок. Битые ссылки не представляют никакой опасно- сти, но вносят определенную путаницу. При попытке использовать битую ссылку вы увидите:
[me@linuxbox playground]$ less fun-sym fun-sym: Нет такого файла или каталога
Давайте немного приберем за собой. Удалите символическую ссылку:
[me@linuxbox playground]$ rm fun-sym dir1-sym [me@linuxbox playground]$ ls -l итого 8 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir1 drwxrwxr-x 2 me me 4096 2018-01-15 15:17 dir2
Главное, что следует помнить о символических ссылках: большинство операций с файлами воздействуют на целевой элемент, а не на саму ссылку. Однако команда rm является исключением из этого правила. Когда вы удаляете ссылку, удаляется сама ссылка, а не элемент, на который она указывает.
В заключение удалим каталог playground. Для этого вернитесь в домашний ката- лог и вызовите команду rm с параметром рекурсивного удаления каталогов (-r), чтобы удалить каталог playground и все его содержимое, включая подкаталоги:
[me@linuxbox playground]$ cd [me@linuxbox ~]$ rm -r playground
Заключение
75
СОЗДАНИЕ СИМВОЛИЧЕСКИХ ССЫЛОК С ПОМОЩЬЮ ГРАФИЧЕСКОГО ИНТЕРФЕЙСА
Диспетчеры файлов в GNOME и KDE предоставляют простой автоматизированный способ создания символических ссылок. Если в GNOME во время перетаскивания файла мышью удерживать нажатыми клавиши CTRL и SHIFT, вместо копирования (или перемещения) файлов будет выполнена операция создания ссылки. В KDE, когда перетаскиваемый файл сбрасывается в целевой каталог, появляется не- большое меню, предлагающее выбор из трех операций: скопировать, переместить или создать ссылку.
Заключение
Мы узнали много нового, но чтобы информация усвоилась, требуется время. Вы- полняйте упражнения в песочнице раз за разом, пока не почувствуете, что пони- маете их смысл. На данном этапе очень важно надежно усвоить, как работают ос- новные команды управления файлами и групповые символы. Не бойтесь выйти за рамки предложенных упражнений — добавьте дополнительные файлы и каталоги, поэкспериментируйте с групповыми символами для определения групп файлов в разных операциях. Идея ссылок на первый взгляд может показаться малопо- нятной, поэтому уделите время их исследованию. Зачастую они оказываются на- стоящим спасательным кругом.
5 Работа с командами
До настоящего момента мы видели группы мистических команд, каждая из кото- рых имеет свои таинственные параметры и аргументы. Теперь мы удалим часть этой таинственности и даже создадим несколько собственных команд. В этой гла- ве будут представлены следующие команды:
type — сообщает, как интерпретируется имя указанной команды;
which — сообщает, какая программа будет выполнена;
help — выводит справку по встроенным командам оболочки;
man — выводит страницу справочного руководства с описанием команды;
apropos — выводит список подходящих команд;
info — выводит запись из справочного руководства Info с описанием команды;
whatis — выводит краткое описание команды;
alias — создает псевдоним для команды.
Что такое команды?
Команда может быть:
Выполняемой программой, как те файлы, что мы видели в каталоге /usr/bin. К этой категории относятся: скомпилированные двоичные программы, напри- мер, написанные на C и C++; программы, написанные на языках сценариев, та- ких как shell, Perl, Python, Ruby и др.
Встроенной командой, реализованной внутри самой командной оболочки. Командная оболочка bash поддерживает множество внутренних команд, кото-
Идентификация команд
77
рые так и называют — встроенными (shell builtins). Команда cd, например, — это встроенная команда.
Функцией командной оболочки. Функции командной оболочки (shell functions) — это миниатюрные сценарии на языке командной оболочки, встро- енные в окружение. Мы еще вернемся к вопросам настройки окружения и соз- дания функций командной оболочки в последующих главах, а пока просто помните об их существовании.
Псевдонимом. Псевдоним (alias) — это команда, которую мы можем опреде-
лить сами, сконструировав из других команд.
Идентификация команд
Часто бывает полезно точно знать, какому из четырех типов принадлежит коман- да, и Linux предлагает пару способов узнать это.
type — получение типа команды
Команда type — это встроенная команда, которая сообщает тип указанной ей команды. Вызывается она следующим образом:
type команда
где команда — это имя исследуемой команды. Например:
[me@linuxbox ~]$ type type type встроена в оболочку [me@linuxbox ~]$ type ls ls является алиасом для `ls –color=tty' [me@linuxbox ~]$ type cp cp хэширована (/bin/cp)
Здесь мы видим результаты определения типов трех разных команд. Обратите внимание, что команда ls (в дистрибутиве Fedora) фактически является псевдо- нимом (alias) команды ls с параметром –color=tty. Теперь-то мы знаем, почему результаты команды ls отображаются в цвете!
which — определение местоположения выполняемого файла
Иногда в системе имеется более одной версии исполняемой программы. Это до- вольно редкое явление для настольных систем, но вполне обычное для больших
78
Глава 5. Работа с командами
серверов. Точно определить местоположение данного исполняемого файла позво- ляет команда which:
[me@linuxbox ~]$ which ls /bin/ls
which ищет только исполняемые программы, она не способна выявлять встроен- ные команды или псевдонимы, замещающие фактические исполняемые програм- мы. Если попытаться с помощью which определить местоположение встроенной команды (например, cd), мы либо ничего не получим, либо получим сообщение об ошибке:
[me@linuxbox ~]$ which cd /usr/bin/which: no cd in (/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games)
Это своеобразное сообщение «command not found» (команда не найдена).
Получение документации с описанием команд
Теперь, зная тип команды, можно поискать документацию с описанием, доступ- ную для каждого вида команд1.
help — получение справки для встроенных команд
bash имеет встроенную справку для каждой встроенной команды. Чтобы полу- чить ее, введите help с именем встроенной команды. Например:
[me@linuxbox ~]$ help cd cd: cd [-L|-P] [dir] Change the current directory to DIR. The default DIR is the value of the HOME shell variable.
The variable CDPATH defines the search path for the directory containing DIR. Alternative directory names in CDPATH are separated by a colon (:). A null directory name is the same as the current directory. If DIR begins with a slash (/), then CDPATH is not used.
If the directory is not found, and the shell option `cdable_vars’ is set, then try the word as a variable name. If that variable has a value, its value is used for DIR.
1 Некоторые разделы справки Linux переведены на русский язык, а некоторые — нет. Для переведенных разделов мы будем приводить русский текст, выводимый системой на кон- соль, для непереведенных — указывать перевод в сносках. — Примеч. ред.
Получение документации с описанием команд
79
Options:
-L force symbolic links to be followed: resolve symbolic links in DIR after
processing instances of ..' -P use the physical directory structure without following symbolic links: resolve symbolic links in DIR before processing instances of ..'
-e if the -P option is supplied, and the current working directory cannot be
determined successfully, exit with a non-zero status
-@ on systems that support it, present a file with extended attributes
as a directory containing the file attributes
The default is to follow symbolic links, as if -L' were specified. ..’ is
processed by removing the immediately previous pathname component back to a slash
or the beginning of DIR.
Exit Status: Returns 0 if the directory is changed, and if $PWD is set successfully when -P is used; non-zero otherwise..1
1 Перевод:
Делает указанный каталог DIR текущим. Если каталог DIR не указан, по умолчанию используется значение переменной окружения
$HOME.
Переменная CDPATH определяет пути поиска каталога, содержащего DIR. Альтернативные имена каталогов в CDPATH отделяются друг от друга двоеточием (:). Пустое имя каталога соответствует текущему каталогу. Если DIR начинается с символа слеш (/), переменная CDPATH не используется.
Если каталог не найден и установлен параметр `cdable_vars’ командной оболочки, вы- полняется попытка интерпретировать слово как имя переменной. Если эта переменная имеет значение, тогда это значение используется как DIR.
Параметры: -L требует следовать по символическим ссылкам: символические ссылки разрешаются
в DIR после обработки элементов `..'
–P требует использовать физическую структуру каталогов без следования по символиче- ским ссылкам: символические ссылки разрешаются в DIR до обработки элементов `..' -e если указан параметр -P и текущий рабочий каталог невозможно определить, завер-
шить команду с ненулевым кодом возврата
-@ в системах, поддерживающих это, представляет файл с расширенными атрибутами как
каталог, содержащий атрибуты файлов
По умолчанию следует по символическим ссылкам, как если бы был указан параметр
-L'. Встретив ..’, удаляет предыдущий компонент пути до ближайшего символа
слеш или до начала DIR.
Возвращаемый код состояния: Возвращает 0, если смена каталога произошла и переменной $PWD было благополучно присвоено значение (при использовании параметра -P); в остальных случаях возвращает ненулевое значение.
80
Глава 5. Работа с командами
Примечание к форме записи: квадратные скобки в описании синтаксиса команды указывают на необязательность элемента. Вертикальная черта используется для перечисления взаимоисключающих вариантов. В примере с описанием команды cd, приведенном выше, ее синтаксис описывается как
cd [-L|[-P[-e]]] [dir]
Эта форма записи говорит, что команда cd может принимать необязательный па- раметр -L или -P и необязательный аргумент dir. Если указан параметр -P, вместе с ним можно также передать параметр -e.
Несмотря на то что help дает краткое и точное описание команды cd, это описание не может служить инструкцией по использованию, и, как вы можете видеть, в нем упоминается многое из того, чего мы еще не знаем! Но не волнуйтесь, со всем этим мы познакомимся в свое время.
–help — вывод инструкции по использованию
Многие выполняемые программы поддерживают параметр –help для вывода описания синтаксиса и параметров, поддерживаемых командой. Например:
[me@linuxbox ~]$ mkdir –help Использование: mkdir [КЛЮЧ]… КАТАЛОГ… Создает КАТАЛОГ(и), если он еще не существует.
-Z, –context=CONTEXT (SELinux) установить контекст безопасности в CONTEXT Аргументы, обязательные для длинных ключей, обязательны и для коротких. -m, –mode=РЕЖИМ установить режим доступа (как в chmod), не a=rwx — umask -p, –parents не выдавать ошибок, если существует, создавать родительские каталоги, если необходимо -v, –verbose печатать сообщение о каждом созданном каталоге –help показать эту справку и выйти –version показать информацию о версии и выйти Об ошибках в mkdir сообщайте по адресу .
Некоторые программы не поддерживают параметр –help, но вы все равно про- буйте передать его. Часто в результате выводится сообщение об ошибке, содержа- щее ту же информацию о порядке использования.
man — вывод страниц справочного руководства
Большинство программ, предназначенных для использования в командной стро- ке, предоставляют официальную документацию, которую называют страницей
Получение документации с описанием команд
81
справочного руководства (man-страницу). Для просмотра этих страниц использу- ется специальная программа постраничного просмотра man, например:
man программа
где программа — имя команды.
Страницы справочного руководства могут несколько отличаться друг от друга оформлением, но в общем случае содержат:
заголовок (название страницы);
краткий обзор синтаксиса команды;
описание назначения команды;
список всех параметров с их описанием.
Однако страницы справочного руководства обычно не включают примеры ис- пользования, и их главная цель — служить справочником, а не инструкцией по использованию. Для примера попробуйте вывести страницу справочного руко- водства для команды ls:
[me@linuxbox ~]$ man ls
В большинстве систем Linux man использует less для вывода страницы, поэтому при просмотре страницы можно использовать все известные команды less.
«Руководство», которое отображает man, разбито на разделы и охватывает не толь- ко пользовательские команды, но и команды системного администрирования, программные интерфейсы, форматы файлов и многое другое. В табл. 5.1 перечис- лены разделы справочного руководства.
Таблица 5.1. Организация справочного руководства
Раздел
Содержит
1
2
3
4
5
6
7
8
Пользовательские команды
Программные интерфейсы системных вызовов в ядре
Программные интерфейсы в библиотеке C
Специальные файлы, такие как узлы устройств и драйверы
Форматы файлов
Игры и развлечения, такие как хранители экрана
Прочее
Команды системного администрирования
82
Глава 5. Работа с командами
Иногда, чтобы найти искомое, нужно заглянуть в конкретный раздел. Это актуаль- но для форматов файлов, названия которых часто совпадают с именами команд. Если номер раздела не указан, man всегда будет возвращать первую найденную страницу, обычно из раздела 1. Ниже приведен пример прямого указания номера раздела:
man раздел искомый_термин
Например:
[me@linuxbox ~]$ man 5 passwd
выведет страницу с описанием формата файла /etc/passwd.
apropos — вывод списка подходящих команд
Кроме того, существует возможность найти страницы справочного руководства для близких совпадений с искомым термином. Несмотря на неточность, этот под- ход иногда оказывается полезным. Ниже приводится пример поиска страниц справочного руководства по слову partition:
[me@linuxbox ~]$ apropos partition addpart (8) - simple wrapper around the “add partition” ioctl all-swaps (7) - event signalling that all swap partitions have been ac… cfdisk (8) - display or manipulate disk partition table cgdisk (8) - Curses-based GUID partition table (GPT) manipulator delpart (8) - simple wrapper around the “del partition” ioctl fdisk (8) - manipulate disk partition table fixparts (8) - MBR partition table repair utility gdisk (8) - Interactive GUID partition table (GPT) manipulator mpartition (1) - partition an MSDOS hard disk partprobe (8) - inform the OS of partition table changes partx (8) - tell the Linux kernel about the presence and numbering… resizepart (8) - simple wrapper around the “resize partition” ioctl sfdisk (8) - partition table manipulator for Linux sgdisk (8) - Command-line GUID partition table (GPT) manipulator fo..1
1 Перевод:
addpart (8) - простая обертка для ioctl-команды “add partition” all-swaps (7) - событие, сигнализирующее, что все разделы подкачки были… cfdisk (8) - выводит или изменяет таблицу разделов диска cgdisk (8) - программа управления таблицами GUID разделов на основе curses delpart (8) - простая обертка для ioctl-команды “del partition” fdisk (8) - программа управления таблицей разделов диска fixparts (8) - утилита восстановления MBR таблицы разделов gdisk (8) - программа интерактивного управления таблицами GUID разделов (GPT)
Получение документации с описанием команд
83
Первое поле в каждой строке вывода — это имя страницы справочного руковод- ства, а второе поле — номер раздела. Обратите внимание, что команда man с пара- метром -k действует как apropos.
САМАЯ БРУТАЛЬНАЯ СТРАНИЦА СПРАВОЧНОГО РУКОВОДСТВА
Как вы могли убедиться, страницы справочного руководства, входящие в состав Linux и других Unix-подобных систем, играют роль справочной документации, но не инструкций по использованию. Многие страницы очень сложно читать, но, как мне кажется, первый приз за сложность можно было бы присудить странице с опи- санием bash. Работая над книгой, я очень внимательно прочитал эту страницу, чтобы убедиться, что не упустил ни одной важной темы. Когда я ее распечатал, у меня получилось больше 80 страниц чрезвычайно плотного текста, структура которого не имеет никакого смысла для начинающих пользователей.
С другой стороны, эта страница очень точная и краткая и содержит полную ин- формацию. Поэтому почитайте ее, если у вас есть запас терпения, а затем ждите того момента, когда вы сможете читать ее и прочитанное будет наполнено для вас смыслом.
whatis — вывод очень краткого описания команды
Программа whatis выводит имя и однострочное описание из страницы справочно- го руководства, соответствующей искомому слову:
[me@linuxbox ~]$ whatis ls ls (1) — выводит содержимое каталога
info — вывод записи из справочного руководства Info
В проекте GNU имеется альтернативное руководство Info, которое часто назы- вают info-страницами. Info-страницы выводятся с помощью программы чтения
mpartition (1) - создание разделов MSDOS на жестком диске partprobe (8) - информирует ОС об изменении таблицы разделов partx (8) - сообщает ядру Linux об имеющихся разделах и их нумерации… resizepart (8) - простая обертка для ioctl-команды “resize partition” sfdisk (8) - программа управления таблицами разделов для Linux sgdisk (8) - программа командной строки для управления таблицами GUID разделов (GPT)…
84
Глава 5. Работа с командами
с подходящим названием info. Info-страницы содержат гиперссылки, подобно веб-страницам. Например:
File: coreutils.info, Node: ls invocation, Next: dir invocation, Up: Directory listing 10.1 `ls’: List directory contents #
The ls' program lists information about files (of any type, including directories). Options and file arguments can be intermixed arbitrarily, as usual. For non-option command-line arguments that are directories, by default ls'
lists the contents of directories, not recursively, and omitting files with
names beginning with .'. For other non-option arguments, by default ls'
lists just the filename. If no non-option argument is specified, ls' operates on the current directory, acting as if it had been invoked with a single argument of .’.
By default, the output is sorted alphabetically, according to the
–zz-Info: (coreutils.info.gz)ls invocation, 63 lines –Top———-1
Программа info читает info-файлы, организованные в древовидную структу- ру отдельных узлов, каждый из которых содержит отдельную тему. Info-файлы включают гиперссылки, с помощью которых можно перемещаться от узла к узлу. Гиперссылку можно узнать по начальному символу звездочки. Гиперссылки акти- вируются при установке текстового курсора на них и осуществляют переход при нажатии клавиши ENTER.
Чтобы вывести info-страницу, введите команду info и добавьте после нее необяза- тельное имя интересующей программы. В табл. 5.2 перечислены команды, которые можно использовать для управления программой во время чтения info-страницы.
1 Перевод:
Файл: coreutils.info, Узел: команда ls, Следующий: команда dir, Up: Содержимое каталога 10.1 `ls’: выводит содержимое каталога #
Программа ls' выводит информацию о файлах (любого типа, включая каталоги). Параметры и аргументы могут смешиваться произвольно, как обычно. Для аргументов без параметров, представляющих каталоги, по умолчанию ls'
выводит содержимое каталогов нерекурсивно и пропускает файлы с
именами, начинающимися с .'. Для других аргументов без параметров по умолчанию ls'
выводит только указанный файл. В отсутствие аргументов без параметров ls' выводит содержимое текущего каталога, действуя, как если бы была вызвана с единственным аргументом .’.
По умолчанию вывод сортируется в алфавитном порядке, согласно
–zz-Info: (coreutils.info.gz)команда ls, 63 строки –Top———-
Создание собственных команд с помощью alias
85
Таблица 5.2. Команды программы info
Команда
Действие
?
Вывести справку
Page Up или Backspace
Вывести предыдущую страницу
Page Down или ПРОБЕЛ Вывести следующую страницу
n
p
u
ENTER
q
Вперед (next) — вывести следующий узел
Назад (previous) — вывести предыдущий узел
Вверх (up) — вывести узел, родительский по отношению к теку- щему, обычно меню
Перейти по гиперссылке, находящейся на позиции курсора
Завершить (quit)
Большинство программ из числа рассматривавшихся до сих пор, является частью пакета coreutils проекта GNU, поэтому о них можно получить дополнительную информацию командой
[me@linuxbox ~]$ info coreutils
Она выведет страницу с меню, состоящим из гиперссылок на документацию для каждой программы, входящей в состав пакета coreutils.
README и другие файлы с описанием программ
Многие программные пакеты, установленные в вашей системе, включают файлы с документацией, размещаемые в каталоге /usr/share/doc. Большинство из них имеют простой текстовый формат и могут просматриваться с помощью less. Не- которые файлы имеют формат HTML и могут просматриваться с помощью веб- браузера. Можно также встретить файлы с расширением .gz. Это сжатые файлы, обработанные программой-архиватором gzip. Пакет gzip включает специальную версию less с именем zless, которая выводит содержимое текстовых файлов, сжа- тых архиватором gzip.
Создание собственных команд с помощью alias
А теперь проведем первый опыт по программированию! У нас есть возможность создавать собственные команды с помощью команды alias. Но прежде чем начать,
86
Глава 5. Работа с командами
познакомимся с одной маленькой хитростью командной строки. Она позволяет уместить в одной строке несколько команд, для чего нужно просто отделить их друг от друга точкой с запятой:
команда1; команда2; команда3…
Следующий пример демонстрирует этот прием:
[me@linuxbox ~]$ cd /usr; ls; cd - bin games include lib local sbin share src /home/me [me@linuxbox ~]$
Как видите, мы поместили три команды в одну строку. Первая выполняет пере- ход в каталог /usr, вторая выводит его содержимое, и третья осуществляет возврат в предыдущий каталог (команда cd -), поэтому по завершении мы оказываемся там же, где и были. Давайте теперь с помощью alias превратим эту последова- тельность в новую команду. Первое, что мы должны сделать, — придумать имя для новой команды. Пусть это будет test. Но прежде чем продолжить, хорошо бы проверить, не занято ли уже имя test. Для этого воспользуемся командой type:
[me@linuxbox ~]$ type test test встроена в оболочку
Ой! Имя test уже занято. Попробуем foo:
[me@linuxbox ~]$ type foo bash: type: foo: не найден
Отлично! Имя foo свободно. Теперь создадим наш псевдоним:
[me@linuxbox ~]$ alias foo=‘cd /usr; ls; cd -’
Обратите внимание на структуру этой команды:
alias имя=‘строка’
За командой alias следует имя, сразу за которым (то есть без пробелов) следует знак «равно» и строка в кавычках, описывающая действие, присваиваемое име- ни. После определения псевдонима его можно использовать везде, где ожидается коман да. Давайте попробуем:
[me@linuxbox ~]$ foo bin games inclide lib local sbin share src /home/me [me@linuxbox ~]$
Заключение
87
Команда type правильно определяет псевдонимы:
[me@linuxbox ~]$ type foo foo является алиасом для `cd /usr; ls; cd -'
Удалить псевдоним можно с помощью команды unalias:
[me@linuxbox ~]$ unalias foo [me@linuxbox ~]$ type foo bash: type: foo: не найден
Несмотря на то что в этом примере мы постарались не использовать имя суще- ствующей команды, иногда это бывает полезно. Часто это делается, чтобы при- менить наиболее желательные параметры к каждому вызову команды. Например, выше мы видели, что команда ls часто определяется как псевдоним, — это позво- ляет реализовать вывод информации в цвете:
[me@linuxbox ~]$ type ls ls является алиасом для `ls –color=tty'
Если вызвать команду alias без аргументов, она выведет список всех псевдони- мов в окружении. Ниже приводятся несколько псевдонимов, объявляемых в дис- трибутиве Fedora по умолчанию. Попробуйте понять, что они делают:
[me@linuxbox ~]$ alias alias l.=‘ls -d .* –color=tty’ alias ll=‘ls -l –color=tty’ alias ls=‘ls –color=tty’
Существует одна маленькая проблема, связанная с определением псевдонимов в командной строке. Они исчезают по завершении сеанса работы с командной оболочкой. В главе 11 будет показано, как добавить определения псевдонимов в файлы, чтобы они восстанавливались при каждом запуске командной оболочки, а пока насладимся нашим первым, пусть и крошечным, шагом в мир программи- рования на языке командной оболочки!
Заключение
Теперь, когда мы узнали, как найти документацию с описанием команд, поупраж- няйтесь самостоятельно и найдите описание всех команд, встретившихся вам в этой книге. Познакомьтесь с их дополнительными параметрами и опробуйте их!
6 Перенаправление
В этом уроке мы познакомимся с самой крутой возможностью командной строки: перенаправлением ввода/вывода. Благодаря этой возможности мы сможем перена- правлять ввод и вывод команд из файлов и в файлы, а также составлять из команд целые конвейеры. Для демонстрации этой возможности введем в обиход следую- щие команды:
cat — объединяет файлы;
sort — сортирует строки текста;
uniq — сообщает о повторяющихся строках или удаляет их;
grep — находит и выводит строки, соответствующие шаблону;
wc — выводит число символов перевода строки, слов и байтов в каждом указанном файле;
head — выводит первые строки из файла;
tail — выводит последние строки из файла;
tee — читает данные со стандартного ввода и записывает в стандартный вывод и в файлы.
Стандартный ввод, вывод и вывод ошибок
Многие программы, которыми мы уже пользовались, что-нибудь выводят на кон- соль. Этот вывод часто делится на два типа:
Результаты работы программы, то есть данные, для получения которых созда-
валась программа.
Перенаправление стандартного вывода
89
Сообщения о состоянии или об ошибках, извещающие нас о самочувствии
программы.
Например, если взглянуть на вывод программы ls, можно увидеть, что она выво- дит на экран результаты своей работы и иногда сообщения об ошибках.
Согласно центральной идее Unix, что «все сущее есть файл», такие программы, как ls, в действительности выводят свои результаты в специальный файл, кото- рый называется стандартным выводом (standard output, часто обозначается как stdout), а сообщения о состоянии — в специальный файл стандартный вывод оши- бок (standard error, stderr). По умолчанию оба файла, стандартный вывод и стан- дартный вывод ошибок, связаны с экраном и не сохраняются на диске.
Кроме того, многие программы принимают ввод из специального файла с назва- нием стандартный ввод (standard input, stdin), который по умолчанию связан с клавиатурой.
Механизм перенаправления ввода/вывода позволяет изменять направление вы- вода и ввода. Обычно вывод осуществляется на экран, а ввод — с клавиатуры, но механизм перенаправления ввода/вывода позволяет изменить этот порядок вещей.
Перенаправление стандартного вывода
Механизм перенаправления ввода/вывода позволяет явно указать, куда должен осуществляться стандартный вывод. Чтобы перенаправить стандартный вывод в другой файл вместо экрана, нужно добавить в команду оператор перенаправле- ния > и имя файла. Где это может пригодиться? Иногда полезно сохранить вывод команды в файл. Например, можно сообщить командной оболочке, что она долж- на направить вывод команды ls в файл ls-output.txt вместо экрана:
[me@linuxbox ~]$ ls -l /usr/bin > ls-output.txt
Здесь мы создали длинный список содержимого каталога /usr/bin и отправи- ли результаты в файл ls-output.txt. Давайте исследуем перенаправленный вывод команды:
[me@linuxbox ~]$ ls -l ls-output.txt -rw-rw-r– 1 me me 167878 2018-02-01 15:07 ls-output.txt
Неплохой файл получился. Если вывести содержимое ls-output.txt с помощью команды less, можно увидеть, что он действительно содержит результаты работы команды ls:
[me@linuxbox ~]$ less ls-output.txt
90
Глава 6. Перенаправление
Давайте теперь повторим эксперимент с перенаправлением, но с небольшим ус- ложнением: укажем имя несуществующего каталога:
[me@linuxbox ~]$ ls -l /bin/usr > ls-output.txt ls: невозможно получить доступ к ‘/bin/usr’: Нет такого файла или каталога
Мы получили сообщение об ошибке. Все логично — мы указали несуществующий каталог /bin/usr, но почему же сообщение появилось на экране, а не было перена- правлено в файл ls-output.txt? Дело в том, что программа ls не выводит сообщения об ошибках в стандартный вывод. Как и многие добропорядочные программы для Unix, она выводит сообщения об ошибках в стандартный поток вывода ошибок. Поскольку мы перенаправили только стандартный вывод, а стандартный вывод ошибок — нет, сообщение об ошибке появилось на экране. Как перенаправить стандартный вывод ошибок, будет показано чуть ниже, но перед этим посмотрим, что произошло с нашим файлом:
[me@linuxbox ~]$ ls -l ls-output.txt -rw-rw-r– 1 me me 0 2018-02-01 15:08 ls-output.txt
Файл очистился! Это объясняется тем, что при перенаправлении вывода с по- мощью оператора > файл назначения всегда перезаписывается с самого начала. Поскольку команда ls не вывела никаких результатов, а только сообщение об ошибке, оператор перенаправления перезаписал файл, а затем остановился из-за ошибки, что привело к его очистке. Получается, что если вам понадобится очи- стить какой-нибудь файл (или создать новый, пустой файл), это можно сделать с помощью следующего трюка:
[me@linuxbox ~]$ > ls-output.txt
Простой оператор перенаправления, без предшествующей ему команды, очистит существующий файл или создаст новый, пустой файл.
Так как же добавить вывод в конец существующего файла, не затерев его? Для этого используем оператор перенаправления »:
[me@linuxbox ~]$ ls -l /usr/bin » ls-output.txt
Оператор » просто добавит результаты в конец файла. Если файл не существует, он будет создан, как при использовании оператора >. Давайте протестируем его:
[me@linuxbox ~]$ ls -l /usr/bin » ls-output.txt [me@linuxbox ~]$ ls -l /usr/bin » ls-output.txt [me@linuxbox ~]$ ls -l /usr/bin » ls-output.txt
Перенаправление стандартного вывода ошибок
91
[me@linuxbox ~]$ ls -l ls-output.txt -rw-rw-r– 1 me me 503634 2018-02-01 15:45 ls-output.txt
Мы повторили команду трижды и получили файл втрое большего размера.
Перенаправление стандартного вывода ошибок
Перенаправление стандартного вывода ошибок осуществляется не так просто, как стандартного вывода. Чтобы перенаправить стандартный вывод ошибок, нужно указать его дескриптор файла. Программа может производить вывод в любой из нескольких нумерованных файловых потоков. Первые три из них мы упомянули как стандартный ввод, вывод и вывод ошибок. Командная оболочка ссылается на них как на файловые дескрипторы 0, 1 и 2 соответственно. Командная оболоч- ка поддерживает синтаксис перенаправления файлов с использованием номеров файловых дескрипторов. Так как стандартному выводу ошибок соответствует файловый дескриптор 2, мы можем перенаправить его, как показано ниже:
[me@linuxbox ~]$ ls -l /bin/usr 2> ls-error.txt
Номер файлового дескриптора 2 помещается непосредственно перед операто- ром перенаправления, чтобы перенаправить стандартный вывод ошибок в файл ls-error.txt.
Перенаправление стандартного вывода и стандартного вывода ошибок в один файл
Иногда необходимо сохранить весь вывод команды в один файл. Для этого пере- направьте сразу два потока, стандартный вывод и стандартный вывод ошибок. Сделать это можно двумя способами. Первый — традиционный — работает в ста- рых версиях командной оболочки:
[me@linuxbox ~]$ ls -l /bin/usr > ls-output.txt 2>&1
Здесь выполняются два перенаправления. Сначала — перенаправление стандарт- ного вывода в файл ls-output.txt, а затем, с использованием нотации 2>&1, — пере- направление файлового дескриптора 2 (стандартный вывод ошибок) в файловый дескриптор 1 (стандартный вывод).
92
Глава 6. Перенаправление
ИМЕЙТЕ В ВИДУ, ЧТО ПОРЯДОК ПЕРЕНАПРАВЛЕНИЯ ИГРАЕТ ВАЖНУЮ РОЛЬ
Перенаправление стандартного вывода ошибок всегда должно производиться после перенаправления стандартного вывода, иначе этот трюк не сработает. Следующий пример перенаправит стандартный вывод ошибок в файл ls-output.txt:
ls-output.txt 2>&1
Если порядок перенаправления изменить, как показано ниже, стандартный вывод ошибок будет перенаправлен на экран:
2>&1 >ls-output.txt
Современные версии bash поддерживают второй, более простой метод выполне- ния перенаправления этого вида:
[me@linuxbox ~]$ ls -l /bin/usr &> ls-output.txt
В данном примере используется единственный оператор &>, перенаправляющий стандартный вывод и стандартный вывод ошибок в файл ls-output.txt. Аналогично можно перенаправить стандартный вывод и стандартный вывод ошибок с добав- лением в конец одного и того же файла:
[me@linuxbox ~]$ ls -l /bin/usr &» ls-output.txt
Удаление нежелательного вывода
Иногда молчание действительно золото, и вывод команды нужно отбросить. В особенности это касается служебных сообщений и сообщений об ошибках. Си- стема дает такую возможность, предоставляя специальный файл /dev/null, куда можно перенаправить вывод. Этот файл представляет системное устройство, на- зываемое битоприемником (bit bucket) или мусорной корзиной, которое прини- мает любой ввод и ничего с ним не делает. Чтобы подавить вывод сообщений об ошибках, достаточно проделать следующее:
[me@linuxbox ~]$ ls -l /bin/usr 2> /dev/null
/DEV/NULL В КУЛЬТУРЕ UNIX
«Битоприемник» — старое понятие в Unix, благодаря своему универсализму ши- роко используется в культуре Unix. Так, когда кто-то скажет, что посылает ваши комментарии в /dev/null, вы теперь будете знать, что это означает. Еще больше примеров вы найдете в статье Википедии https://ru.wikipedia.org/wiki//dev/null.
Перенаправление стандартного ввода
93
Перенаправление стандартного ввода
До сих пор нам не встречались команды, использующие стандартный ввод (на са- мом деле они встречались, но мы подробнее обсудим их чуть ниже), поэтому нам нужно познакомиться с ними.
cat — объединение файлов
Команда cat читает содержимое одного или нескольких файлов и копирует его в стандартный вывод:
cat [файл…]
Часто команду cat можно считать аналогом команды TYPE в DOS. Она использу- ется для вывода содержимого файлов без возможности постраничного просмотра. Например,
[me@linuxbox ~]$ cat ls-output.txt
выведет содержимое файла ls-output.txt.
Команда cat часто используется для вывода коротких текстовых файлов. По- скольку cat способна принимать сразу несколько файлов, она используется для их объединения. Представьте, что вы загрузили большой файл, разбитый на мно- жество частей (в Usenet мультимедийные файлы часто разбиваются таким спосо- бом), и требуется объединить их в один файл. Если файлы имеют имена, такие как
movie.mpeg.001 movie.mpeg.002 … movie.mpeg.099
их можно объединить следующей командой:
[me@linuxbox ~]$ cat movie.mpeg.0* > movie.mpeg
Поскольку подстановка фактических имен взамен групповых символов всегда вы- полняется в порядке сортировки, аргументы окажутся расположенными в пра- вильном порядке.
Все это прекрасно, но при чем здесь стандартный ввод? Пока ни при чем, но да- вайте попробуем кое-что еще. Что получится, если вызвать cat без аргументов?
[me@linuxbox ~]$ cat
Ничего не произошло — такое ощущение, что команда зависла. Однако в действи- тельности команда делает именно то, что и предполагалось.
94
Глава 6. Перенаправление
Если вызвать cat без аргументов, она начнет читать данные со стандартного ввода, а поскольку стандартный ввод по умолчанию подключен к клавиатуре, получает- ся, что команда ждет, пока вы что-нибудь напечатаете! Попробуйте так:
[me@linuxbox ~]$ cat Съешь ещё этих мягких французских булок, да выпей чаю.
Затем нажмите комбинацию CTRL+D (то есть, удерживая нажатой клавишу CTRL, нажмите клавишу D), чтобы сообщить команде cat, что достигнут конец файла (end-of-file, EOF) на стандартном вводе:
[me@linuxbox ~]$ cat Съешь ещё этих мягких французских булок, да выпей чаю. Съешь ещё этих мягких французских булок, да выпей чаю.
В отсутствие аргументов с именами файлов cat копирует содержимое стандарт- ного ввода в стандартный вывод, поэтому-то мы и увидели, как она повторила введенную нами строку. Эту ее особенность можно использовать для создания коротких текстовых файлов. Представьте, что вам потребовалось создать файл с именем eat_more.txt, содержащий текст из примера, приведенного выше. Сделать это можно было бы так:
[me@linuxbox ~]$ cat > eat_more.txt Съешь ещё этих мягких французских булок, да выпей чаю.
Введите команду, затем текст, который нужно поместить в файл, и не забудьте на- жать комбинацию CTRL+D в конце. Используя командную строку, мы реализовали самый простой в мире текстовый процессор! Чтобы увидеть результат, воспользу- емся командой cat и скопируем файл в стандартный вывод:
[me@linuxbox ~]$ cat eat_more.txt Съешь ещё этих мягких французских булок, да выпей чаю.
Теперь, когда мы знаем, что команда cat может принимать данные не только из файлов, указанных в аргументах, но и со стандартного ввода, попробуем выпол- нить перенаправление стандартного ввода:
[me@linuxbox ~]$ cat . Выражаясь простым языком, оператор перенаправ- ления связывает команду с файлом, а оператор конвейера связывает вывод одной команды с вводом другой.
команда1 > файл1 команда1 | команда2
Многие, впервые познакомившись с идеей конвейера, пробуют проделать следу- ющий трюк, «только чтобы посмотреть, что из этого получится»:
команда1 > команда2
Не повторяйте этот эксперимент: иногда он может стать причиной больших про- блем.
Вот конкретный пример, представленный читателем, который по роду своей дея- тельности занимается администрированием сервера с ОС Linux. Зарегистрировав- шись как суперпользователь, он выполнил следующую пару команд:
cd /usr/bin #
ls > less #
Первая команда выполнила переход в каталог, где хранится большинство про- грамм, а вторая потребовала от командной оболочки очистить файл less и записать в него вывод команды ls. Так как в каталоге /usr/bin уже имеется файл с именем less (программа less), вторая команда затерла программу в файле less текстом, который вернула команда ls, и тем самым уничтожила программу less.
Помните, что оператор перенаправления без лишних предупреждений создает или затирает файлы, поэтому относитесь к нему с особым вниманием.
uniq — поиск или удаление повторяющихся строк
Команда uniq часто используется в комбинации с командой sort. uniq прини- мает сортированный список данных либо со стандартного ввода, либо из фай- ла, имя которого можно передать в единственном аргументе (за подробностями
Конвейеры
97
обращайтесь к странице справочного руководства (man) для команды uniq), и по умолчанию удаляет повторяющиеся строки из списка. Поэтому, чтобы гарантиро- вать отсутствие дубликатов в нашем списке (то есть любых программ с одинако- выми именами в каталогах /bin и /usr/bin), добавим uniq в конвейер:
[me@linuxbox ~]$ ls /bin /usr/bin | sort | uniq | less
В этом примере мы использовали uniq для удаления любых повторяющихся строк в выводе команды sort. Если бы нам потребовалось, наоборот, получить список дубликатов, мы добавили бы в команду uniq параметр -d:
[me@linuxbox ~]$ ls /bin /usr/bin | sort | uniq -d | less
wc — вывод числа строк, слов и байтов
Команда wc (word count — счетчик слов) используется для подсчета числа строк, слов и байтов в файлах. Например:
[me@linuxbox ~]$ wc ls-output.txt 7902 64566 503634 ls-output.txt
В данном случае команда вывела три числа: число строк, число слов и число бай- тов в файле ls-output.txt. Подобно предыдущим командам, она может вызываться без аргументов, и в этом случае wc будет принимать данные со стандартного вво- да. Параметр -l ограничивает вывод результатов только числом строк. Команду удобно использовать в конвейерах для подсчета: например, подсчитать число эле- ментов в нашем сортированном списке можно так:
[me@linuxbox ~]$ ls /bin /usr/bin | sort | uniq | wc -l 2728
grep — поиск строк, соответствующих шаблону
grep — очень мощная программа, она часто используется для поиска в файлах тек- ста по шаблону:
grep шаблон [файл…]
Когда grep находит в файле совпадение с «шаблоном», она выводит строку с най- денным совпадением. Шаблоны, используемые командой grep для поиска, могут быть очень сложными, но сейчас мы рассмотрим только поиск прямого совпаде- ния с текстом. Более сложные шаблоны, которые называют регулярными выраже- ниями, мы рассмотрим в главе 19.
98
Глава 6. Перенаправление
Допустим, что нам нужно найти все файлы в списке программ, которые имеют в своем имени последовательность символов zip. Результаты такого поиска могут подсказать нам, какие программы в системе имеют отношение к сжатию файлов. Сделать это можно так:
[me@linuxbox ~]$ ls /bin /usr/bin | sort | uniq | grep zip bunzip2 bzip2 gunzip gzip unzip zip zipcloak zipgrep zipinfo zipnote zipsplit
Команда grep имеет пару удобных параметров:
-i требует от grep игнорировать регистр символов в процессе поиска (обычно
поиск выполняется с учетом регистра символов);
-v требует от grep выводить только строки, где совпадение с шаблоном не най-
дено.
head/tail — вывод первых/последних строк из файлов
Иногда требуется выводить не все результаты работы команды, а только несколько первых или несколько последних строк. Команда head выводит первые 10 строк из файла, а tail — последние 10 строк. По умолчанию обе команды выводят 10 строк текста, но это число можно изменить с помощью параметра -n:
[me@linuxbox ~]$ head -n 5 ls-output.txt total 343496 -rwxr-xr-x 1 root root 31316 2017-12-05 08:58 [ -rwxr-xr-x 1 root root 8240 2017-12-09 13:39 411toppm -rwxr-xr-x 1 root root 111276 2017-11-26 14:27 a2p -rwxr-xr-x 1 root root 25368 2016-10-06 20:16 a52dec [me@linuxbox ~]$ tail -n 5 ls-output.txt -rwxr-xr-x 1 root root 5234 2017-06-27 10:56 znew -rwxr-xr-x 1 root root 691 2005-09-10 04:21 zonetab2pot.py -rw-r–r– 1 root root 930 2017-11-01 12:23 zonetab2pot.pyc -rw-r–r– 1 root root 930 2017-11-01 12:23 zonetab2pot.pyo lrwxrwxrwx 1 root root 6 2016-01-31 05:22 zsoelim -> soelim
Конвейеры
99
Их также можно использовать в конвейерах:
[me@linuxbox ~]$ ls /usr/bin | tail -n 5 znew zonetab2pot.py zonetab2pot.pyc zonetab2pot.pyo zsoelim
Команда tail позволяет наблюдать, как изменяется содержимое файла в режи- ме реального времени. Эту ее особенность удобно использовать для наблюдения за появлением новых записей в файлах журналов. В следующем примере демон- стрируется наблюдение за файлом messages в каталоге /var/log. В некоторых дис- трибутивах Linux для этого требуется обладать привилегиями суперпользовате- ля, поскольку файл /var/log/messages может содержать секретную информацию.
[me@linuxbox ~]$ tail -f /var/log/messages Feb 8 13:40:05 twin4 dhclient: DHCPACK from 192.168.1.1 Feb 8 13:40:05 twin4 dhclient: bound to 192.168.1.4 – renewal in 1652 seconds. Feb 8 13:55:32 twin4 mountd[3953]: /var/NFSv4/musicbox exported to both 192.168.1.0/24 and twin7.localdomain in 192.168.1.0/24,twin7.localdomain Feb 8 14:07:37 twin4 dhclient: DHCPREQUEST on eth0 to 192.168.1.1 port 67 Feb 8 14:07:37 twin4 dhclient: DHCPACK from 192.168.1.1 Feb 8 14:07:37 twin4 dhclient: bound to 192.168.1.4 – renewal in 1771 seconds. Feb 8 14:09:56 twin4 smartd[3468]: Device: /dev/hda, SMART Prefailure Attribute: 8 Seek_Time_Performance changed from 237 to 236 Feb 8 14:10:37 twin4 mountd[3953]: /var/NFSv4/musicbox exported to both 192.168.1.0/24 and twin7.localdomain in 192.168.1.0/24,twin7.localdomain Feb 8 14:25:07 twin4 sshd(pam_unix)[29234]: session opened for user me by (uid=0) Feb 8 14:25:36 twin4 su(pam_unix)[29279]: session opened for user root by me(uid=500)
При вызове с параметром -f команда tail продолжает следить за файлом и при добавлении в конец этого файла новых строк немедленно выводит их. Так про- должается до тех пор, пока пользователь не нажмет комбинацию клавиш CTRL+C.
tee — чтение со стандартного ввода и запись в стандартный вывод и в файлы
Linux предоставляет команду tee, которая создает Т-образное разветвление в конвейере. Программа tee читает данные со стандартного ввода и копирует их в стандартный вывод (чтобы дать возможность передать их дальше по конвей- еру) и в один или несколько файлов. Это может пригодиться для сохранения промежуточных результатов обработки в конвейере. Ниже, продолжая один из
100
Глава 6. Перенаправление
предыдущих примеров, мы сохраним полный список файлов в каталогах в файле ls.txt, перед тем как он будет отфильтрован командой grep:
[me@linuxbox ~]$ ls /usr/bin | tee ls.txt | grep zip bunzip2 bzip2 gunzip gzip unzip zip zipcloak zipgrep zipinfo zipnote zipsplit
LINUX РАЗВИВАЕТ ВООБРАЖЕНИЕ
Когда меня просят объяснить разницу между Windows и Linux, я часто привожу аналогию с игрушками.
Windows — это как игровая приставка Game Boy. Вы идете в магазин и покупаете новенькую сияющую приставку с игрой в комплекте. Приносите ее домой, включа- ете и играете. Отличная графика, чудные звуки. Но спустя некоторое время игра надоедает. Вы опять идете в магазин и покупаете другую игру. Так повторяется снова и снова. Наконец, вы возвращаетесь в магазин и говорите человеку за при- лавком: «Я хочу игру, которая делает это!» — а в ответ слышите, что такой игры не существует, потому что на нее нет спроса. Тогда вы говорите: «Но мне нужно всего лишь изменить вот это!» А продавец за прилавком говорит, что это невоз- можно. Игры продаются зашитыми в картриджи. И тут вы понимаете, что ваша приставка ограничена кругом игр, при создании которых кто-то другой решил за вас, что вам нужно, а что нет.
Linux, напротив, можно сравнить с самым большим в мире конструктором. Вы от- крываете коробку и видите необозримую коллекцию деталей — огромное число железных планочек, болтиков, гаечек, шестеренок, колесиков и моторчиков и не- сколько рекомендаций по сборке. Вы начинаете играть. Сначала создаете один предлагаемый образец, затем другой. Затем вы обнаруживаете, что у вас появились собственные идеи новых конструкций и механизмов. И вам не нужно возвращаться в магазин, потому что у вас уже есть все, что требуется. Конструктор формирует ваше воображение. Он позволяет создать то, что вы хотите.
Выбор игрушки, конечно же, дело глубоко личное, но признайтесь честно: какая игрушка принесла бы вам больше удовольствия?
Заключение
101
Заключение
Как обычно, загляните в документацию с описанием каждой команды, пред- ставленной в этой главе. Здесь были показаны только самые общие примеры их использования, и все они имеют множество интересных параметров. По мере накопления опыта работы в Linux мы увидим, что поддержка перенаправления в командной оболочке чрезвычайно полезна для решения специализированных задач. Многие команды используют стандартный ввод и стандартный вывод, и почти все программы командной строки используют стандартный вывод оши- бок для отображения информационных сообщений.
7 Взгляд на мир глазами
командной оболочки
В этой главе мы посмотрим, что происходит в командной строке после нажатия клавиши ENTER. И в процессе исследования некоторых интересных и сложных ме- ханизмов командной оболочки будем пользоваться только одной новой командой:
echo — выводит строку текста.
Подстановка
Каждый раз, когда вы вводите команду и нажимаете ENTER, bash производит не- сколько операций с текстом, прежде чем выполнит вашу команду. Мы уже виде- ли пару примеров, где простая последовательность символов, например *, может много значить для командной оболочки. Процесс, который происходит при этом, называется подстановкой (expansion). То есть вы вводите что-то, и это что-то за- мещается чем-то другим, прежде чем командная оболочка продолжит обработку. Чтобы показать, что все это значит, возьмем для примера команду echo — встроен- ную команду, выполняющую очень простую операцию: она выводит свои тексто- вые аргументы в стандартный поток вывода.
[me@linuxbox ~]$ echo this is a test this is a test
Все очень просто. echo выведет любой свой аргумент. Давайте попробуем другой пример:
[me@linuxbox ~]$ echo * Desktop Documents ls-output.txt Music Pictures Public Templates Videos
Подстановка
103
Что это? Почему echo не вывела символ *? Как вы помните из опытов с групповы- ми символами, символ * означает «последовательность любых символов в имени файла», правда, в том обзоре не рассказывалось, как командная оболочка делает это. На самом деле все просто: перед тем, как выполнить команду echo, оболочка замещает символ * чем-то другим (в данном случае именами файлов в текущем рабочем каталоге). После нажатия клавиши ENTER командная оболочка автомати- чески производит подстановку любых групповых символов в командной строке, прежде чем выполнить ее, поэтому команда echo не увидела * — она получила уже готовый результат после подстановки. Теперь вы понимаете, что в действитель- ности echo действует в точности с нашими ожиданиями?
Подстановка путей
Механизм работы групповых символов называется подстановкой пути (pathname expansion). Если вернуться к некоторым приемам, продемонстрированным в пре- дыдущих главах, мы увидим, что в действительности они основаны на подстанов- ке. Допустим, содержимое домашнего каталога выглядит вот так:
[me@linuxbox ~]$ ls Desktop ls-output.txt Pictures Templates Documents Music Public Videos
Мы могли бы выполнить следующую подстановку:
[me@linuxbox ~]$ echo D* Desktop Documents
или
[me@linuxbox ~]$ echo *s Documents Pictures Templates Videos
или даже
[me@linuxbox ~]$ echo [[:upper:]]* Desktop Documents Music Pictures Public Templates Videos
И заглянуть за пределы домашнего каталога:
[me@linuxbox ~]$ echo /usr/*/share /usr/kerberos/share /usr/local/share
104
Глава 7. Взгляд на мир глазами командной оболочки
ПОДСТАНОВКА ПУТИ ДЛЯ СКРЫТЫХ ФАЙЛОВ
Как мы знаем, файлы с именами, начинающимися с точки, считаются скрытыми. Механизм подстановки пути также учитывает это. Подстановка, такая как
echo *
не покажет скрытые файлы.
На первый взгляд кажется, что можно было бы включить скрытые файлы в под- становку, добавив в начало шаблона точку, например:
echo .*
Да, такой подход даст желаемое. Однако, если внимательно исследовать резуль- таты, можно заметить, что в них также присутствуют имена . (точка) и .. (две точки). Так как эти имена соответствуют текущему рабочему каталогу и роди- тельскому каталогу, применение такого шаблона может привести к неправильным результатам. Убедимся в этом с помощью команды
ls -d .* | less
Чтобы обеспечить правильную подстановку пути в такой ситуации, следует исполь- зовать специализированный шаблон. Следующий шаблон действует правильно:
ls -d .[!.]?*
Этот шаблон замещается именами файлов, начинающимися с единственной точ- ки, за которой следует любое количество других символов. Он будет работать правильно с большинством имен скрытых файлов (кроме тех, что начинаются с нескольких точек). Команда ls с параметром -A («almost all» — «почти все») выведет список всех скрытых файлов без ошибок.
ls -A
Подстановка тильды
Как вы помните из вводного пояснения команды cd, символ «тильда» (~) имеет специальное значение. Если он используется в начале слова, то замещается име- нем домашнего каталога указанного пользователя или, если пользователь не ука- зан, именем домашнего каталога текущего пользователя:
[me@linuxbox ~]$ echo ~ /home/me
Если в системе существует учетная запись пользователя foo, тогда
[me@linuxbox ~]$ echo ~foo /home/foo
Подстановка
105
Подстановка результатов арифметических выражений
Командная оболочка поддерживает также подстановку результатов арифметиче- ских выражений. Это позволяет использовать командную строку как калькуля- тор:
[me@linuxbox ~]$ echo $((2 + 2)) 4
Для подстановки арифметических выражений используется следующий формат:
$((выражение))
где выражение — это арифметическое выражение, состоящее из значений и ариф- метических операторов.
Механизм подстановки арифметических выражений позволяет использовать только целые числа (невещественные), зато поддерживает множество арифмети- ческих операций. В табл. 7.1 перечислены некоторые из поддерживаемых опера- торов.
Таблица 7.1. Арифметические операторы
Оператор
Описание
/
%
Сложение
Вычитание
Умножение
Деление (но помните, из-за того, что подстановка поддерживает только целочисленную арифметику, результатом будет целое число)
Деление по модулю или остаток от деления
**
Возведение в степень
Пробелы в арифметических выражениях не играют роли, а выражения могут со- держать вложенные выражения. Например, умножение 5^2 на 3:
[me@linuxbox ~]$ echo $(($((5**2)) * 3)) 75
Для группировки подвыражений допускается использование одиночных круг- лых скобок. С помощью этого приема выражение, приведенное выше, можно
106
Глава 7. Взгляд на мир глазами командной оболочки
переписать, как показано ниже, и получить тот же результат, но при этом будет использоваться одна операция подстановки вместо двух:
[me@linuxbox ~]$ echo $(((5**2) * 3)) 75
Следующий пример демонстрирует использование операторов деления и получе- ния остатка. Обратите внимание, как действует целочисленное деление:
[me@linuxbox ~]$ echo Пять разделить на два будет $((5/2)) Пять разделить на два будет 2 [me@linuxbox ~]$ echo и $((5%2)) в остатке. и 1 в остатке.
Подстановка результатов арифметических выражений подробнее будет рассма- триваться в главе 34.
Подстановка фигурных скобок
Самым малопонятным, пожалуй, выглядит результат подстановки фигурных скобок. С помощью этого механизма из одного шаблона, содержащего фигурные скобки, создается множество текстовых строк. Например:
[me@linuxbox ~]$ echo Впереди-{A,B,C}-позади Впереди-A-позади Впереди-B-позади Впереди-C-позади
Шаблоны с фигурными скобками могут содержать начальную часть, которая называется преамбулой, и заключительную часть, которая называется эпилогом. Внутри фигурных скобок находится список строк, разделенных запятыми, или диапазон целых чисел или одиночных символов. Использование пробелов внутри фигурных скобок не допускается. Ниже приводится пример с использованием диапазона целых чисел:
[me@linuxbox ~]$ echo Число_{1..5} Число_1 Число_2 Число_3 Число_4 Число_5
В версии bash 4.0 и выше целые числа можно дополнять ведущими нулями, напри- мер:
[me@linuxbox ~]$ echo {01..15} 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 [me@linuxbox ~]$ echo {001..15} 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015
В следующем примере используется диапазон символов в обратном порядке:
Подстановка
107
[me@linuxbox ~]$ echo {Z..A} Z Y X W V U T S R Q P O N M L K J I H G F E D C B A
Допускается вложение фигурных скобок:
[me@linuxbox ~]$ echo a{A{1,2},B{3,4}}b aA1b aA2b aB3b aB4b
Какую пользу можно извлечь из этого? Такая возможность может пригодиться для формирования списков файлов или каталогов, которые требуется создать. Например, фотограф, имеющий огромную коллекцию фотографий и желающий организовать ее по годам и месяцам, мог бы начать с создания группы каталогов с именами, состоящими из номера года и месяца. Благодаря этому имена ката- логов будут отсортированы в хронологическом порядке. Можно было бы ввести полный список каталогов, но это обременительно и чревато ошибками. Вместо этого выполним следующую команду:
[me@linuxbox ~]$ mkdir Photos [me@linuxbox ~]$ cd Photos [me@linuxbox Photos]$ mkdir {2007..2009}-{01..12} [me@linuxbox Photos]$ ls 2007-01 2007-07 2008-01 2008-07 2009-01 2009-07 2007-02 2007-08 2008-02 2008-08 2009-02 2009-08 2007-03 2007-09 2008-03 2008-09 2009-03 2009-09 2007-04 2007-10 2008-04 2008-10 2009-04 2009-10 2007-05 2007-11 2008-05 2008-11 2009-05 2009-11 2007-06 2007-12 2008-06 2008-12 2009-06 2009-12
Однако!
Подстановка параметров
В этой главе мы лишь кратко коснемся подстановки параметров, а детально об- судим ее позже. Эта возможность полезнее в сценариях на языке командной обо- лочки, чем непосредственно в командной строке. Многие из ее возможностей имеют отношение к способности системы хранить маленькие фрагменты данных и присваивать этим фрагментам имена. Многие такие фрагменты, правильнее их называть переменными, уже существуют и доступны для исследования. На- пример, переменная с именем USER хранит ваше имя пользователя. Подстановка параметра и получение содержимого переменной USER выполняется следующим образом:
[me@linuxbox ~]$ echo $USER me
108
Глава 7. Взгляд на мир глазами командной оболочки
Чтобы увидеть список доступных переменных, выполните следующую команду:
[me@linuxbox ~]$ printenv | less
Возможно, вы обратили внимание, что если в других вариантах подстановки до- пустить ошибку в шаблоне, подстановка не будет выполнена и команда echo про- сто выведет ошибочный шаблон. В случае с подстановкой параметров все иначе: если ошибиться в имени переменной, подстановка все равно будет выполнена, но результатом будет пустая строка:
[me@linuxbox ~]$ echo $SUER [me@linuxbox ~]$
Подстановка команд
Подстановка команд позволяет использовать поток вывода команд в качестве ар- гументов других команд:
[me@linuxbox ~]$ echo $(ls) Desktop Documents ls-output.txt Music Pictures Public Templates Videos
Один из моих любимых вариантов выглядит так:
[me@linuxbox ~]$ ls -l $(which cp) -rwxr-xr-x 1 root root 71516 2007-12-05 08:58 /bin/cp
Здесь результат команды which cp передается как аргумент команде ls, благодаря чему мы получаем информацию о программе cp, не зная полного пути к ней. Под- становка команд не ограничивается такими простыми командами. Можно исполь- зовать целые конвейеры (здесь показана только часть вывода):
[me@linuxbox ~]$ file $(ls -d /usr/bin/* | grep zip)
/usr/bin/bunzip2: symbolic link to bzip2' /usr/bin/bzip2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV ), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped /usr/bin/bzip2recover: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped /usr/bin/funzip: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped /usr/bin/gpg-zip: Bourne shell script text executable /usr/bin/gunzip: symbolic link to ../../bin/gunzip’
/usr/bin/gzip: symbolic link to ../../bin/gzip' /usr/bin/mzip: symbolic link to mtools'
В этом примере результаты конвейера превратились в список аргументов коман- ды file.
Экранирование
109
Механизм подстановки команд имеет альтернативный синтаксис, унаследо- ванный от более старых командных оболочек, который также поддерживается в bash. В нем вместо знака доллара и круглых скобок используются обратные апострофы:
[me@linuxbox ~]$ ls -l which cp
-rwxr-xr-x 1 root root 71516 2012-12-05 08:58 /bin/cp
Экранирование
Теперь, после знакомства с множеством способов подстановки, поддерживаемых командной оболочкой, можно начинать учиться управлять ими. Например, взгля- ните на эту команду:
[me@linuxbox ~]$ echo this is a test this is a test
Или на эту:
[me@linuxbox ~]$ echo Итого $100.00 Итого 00.00
В первом примере механизм разбиения на слова удалил дополнительные пробелы из списка аргументов команды echo. Во втором — механизм подстановки параме- тров подставил пустую строку вместо $1, потому что не нашел такую переменную. Командная оболочка предоставляет механизм, который называется экранирова- нием (quoting), для выборочного подавления нежелательной подстановки.
Двойные кавычки
Первый тип экранирования, который мы рассмотрим, — двойные кавычки. Если заключить текст в двойные кавычки, все специальные символы потеряют свое специальное значение и будут интерпретироваться как обычные символы. Ис- ключение составляют: $ (знак доллара), \ (обратный слеш) и ` (обратный апо- строф). То есть разбиение на слова, подстановка путей, подстановка тильды и подстановка фигурных скобок выполняться не будут, но подстановка параме- тров, подстановка значений арифметических выражений и подстановка команд все еще будут выполняться. Благодаря двойным кавычкам мы сможем обраба- тывать имена файлов с пробелами. Представьте, что мы по ошибке создали файл с именем Два слова.txt. Если попытаться использовать это имя в командной строке, механизм разбиения слов будет интерпретировать его как два отдельных аргумента:
110
Глава 7. Взгляд на мир глазами командной оболочки
[me@linuxbox ~]$ ls -l Два слова.txt ls: невозможно получить доступ к ‘Два’: Нет такого файла или каталога ls: невозможно получить доступ к ‘слова.txt’: Нет такого файла или каталога
Добавив двойные кавычки, можно запретить разбиение слов и получить желае- мый результат; кроме того, с помощью двойных кавычек мы исправим ошибку:
[me@linuxbox ~]$ ls -l “Два слова.txt” -rw-rw-r– 1 me me 18 2012-02-20 13:03 Два слова.txt [me@linuxbox ~]$ mv “Два слова.txt” Два_слова.txt
Вот так! Теперь не нужно вводить эти противные двойные кавычки.
Запомните: подстановка параметров, подстановка значений арифметических вы- ражений и подстановка команд все еще выполняются в двойных кавычках:
[me@linuxbox ~]$ echo “$USER $((2+2)) $(cal)” me 4 February 2020 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
Давайте отвлечемся и посмотрим, какой эффект оказывают двойные кавычки на подстановку команд. Сначала рассмотрим действие механизма разбиения на сло- ва. В одном из примеров, приведенных выше, мы видели, как механизм разбиения на слова удаляет дополнительные пробелы из текста:
[me@linuxbox ~]$ echo this is a test this is a test
По умолчанию этот механизм находит пробелы, символы табуляции и символы перевода строки и интерпретирует их как разделители слов. То есть вне кавычек упомянутые символы не считаются частью текста. Они являются лишь разделите- лями. Поскольку они делят слова на аргументы, получается, что в нашем примере командная строка состоит из команды и четырех аргументов. Однако если доба- вить двойные кавычки, разбиение на слова выполняться не будет и внутренние пробелы не будут считаться разделителями — они станут частью аргумента:
[me@linuxbox ~]$ echo “this is a test” this is a test
После добавления двойных кавычек командная строка будет состоять из команды и одного аргумента.
Экранирование
111
Тот факт, что символы перевода строки интерпретируются механизмом разбие- ния на слова как разделители, вызывает интересный и трудноуловимый эффект при подстановке команд. Взгляните:
[me@linuxbox ~]$ echo $(cal) February 2020 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [me@linuxbox ~]$ echo “$(cal)” February 2020 Su Mo Tu We Th Fr Sa 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
В первом случае подстановка команд без кавычек привела к созданию командной строки с 38 аргументами, а во втором случае получилась командная строка с од- ним аргументом, включающим внутренние пробелы и символы перевода строки.
Одиночные кавычки
Если вам требуется подавить все подстановки, используйте одиночные кавычки. Ниже для сравнения приводятся результаты неэкранированной команды и коман- ды, экранированной двойными и одиночными кавычками:
[me@linuxbox ~]$ echo text ~/.txt {a,b} $(echo foo) $((2+2)) $USER text /home/me/ls-output.txt a b foo 4 me [me@linuxbox ~]$ echo “text ~/.txt {a,b} $(echo foo) $((2+2)) $USER” text ~/.txt {a,b} foo 4 me [me@linuxbox ~]$ echo ’text ~/.txt {a,b} $(echo foo) $((2+2)) $USER’ text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
Как видите, каждый следующий уровень экранирования все больше и больше по- давляет подстановку.
Экранирование символов
Иногда бывает необходимо экранировать только один символ. Для этого доста- точно добавить перед символом обратный слеш, который в данном случае назы- вается экранирующим символом (escape character). Часто этот прием используется в двойных кавычках, чтобы выборочно предотвратить подстановку.
[me@linuxbox ~]$ echo “Баланс счета пользователя $USER: $5.00” Баланс счета пользователя me: $5.00
112
Глава 7. Взгляд на мир глазами командной оболочки
Экранирование символов также широко применяется для подавления специаль- ного значения символов в именах файлов. Например, в именах файлов допускает- ся использование символов, которые имеют специальное значение для командной оболочки. К их числу относятся: $, !, &, пробел и др. Чтобы включить специальный символ в имя файла, его достаточно экранировать, как показано ниже:
[me@linuxbox ~]$ mv bad&filename good_filename
Чтобы включить сам экранирующий символ, его также нужно экранировать, вве- дя \. Имейте в виду, что внутри одиночных кавычек обратный слеш теряет свое специальное значение и интерпретируется как обычный символ.
Управляющие последовательности
Обратный слеш используется не только в роли экранирующего символа, но и как часть специальных символов, которые называют управляющими кодами (control codes). Первые 32 символа в схеме кодирования ASCII использовались для передачи различных команд в устройствах, таких как телетайп. Некоторые из этих кодов хорошо знакомы вам (табуляция, забой, перевод строки и возврат каретки), тогда как другие — нет (пустой символ, конец передачи и подтверж- дение).
В табл. 7.2 перечислены некоторые наиболее известные управляющие последова- тельности.
Таблица 7.2. Управляющие последовательности
Управляющая после- довательность
Значение
\a
\b
\n
\r
\t
Звонок («предупреждение» — заставляет компьютер подать звуковой сигнал)
Забой (backspace)
Новая строка (в Unix-подобных системах этот символ выполняет перевод строки)
Возврат каретки
Табуляция
Идея использования обратного слеша зародилась в языке программирования C и была заимствована многими другими языками, включая язык командной обо- лочки.
Заключение
113
Параметр -e команды echo включает интерпретацию управляющих последова- тельностей. Их можно также заключать в конструкцию $’ ‘. Ниже демонстриру- ется использование команды sleep для создания элементарного таймера, простой программы, которая всего лишь ждет указанное число секунд и завершается.
sleep 10; echo -e “Time’s up\a”
То же самое можно выразить так:
sleep 10; echo “Time’s up” $’\a’
Заключение
По мере накопления опыта использования командной оболочки мы все чаще бу- дем использовать возможности подстановки и экранирования, поэтому важно хо- рошо понимать, как они работают. Фактически можно смело утверждать, что эти два механизма являются наиболее важными для изучения аспектами командной оболочки. Без надлежащего понимания того, как действует подстановка, команд- ная оболочка будет оставаться источником непонимания и домыслов, при этом многие ее возможности останутся неиспользованными.
8 Продвинутые приемы
работы с клавиатурой
Я часто шутливо описываю Unix как «операционную систему для тех, кто любит печатать». Казалось бы, сам факт наличия командной строки доказывает это. Но в действительности пользователи командной строки не любят печатать слишком много. Зачем, если есть так много команд с короткими именами, таких как cp, ls, mv и rm?
Фактически одной из самых заветных целей командной строки является умень- шение объема ввода — возможность выполнить большую часть работы всего не- сколькими нажатиями клавиш. Другая цель — не позволить рукам оторваться от клавиатуры и коснуться мыши. В этой главе мы рассмотрим возможности bash, увеличивающие скорость и эффективность использования клавиатуры.
Здесь будут представлены следующие команды:
clear — очищает экран;
history — выводит содержимое истории команд.
Редактирование командной строки
Для поддержки операций редактирования командной строки bash использует би- блиотеку (коллекцию подпрограмм, которую могут использовать разные програм- мы) с именем Readline. Мы уже видели некоторые из них. Например, нам знако- мы клавиши со стрелками влево и вправо, перемещающие курсор, но существует еще целое множество других операций. Рассматривайте их как дополнительные
Редактирование командной строки
115
инструменты, которые можно использовать в работе. Необязательно стремиться изучить их все, но многие из них весьма практичны. Выбирайте те, что вам по- нравятся.
ПРИМЕЧАНИЕ
Некоторые комбинации клавиш, описываемые далее (особенно те, что включают клавишу ALT), могут перехватываться графическим интерфейсом и использовать- ся для выполнения других функций. Однако все комбинации без исключения должны правильно работать в виртуальной консоли.
Перемещение курсора
В табл. 8.1 перечислены комбинации клавиш, используемые для перемещения курсора.
Таблица 8.1. Команды перемещения курсора
Клавиша Действие
CTRL+A
Перемещает курсор в начало строки
CTRL+E
Перемещает курсор в конец строки
CTRL+F
CTRL+B
Перемещает курсор на один символ вперед; действует так же, как клавиша со стрелкой вправо
Перемещает курсор на один символ назад; действует так же, как клавиша со стрелкой влево
ALT+F
Перемещает курсор на одно слово вперед
ALT+B
Перемещает курсор на одно слово назад
CTRL+L
Очищает экран и устанавливает курсор в левый верхний угол. То же самое делает команда clear
Изменение текста
Вводя команды, легко допустить ошибку, поэтому нам нужен способ, позволяю- щий быстро исправлять их. В табл. 8.2 перечислены комбинации клавиш для ре- дактирования символов в командной строке.
116
Глава 8. Продвинутые приемы работы с клавиатурой
КЛАВИША META
Отважившиеся заглянуть в документацию к Readline, которая находится в разделе «READLINE», на странице справочного руководства (man) для bash, столкнутся с термином клавиша meta (meta key). На современных клавиатурах ей соответ- ствует клавиша ALT, но так было не всегда.
В стародавние времена (до появления IBM-совместимых персональных ком- пьютеров, но после появления Unix) персональные компьютеры не были так широко распространены. Иногда их заменяли устройства, называемые терми- налами. Терминал — это коммуникационное устройство с текстовым дисплеем и клавиатурой, имеющее внутри столько электроники, сколько необходимо для отображения символов и перемещения курсора. Терминалы подключались (обычно посредством последовательного кабеля) к большому компьютеру или коммуникационной сети большого компьютера. В то время существовало очень много различных терминалов, имевших разные клавиатуры и дисплеи с разными функциональными возможностями. Так как все они поддерживали как минимум набор символов ASCII, разработчикам программного обеспечения, пишущим переносимые приложения, необходимо было прийти к общему знаменателю. В системах Unix применяется очень сложный способ использования терминалов и их разнообразных возможностей. Поскольку разработчики Readline не были уверены в наличии специализированной управляющей клавиши, они изобрели ее и назвали meta. На современных клавиатурах роль клавиши meta играет ALT, однако если вы все еще используете терминал (до сих пор поддерживаются в Linux!), можно просто нажать и отпустить клавишу ESC, и вы получите эффект нажатия и удержания клавиши ALT.
Таблица 8.2. Команды редактирования текста
Клавиша Действие
CTRL+D
Удаляет символ в позиции курсора
CTRL+T
Меняет местами два символа — в позиции курсора и предшествующий ему
ALT+T
Меняет местами два слова — в позиции курсора и предшествующее ему
ALT+L
ALT+U
Переводит в нижний регистр символы, начиная с символа в позиции курсора и до конца слова
Переводит в верхний регистр символы, начиная с символа в позиции курсора и до конца слова
Дополнение
117
Вырезание и вставка (удаление и возврат) текста
В документации к Readline используется термин killing and yanking (удаление и возврат), обозначающий операцию, которую обычно называют вырезанием и вставкой (cutting and pasting). В табл. 8.3 перечислены комбинации клавиш, вы- полняющие вырезание и вставку. Вырезанные элементы сохраняются в кольце- вом буфере, который называется kill-ring (кольцо удалений).
Таблица 8.3. Команды вырезания и вставки
Клавиша
Действие
CTRL+K
CTRL+U
ALT+D
Удаляет символы от позиции курсора до конца строки
Удаляет символы от позиции курсора до начала строки
Удаляет символы от позиции курсора до конца текущего слова
ALT+BACKSPACE
Удаляет символы от позиции курсора до начала текущего слова. Если курсор находится в начале слова, удаляется предшествующее слово
CTRL+Y
Извлекает текст из кольцевого буфера удалений и вставляет его в по- зицию курсора
Дополнение
Другой вариант помощи пользователям реализован в командной оболочке в виде механизма дополнения (completion). Дополнение происходит, когда в процессе ввода команды нажимается клавиша TAB. Давайте посмотрим, как это работает. Допустим, что ваш домашний каталог содержит следующее:
[me@linuxbox ~]$ ls Desktop ls-output.txt Pictures Templates Videos Documents Music Public
Попробуйте ввести следующую строку, но не нажимайте клавишу ENTER:
[me@linuxbox ~]$ ls l
Теперь нажмите клавишу TAB:
[me@linuxbox ~]$ ls ls-output.txt
Обратили ли вы внимание, как командная оболочка дополнила командную строку за вас? Попробуйте теперь набрать следующую строку — и снова не нажимайте ENTER):
[me@linuxbox ~]$ ls D
118
Глава 8. Продвинутые приемы работы с клавиатурой
Нажмите TAB:
[me@linuxbox ~]$ ls D
Дополнения не произошло — просто прозвучал звуковой сигнал. Так получилось потому, что символу D соответствует более одного элемента в каталоге. Чтобы ко- мандная оболочка дополнила вашу строку, предложенная вами «подсказка» долж- на иметь однозначное продолжение. Попробуйте продолжить ввод:
[me@linuxbox ~]$ ls Do
Затем нажмите TAB:
[me@linuxbox ~]$ ls Documents
Дополнение произошло.
Этот пример демонстрирует дополнение путей как наиболее частый случай ис- пользования дополнения. Однако дополнение также работает с именами перемен- ных (когда слово начинается с символа $), именами пользователей (когда слово начинается с символа ~), командами (когда дополняемое слово является первым в командной строке) и сетевыми именами компьютеров (когда слово начинается с символа @). Дополнение сетевых имен компьютеров действует только в отноше- нии имен, перечисленных в /etc/hosts.
С механизмом дополнения связано несколько управляющих комбинаций клавиш (табл. 8.4).
Таблица 8.4. Команды дополнения
Клавиша
Действие
ALT+?
ALT+*
Выводит список возможных дополнений. В большинстве систем тот же эффект можно получить, нажав клавишу TAB второй раз, что намного проще
Вставит все возможные дополнения. Это пригодится в том случае, если требуется использовать больше одного возможного варианта дополнения
Существует еще несколько команд, смысл которых для меня не совсем ясен. Пол- ный список вы сможете найти на странице справочного руководства (man) для bash, в разделе «READLINE».
Использование истории
119
ПРОГРАММИРУЕМОЕ ДОПОЛНЕНИЕ
Последние версии bash реализуют механизм программируемого дополнения. Программируемое дополнение дает возможность добавлять дополнительные правила. Обычно это делается с целью добавить поддержку определенных при- ложений. Например, можно добавить дополнение списка параметров команды или файлов определенного типа, поддерживаемых приложением. В Ubuntu определено огромное множество таких правил. Программируемое дополнение реализуется посредством функций командной оболочки — небольших мини-сценариев, о кото- рых будет рассказываться в следующих главах. Если вам любопытно, попробуйте выполнить команду
set | less
и вы увидите их. Однако не все дистрибутивы включают эти функции по умолчанию.
Использование истории
Как рассказывалось в главе 1, bash поддерживает историю вводившихся команд. Этот список команд хранится в домашнем каталоге, в файле с именем .bash_history. Механизм истории помогает уменьшить объем ручного ввода, особенно в сочета- нии с командами редактирования командной строки.
Поиск в истории
Просмотреть содержимое истории можно в любой момент с помощью команды:
[me@linuxbox ~]$ history | less
По умолчанию bash хранит последние 500 введенных команд, однако в большин- стве современных дистрибутивов этот предел увеличен до 1000. Как изменить это значение, мы узнаем в главе 11. А теперь представим, что вам понадобилось най- ти команды, использовавшиеся для получения списка содержимого /usr/bin. Вот один из возможных способов:
[me@linuxbox ~]$ history | grep /usr/bin
А теперь представим, что среди результатов нужно выбрать запись с интересую- щей вас командой:
88 ls -l /usr/bin > ls-output.txt
120
Глава 8. Продвинутые приемы работы с клавиатурой
Здесь число 88 — это порядковый номер записи команды в списке истории. Зная это число, можно воспользоваться еще одной разновидностью подстановки, ко- торая называется подстановкой записей истории (history expansion). Для этого введите:
[me@linuxbox ~]$ !88
и bash заменит !88 содержимым 88-й записи в списке истории. Подробнее об этой форме подстановки записей истории мы поговорим чуть ниже.
bash также дает возможность выполнять поступательный поиск в списке истории. Это означает, что bash может выполнять поиск в списке истории по мере ввода символов, уточняя результаты с вводом каждого нового символа. Чтобы запу- стить поступательный поиск, нажмите комбинацию CTRL+R и введите искомый текст. Закончив поиск, нажмите ENTER, чтобы выполнить команду, или CTRL+J, что- бы скопировать запись из списка истории в текущую командную строку. Чтобы найти следующее вхождение текста (переместиться «вверх» по списку истории), нажмите CTRL+R еще раз. Чтобы завершить поиск, нажмите CTRL+G или CTRL+C. Следующий пример демонстрирует, как действует поиск:
[me@linuxbox ~]$
Первое нажатие комбинации CTRL+R:
(reverse-i-search)`’:
Приглашение к вводу изменится, показывая, что выполняется поступательный поиск в обратном порядке. Под словами «в обратном порядке» подразумевается, что поиск выполняется от «текущего момента» до некоторого момента в прошлом. Далее мы начинаем ввод искомого текста, в данном примере /usr/bin:
(reverse-i-search)`/usr/bin’: ls -l /usr/bin > ls-output.txt
Механизм поиска сразу же возвращает результат. Теперь, чтобы выполнить най- денную команду, необходимо нажать ENTER, или вы можете скопировать команду в командную строку для дальнейшего редактирования, нажав CTRL+J. Давайте ско- пируем ее. Нажмите CTRL+J:
[me@linuxbox ~]$ ls -l /usr/bin > ls-output.txt
Механизм поиска вернет управление, командная строка заполнится и будет гото- ва для выполнения!
В табл. 8.5 перечислены некоторые комбинации клавиш, используемые для мани- пуляций со списком истории команд.
Использование истории
121
Таблица 8.5. Команды для работы с историей
Клавиша
Действие
CTRL+P
CTRL+N
Переход к предыдущей записи в истории. Действует так же, как клавиша со стрелкой вверх
Переход к следующей записи в истории. Действует так же, как клавиша со стрелкой вниз
ALT+
Переход в конец (к последней записи) списка истории
CTRL+R
ALT+P
Инкрементальный поиск в обратном порядке. Поиск выполняется поступа- тельно, от текущей записи вверх по списку истории
Поиск в обратном порядке, не инкрементальный. При использовании этого вида поиска введите искомую строку и нажмите ENTER, и только после этого будет выполнен фактический поиск
ALT+N
Поиск в прямом порядке, не поступательный
CTRL+O
Выполните текущую команду в списке истории и перейдите к следующей. Эту комбинацию удобно использовать, если требуется повторно выполнить последовательность команд из списка истории
Подстановка записей истории
Командная оболочка поддерживает специализированный вид подстановки — подстановку записей из списка истории при использовании символа !. Мы уже видели, как восклицательный знак, сопровождаемый числом, замещается запи- сью из списка истории. Этот вид подстановки имеет несколько разновидностей (табл. 8.6).
Таблица 8.6. Команды механизма подстановки записей истории
Последовательность
Действие
!!
!число
!строка
!?строка
Повторяет последнюю команду. Проще, пожалуй, нажать клави- шу со стрелкой вверх и ENTER
Повторяет команду из записи с указанным номером
Повторяет последнюю команду в списке истории, начинающуюся с указанной строки
Повторяет последнюю команду в списке истории, содержащую указанную строку
122
Глава 8. Продвинутые приемы работы с клавиатурой
Не используйте формы !строка и !?строка, если только вы абсолютно точно не знаете содержимого записей в списке истории.
Механизм подстановки записей истории поддерживает также другие комбина- ции, но эта тема становится слишком запутанной, и мы не станем перегружать себя лишней информацией. Желающие смогут обратиться к странице справочно- го руководства (man) для bash, в разделе «HISTORY EXPANSION». Загляните туда!
SCRIPT
В дополнение к истории команд в bash большинство дистрибутивов Linux вклю- чают программу script, которую можно использовать для записи в файлы целых сеансов работы с командной оболочкой. Базовый синтаксис команды:
script [файл]
где файл — это имя файла для записи. Если файл не будет указан, сохранение сеанса будет произведено в файл typescript. Полное описание параметров и возможностей программы можно найти на странице справочного руководства (man) для script.
Заключение
В этой главе мы рассмотрели несколько приемов работы с клавиатурой, поддер- живаемых командной оболочкой, с целью помочь истинным фанатам клавиатуры уменьшить объем работы. Я думаю, что потом, когда вы сроднитесь с командной строкой, вы сможете обратиться к этой главе, чтобы вспомнить описанные здесь приемы. А пока будем считать их необязательными, но потенциально полезными.
9 Привилегии
Операционные системы, следующие традициям Unix, отличаются от систем, сле- дующих традициям MS-DOS, тем, что являются не только многозадачными, но и многопользовательскими.
Что это означает на самом деле? Это означает, что компьютером могут одновре- менно пользоваться несколько человек. Несмотря на то что обычно компьютер имеет всего одну клавиатуру и монитор, это обстоятельство не мешает совмест- ному пользованию. Например, если компьютер подключен к локальной сети или к Интернету, удаленные пользователи смогут зайти на него через ssh (secure shell — безопасная командная оболочка) и выполнять операции. Фактически уда- ленные пользователи могут запускать приложения с графическим интерфейсом и получать изображение на удаленном дисплее. X Window System поддерживает такую возможность изначально.
Поддержка многопользовательского режима работы — не недавнее «изобретение» Linux, а возможность, глубоко внедренная в архитектуру операционной системы. Учитывая окружение, в котором создавалась система Unix, это имело определен- ный смысл. В те времена, когда компьютеры еще не были «персональными», они были большими и дорогими. Типичная компьютерная система университета, на- пример, состояла из большого центрального компьютера в одном здании и терми- налов, разбросанных по всему университетскому городку и соединенных с боль- шим центральным компьютером. Компьютер мог одновременно обслуживать множество пользователей.
Чтобы подобная возможность имела практическую ценность, необходим способ определенной «изоляции» пользователей друг от друга. В конце концов, действия рядового пользователя не должны приводить к аварийному завершению работы компьютера, и ни один пользователь не должен иметь возможность вносить из- менения в файлы, принадлежащие другому пользователю.
124
Глава 9. Привилегии
В данной главе мы рассмотрим эту важную сторону безопасности системы и по- знакомимся со следующими командами:
id — выводит информацию об идентичности пользователя;
chmod — изменяет режим доступа к файлу;
umask — определяет разрешения доступа к файлам по умолчанию;
su — запускает командную оболочку от имени другого пользователя;
sudo — выполняет команду от имени другого пользователя;
chown — изменяет владельца файла;
chgrp — изменяет группу файла;
passwd — изменяет пароль пользователя.
Владельцы, члены группы и все остальные
Знакомясь с системой в главе 4, вы уже сталкивались со следующей проблемой при исследовании файлов, таких как /etc/shadow:
[me@linuxbox ~]$ file /etc/shadow /etc/shadow: Обычный файл, нет прав на чтение [me@linuxbox ~]$ less /etc/shadow /etc/shadow: Отказано в доступе
Причина этого сообщения об ошибке заключается в том, что обычные пользовате- ли не имеют права читать этот файл.
В модели безопасности Unix пользователь может владеть файлами и каталогами. Если пользователь владеет файлом или каталогом, он может управлять доступом к нему. Пользователи могут также принадлежать группе, состоящей из одного или нескольких пользователей, и получить права доступа к файлам и каталогам для членов группы, которые определяются владельцами. Кроме прав доступа для группы, владелец может также определить некоторые права доступа для всех остальных, их в терминологии Unix называют мир (world). Получить информа- цию о своей идентичности можно с помощью команды id:
[me@linuxbox ~]$ id uid=500(me) gid=500(me) groups=500(me)
Владельцы, члены группы и все остальные
125
Давайте рассмотрим этот вывод. Когда создается учетная запись пользовате- ля, ей присваивается число, которое называют идентификатором пользователя (user ID), или uid. Это число, исключительно ради удобства человека, отобра- жается как имя пользователя. Пользователю назначается идентификатор ос- новной группы (primary group ID), или gid, и дополнительно пользователь мо- жет включаться в состав других групп. Предыдущий пример взят из системы Fedora. В других системах, таких как Ubuntu, вывод команды может немного отличаться.
[me@linuxbox ~]$ id uid=1000(me) gid=1000(me) groups=4(adm),20(dialout),24(cdrom),25(floppy),29(audio), 30(dip),44(video), 46(plugdev),108(lpadmin),114(admin),1000(me)
Как видите, в Ubuntu числа uid и gid отличаются. Это объясняется тем, что в Fedora нумерация учетных записей обычных пользователей начинается с 500, тогда как в Ubuntu — с 1000. Кроме того, пользователь в Ubuntu принадлежит множеству других групп. Это связано с особенностями управления привилегиями доступа к системным устройствам и службам в Ubuntu.
А где же вся эта информация хранится? Как и многое другое в Linux, она хра- нится в паре текстовых файлов. Учетные записи пользователей хранятся в фай- ле /etc/passwd, а информация о группах — в файле /etc/group. Когда создаются новые учетные записи и группы, эти файлы изменяются вместе с файлом /etc/ shadow, где хранится информация о пароле пользователя. Для каждой учетной за- писи в файле /etc/passwd определяется имя пользователя (для входа), числовой идентификатор пользователя (uid), числовой идентификатор основной группы (gid), действительное имя пользователя, путь к домашнему каталогу и команд- ная оболочка входа (login shell). Заглянув внутрь /etc/passwd и /etc/group, можно заметить, что помимо учетных записей обычных пользователей здесь также хра- нятся учетные записи суперпользователя (uid 0) и различных других системных пользователей.
В следующей главе, где рассказывается о процессах, вы узнаете, что некоторые из этих других «пользователей» в действительности существуют не просто так.
Несмотря на то что во многих Unix-подобных системах обычных пользователей включают в общую группу, такую как users, в современных дистрибутивах Linux принято создавать для каждого пользователя свою, уникальную группу с одним членом и именем, совпадающим с именем пользователя. Это упрощает распреде- ление определенных типов привилегий.
126
Глава 9. Привилегии
Чтение, запись и выполнение
Права доступа к файлам и каталогам определяются в терминах права на чтение, права на запись и права на выполнение. Если взглянуть на вывод команды ls, можно увидеть некоторые подсказки о том, как эти права реализованы:
[me@linuxbox ~]$ > foo.txt [me@linuxbox ~]$ ls -l foo.txt -rw-rw-r– 1 me me 0 2018-03-06 14:52 foo.txt
Первые 10 символов в выводе — это атрибуты файла. Первый из этих символов определяет тип файла. В табл. 9.1 перечислены типы файлов, которые чаще все- го встречаются на практике (существуют также другие, реже используемые типы файлов).
Таблица 9.1. Типы файлов
Атрибут
Тип файла
d
l
c
b
Обычный файл
Каталог
Символическая ссылка. Обратите внимание, что для символических ссылок все остальные атрибуты имеют значение rwxrwxrwx и не отражают действи- тельные права доступа. Фактические права доступа к файлу определяются атрибутами самого файла, на который указывает символическая ссылка
Специальный файл символьного устройства. Файлы этого типа соответ- ствуют устройствам, таким как терминал или модем, которые обрабатывают данные как потоки байтов
Специальный файл блочного устройства. Файлы этого типа соответствуют устройствам, таким как привод жесткого диска или CD-ROM, которые обраба- тывают данные блоками
Остальные девять символов в атрибутах файла называются режимом доступа к файлу и представляют права на чтение, запись и выполнение для владельца фай- ла, группы — владельца файла и всех остальных.
Владелец
Группа
rwx
rwx
Мир
rwx
Установленные атрибуты режима r, w и x оказывают определенное влияние на файлы и каталоги, как показано в табл. 9.2.
Чтение, запись и выполнение
127
Таблица 9.2. Атрибуты прав доступа
Атрибут Файлы
Каталоги
r
w
x
Разрешается открывать и читать содержимое файла
Разрешается записывать в файл или усекать его; однако этот атрибут не дает права пере- именовывать и удалять файлы. Возможность переименования и удаления файлов опреде- ляется атрибутами вмещающего каталога
Разрешается интерпретировать файл как программу и выполнять ее. Файлы, содержа- щие программы на языках сценариев, допол- нительно должны быть доступны для чтения, иначе они не будут выполняться
Разрешается читать содер- жимое каталога, если вместе с этим атрибутом установлен атрибут права на выполнение
Разрешается создавать, удалять и переименовывать файлы внутри каталога, если вместе с этим атрибутом установлен атрибут права на выполнение
Разрешается входить в ката- лог, то есть выполнять коман- ду cd для перехода в него
В табл. 9.3 приводится несколько примеров установки атрибутов файлов.
Таблица 9.3. Примеры установки атрибутов прав доступа к файлам
Атрибуты файлов Значение
-rwx——
-rw——-
-rw-r–r–
-rwxr-xr-x
-rw-rw—-
Lrwxrwxrwx
Обычный файл, доступный владельцу для чтения, записи и выпол- нения. Никто другой не имеет прав доступа к файлу
Обычный файл, доступный владельцу для чтения и записи. Никто другой не имеет прав доступа к файлу
Обычный файл, доступный владельцу для чтения и записи. Чле- ны группы имеют право читать файл. Все остальные имеют право читать файл
Обычный файл, доступный владельцу для чтения, записи и выпол- нения. Все остальные имеют право читать и выполнять файл
Обычный файл, доступный для чтения и записи только владельцу и членам группы
Символическая ссылка. Все символические ссылки имеют недействи- тельные значения атрибутов. Фактические права доступа к файлу определяются атрибутами самого файла, на который указывает символическая ссылка
128
Глава 9. Привилегии
Таблица 9.3 (окончание)
Атрибуты файлов Значение
drwxrwx—
drwxr-x—
Каталог. Владелец и члены группы могут входить в каталог, созда- вать, переименовывать и удалять файлы внутри каталога
Каталог. Владелец может входить в каталог, создавать, переиме- новывать и удалять файлы внутри каталога. Члены группы могут входить в каталог, но не могут создавать, переименовывать и уда- лять файлы внутри каталога
chmod — изменение режима доступа к файлу
Для изменения режима (прав) доступа к файлу или каталогу используется коман- да chmod. Имейте в виду, что права доступа к файлу или каталогу может изменить только владелец или суперпользователь. Команда chmod поддерживает два разных способа изменения режима:
с использованием восьмеричных чисел;
с использованием символического представления.
Сначала рассмотрим использование восьмеричных чисел. При использовании восьмеричной формы записи шаблон желаемых привилегий определяется вось- меричными числами. Так как каждая цифра в восьмеричном числе определяется тремя двоичными разрядами, она точно укладывается в схему хранения режима доступа к файлу. В табл. 9.4 поясняется, что мы имеем в виду.
Таблица 9.4. Режимы доступа к файлу в двоичном и восьмеричном представлениях
Восьмеричное
Двоичное
Режим доступа
0
1
2
3
4
5
6
7
000
001
010
011
100
101
110
111
–x
-w-
-wx
r–
r-x
rw-
rwx
Чтение, запись и выполнение
129
А ПОЧЕМУ ИМЕННО ВОСЬМЕРИЧНОЕ?
Восьмеричная (по основанию 8) и родственная ей шестнадцатеричная (по основанию 16) системы счисления часто используются для представления чисел в компьютерах. Мы, люди, рождаемся с десятью пальцами на руках (по крайней мере большинство из нас), поэтому для счета используем систему счисления с основанием 10. Компьютеры, напротив, рождаются с одним пальцем и потому используют для вычисления двоичную систему счисления (по основанию 2). Их числа состоят всего из двух цифр, нуля и единицы. Поэтому в двоичной системе счет выглядит так:
0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011…
В восьмеричной системе используются цифры от нуля до семи:
0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 20, 21…
В шестнадцатеричной системе используются цифры от нуля до девяти плюс буквы от A до F:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12, 13…
В двоичной системе счисления еще можно увидеть смысл (поскольку компьютеры имеют лишь один палец), но в чем польза восьмеричной и шестнадцатеричной систем счисления? Они были придуманы для удобства человека. Очень часто небольшие порции данных представляются в компьютерах битовыми шаблонами. Примером может служить представление цвета в формате RGB. В большинстве дисплеев компьютеров цвет каждого пиксела определяется тремя цветовыми составляющими: 8 бит для красного цвета, 8 бит для зеленого и 8 бит для синего. Красивый сине-голубой цвет можно представить в виде 24-разрядного числа:
010000110110111111001101.
Хотели бы вы видеть и читать такие числа весь день? Я так не думаю. Именно в таких случаях на выручку приходят другие системы счисления. Каждая цифра в шестнадцатеричной системе счисления представляет четыре двоичные цифры. В восьмеричной системе каждой цифре соответствуют три двоичные цифры. То есть 24-значное значение сине-голубого цвета можно сжать до 6-значного шестнадцатеричного числа: 436FCD. Поскольку цифры в шестнадцатеричных числах «выстраиваются в ряд» с битами в двоичных числах, можно заметить, что красный компонент нашего цвета имеет значение 43, зеленый — 6F и си- ний — CD.
В наше время шестнадцатеричная форма записи получила большее распростра- нение, чем восьмеричная, но, как будет показано ниже, восьмеричная форма записи все еще оказывается весьма полезной для представления групп из трех двоичных битов.
130
Глава 9. Привилегии
C помощью трех восьмеричных цифр мы можем определить режим доступа к фай- лу для владельца, для группы и для остального мира.
[me@linuxbox ~]$ > foo.txt [me@linuxbox ~]$ ls -l foo.txt -rw-rw-r– 1 me me 0 2018-03-06 14:52 foo.txt [me@linuxbox ~]$ chmod 600 foo.txt [me@linuxbox ~]$ ls -l foo.txt -rw——- 1 me me 0 2018-03-06 14:52 foo.txt
Передав аргумент 600, мы установили права для владельца, позволяющие ему читать данные из файла и записывать их в файл, и при этом отобрали все права у группы и остального мира. Несмотря на кажущееся неудобство необходимости запоминания соответствий между восьмеричными и двоичными представления- ми, вам, скорее всего, придется использовать лишь несколько наиболее популяр- ных шаблонов: 7 (rwx), 6 (rw-), 5 (r-x), 4 (r–) и 0 (—).
Команда chmod поддерживает также символическую форму определения режимов доступа к файлу. Символическая форма записи делится на три части:
для кого устанавливаются разрешения;
какие операции с разрешениями будут выполняться;
на какие разрешения эти операции будут влиять.
Чтобы указать, для кого устанавливаются разрешения, используется комбинация символов u, g, o и a, как показано в табл. 9.5.
Таблица 9.5. Символическая форма записи аргументов команды chmod
Символ
Значение
u
g
o
a
Сокращенно от user (пользователь), означает владельца файла или каталога
Группа
Сокращенно от others (другие, остальные), означает весь остальной мир
Сокращенно от all (все); комбинация из всех трех символов: u, g и o
Если не указан ни один символ, предполагается a (all — все). Операцией может быть знак +, соответствующий добавлению заданных разрешений, знак -, соответ- ствующий отъему заданных разрешений, или знак =, указывающий, что только за- данные разрешения должны быть установлены, а все остальные отобраны.
Чтение, запись и выполнение
131
Разрешения определяются символами r, w и x. В табл. 9.6 перечислены некоторые примеры символической формы записи.
Таблица 9.6. Примеры символической формы записи прав доступа к файлам
Атрибуты файлов
Значение
u+x
u-x
+x
o-rw
go=rw
u+x,go=rx
Добавляет право на выполнение, но только для владельца
Отнимает право на выполнение у владельца
Добавляет право на выполнение для владельца, группы и осталь- ного мира. Эквивалент записи a+x
Отнимает право на чтение и запись у всех, кроме владельца и группы
Устанавливает право на чтение и запись для всех, кроме владель- ца. Если прежде файл имел разрешение на выполнение для группы и всего мира, это право отнимается
Добавляет право на выполнение для владельца и устанавливает право на чтение и выполнение для группы и всего мира. При вы- полнении сразу нескольких операций с привилегиями они должны разделяться запятой
Кто-то предпочитает пользоваться восьмеричной формой записи, кому-то больше нравится символическая. Символическая форма записи удобна тем, что позволя- ет установить единственный атрибут, не влияя на остальные.
Дополнительную информацию и полный список параметров команды chmod мож- но найти на странице справочного руководства (man). А теперь несколько слов о параметре –recursive: он воздействует и на файлы, и на каталоги, поэтому он не так полезен, как можно было бы предположить, потому что редко требуется устанавливать одинаковые разрешения для файлов и каталогов.
Установка режима доступа к файлу с помощью графического интерфейса
Теперь, ознакомившись с тем, как устанавливаются разрешения для файлов и ка- талогов, вы лучше поймете диалоги установки разрешений в графическом интер- фейсе. В Files (GNOME) и Dolphin (KDE) можно щелкнуть правой кнопкой мыши на файле или на каталоге и вывести диалог со свойствами. На рис. 9.1 изображен такой диалог из GNOME. Здесь вы видите, какие разрешения установлены для владельца, группы и остального мира.
132
Глава 9. Привилегии
Рис. 9.1. Диалог с правами доступа к файлу в GNOME
umask — определение разрешений доступа к файлам по умолчанию
Команда umask определяет разрешения по умолчанию, которые устанавливаются для файла при его создании. В ней с помощью восьмеричной формы записи опре- деляется битовая маска для сбрасываемых атрибутов режима доступа. Взгляните:
[me@linuxbox ~]$ rm -f foo.txt [me@linuxbox ~]$ umask 0002 [me@linuxbox ~]$ > foo.txt [me@linuxbox ~]$ ls -l foo.txt -rw-rw-r– 1 me me 0 2018-03-06 14:53 foo.txt
Сначала мы удалили существующий файл foo.txt, чтобы, так сказать, начать с чи- стого листа. Далее мы выполнили команду umask без аргумента, чтобы увидеть текущее значение маски. Она вернула нам значение 0002 (часто также использует- ся значение 0022) — восьмеричное представление действующей маски. Затем мы создали новый файл foo.txt и вывели для него разрешения.
Как видите, владелец и группа получили права на чтение и запись, тогда как все остальные — только право на чтение. Весь мир не получил права на запись из-за значения маски. Давайте повторим пример, но на этот раз определим свою маску:
Чтение, запись и выполнение
133
[me@linuxbox ~]$ rm foo.txt [me@linuxbox ~]$ umask 0000 [me@linuxbox ~]$ > foo.txt [me@linuxbox ~]$ ls -l foo.txt -rw-rw-rw- 1 me me 0 2018-03-06 14:58 foo.txt
После установки маски в значение 0000 (таким способом мы фактически выклю- чили ее) вновь созданный файл получил разрешение на запись для всего мира. Чтобы лучше понять суть происходящего, мы снова должны вернуться к восьме- ричным числам. Если развернуть маску в двоичное представление и сравнить ее с двоичным представлением атрибутов, можно понять, что произошло:
Исходный режим доступа к файлу
Маска
Результат
— rw- rw- rw-
000 000 000 010
— rw- rw- r–
Забудем пока про начальные нули (мы вернемся к ним чуть позже) и обратим внимание, что атрибут, соответствующий той позиции, где в маске стоит 1, был сброшен, — в данном случае право на запись для всего мира. Теперь понятно, что делает маска. В любой позиции, где в маске появляется 1, соответствующий атри- бут сбрасывается. Если посмотреть на значение маски 0022, легко увидеть, что оно делает:
Исходный режим доступа к файлу
Маска
Результат
— rw- rw- rw-
000 000 010 010
— rw- r– r–
И снова атрибуты, соответствующие позициям, где в маске стоит 1, были сбро- шены. Поэкспериментируйте с другими значениями (попробуйте несколько 7), чтобы лучше усвоить, как действует маска. Закончив эксперименты, не забудьте все вернуть в исходное состояние:
[me@linuxbox ~]$ rm foo.txt; umask 0002
Вам редко придется изменять маску, потому что значение по умолчанию, уста- навливаемое дистрибутивом, прекрасно подходит для большинства нужд. Однако в некоторых ситуациях, требующих повышенной безопасности, маску может по- надобиться изменить.
134
Глава 9. Привилегии
Некоторые специальные разрешения
Обычно разрешения в восьмеричном представлении мы видим как трехзначные числа, но технически более правильно выражать их четырехзначными числами. Почему? Потому что в дополнение к разрешениям на чтение, запись и выполне- ние существует еще несколько редко используемых разрешений.
Первый атрибут — бит setuid (восьмеричное значение 4000). Если это разреше- ние применяется к выполняемому файлу, в качестве эффективного идентифика- тора пользователя для процесса устанавливается не идентификатор реального пользователя (пользователя, фактически запустившего программу), а иденти- фикатор владельца программы. Чаще этот бит устанавливается для программ, владельцем которых является суперпользователь. Когда обычный пользователь запускает программу с установленным битом setuid, принадлежащую пользова- телю root, программа выполняется с эффективными привилегиями суперполь- зователя. Это дает возможность программам обращаться к файлам и каталогам, недоступным для обычного пользователя. Очевидно, что из-за возникающих проблем безопасности число таких программ в системе должно быть сведено к минимуму.
Второй редко используемый атрибут — бит setgid (восьмеричное значение 2000). По аналогии с битом setuid он устанавливает эффективный идентификатор груп- пы для процесса, выбирая вместо идентификатора группы реального пользователя группу владельца файла. Если установить бит setgid для каталога, вновь создава- емые файлы в этом каталоге будут принадлежать группе владельца каталога, а не группе владельца файла, создавшего его. Это разрешение может пригодиться для установки на каталоги, содержимое которых должно быть доступно всем членам основной группы владельца каталога, независимо от принадлежности к основной группе владельца файла.
Третий атрибут называется битом sticky (восьмеричное значение 1000). Это пере- житок, оставшийся от первых версий Unix, которые предоставляли возможность пометить выполняемый файл как «невытесняемый». Linux игнорирует бит sticky у файлов, но если установить его для каталога, он не позволит пользователю уда- лять или переименовывать файлы, если только пользователь не является владель- цем каталога, владельцем файла или суперпользователем. Это разрешение часто применяется для управления доступом к общим каталогам, таким как /tmp.
Ниже приводится несколько примеров использования chmod с символической формой определения этих специальных разрешений. Первый пример — установка бита setuid на файл программы:
chmod u+s program
Изменение идентичности
135
Далее — установка бита setgid на каталог:
chmod g+s dir
Наконец, установка бита sticky на каталог:
chmod +t dir
Специальные разрешения мы видим в выводе команды ls. Ниже приводится не- сколько примеров. Первый — программа с битом setuid:
-rwsr-xr-x
Теперь — каталог с атрибутом setgid:
drwxrwsr-x
Наконец, каталог с битом sticky:
drwxrwxrwt
Изменение идентичности
Время от времени возникает необходимость приобрести идентичность другого пользователя. Обычно это требуется для получения привилегий суперпользо- вателя, чтобы выполнить некоторые административные задачи, но точно так же можно «превратиться» в другого обычного пользователя, чтобы, к примеру, про- верить настройки учетной записи. Существует три способа приобрести альтерна- тивную идентичность:
- Выйти из системы и войти вновь с учетными данными другого пользователя.
- Воспользоваться командой su.
- Воспользоваться командой sudo.
Мы пропустим первый способ, потому что уже знаем, как им воспользоваться, и он не так удобен, как два других. В рамках сеанса работы с командной оболоч- кой команда su позволяет приобрести идентичность другого пользователя и либо начать новый сеанс командной оболочки с идентификатором этого пользователя, либо запустить одиночную команду от его имени. Команда sudo позволяет адми- нистратору записать настройки в конфигурационный файл с именем /etc/sudoers и определить конкретные команды, которые сможет выполнять тот или иной пользователь под приобретенной идентичностью. Выбор между su и sudo в значи- тельной степени определяется используемым дистрибутивом Linux. Большинство
136
Глава 9. Привилегии
дистрибутивов включают обе команды, но в настройках предпочтение отдается той или иной. Начнем с команды su.
su — запуск командной оболочки с подстановкой идентификаторов пользователя и группы
Команда su используется для запуска нового сеанса работы с командной оболоч- кой от имени другого пользователя. Команда имеет следующий синтаксис:
su [-[l]] [пользователь]
Если указан параметр -l, запущенная командная оболочка станет оболочкой вхо- да для указанного пользователя. Это означает, что будет загружено окружение пользователя и текущим рабочим каталогом станет домашний каталог пользова- теля. Часто это именно то, что требуется. Если пользователь не указан, подразу- мевается суперпользователь. Обратите внимание, что (довольно необычно) пара- метр -l можно сократить до -, и эта особенность часто используется на практике. Запустить командную оболочку от имени суперпользователя можно следующим образом:
[me@linuxbox ~]$ su - Пароль: [root@linuxbox ~]#
После ввода команды будет запрошен пароль суперпользователя. После ввода правильного пароля появится новое приглашение к вводу, показывающее, что данная командная оболочка обладает привилегиями суперпользователя (символ # в конце вместо символа $) и текущим рабочим каталогом теперь стал домашний каталог суперпользователя (обычно /root). После запуска новой оболочки можно выполнять команды с привилегиями суперпользователя. Завершим работу, введя команду exit, чтобы вернуться в предыдущую командную оболочку:
[root@linuxbox ~]# exit [me@linuxbox ~]$
С помощью su можно так же просто выполнить единственную команду, не запу- ская новый интерактивный сеанс:
su -c ‘команда’
При использовании этой формы команде su передается единственная команд- ная строка для выполнения. Не забудьте заключить команду в кавычки, чтобы
Изменение идентичности
137
предотвратить дополнительную ее интерпретацию механизмами подстановки те- кущей командной оболочки:
[me@linuxbox ~]$ su -c ’ls -l /root/*' Пароль: -rw——- 1 root root 754 2007-08-11 03:19 /root/anaconda-ks.cfg
/root/Mail: итого 0 [me@linuxbox ~]$
sudo — выполнение команды от имени другого пользователя
Команда sudo во многом подобна команде su, но имеет некоторые важные допол- нительные особенности. Администратор может определить порядок использо- вания sudo обычными пользователями, ограничив возможность запуска команд от имени другого пользователя (обычно суперпользователя). В частности, поль- зователю может быть разрешен доступ к одним командам и запрещен к другим.
Еще одно важное отличие состоит в том, что sudo не требует ввода пароля супер- пользователя. Для аутентификации в команде sudo пользователь должен ввести свой пароль. Например, допустим, что настройки sudo позволяют выполнить некоторую мифическую программу резервного копирования с именем backup_ script, требующую привилегий суперпользователя. С помощью sudo ее можно запустить так:
[me@linuxbox ~]$ sudo backup_script Пароль: System Backup Starting…
После ввода команды вам будет предложено ввести пароль (ваш, а не суперполь- зователя), и по завершении аутентификации указанная команда будет выполнена. Одно важное отличие между su и sudo — последняя не запускает новую команд- ную оболочку и не загружает окружение другого пользователя. Это означает, что команды не требуется экранировать как-то иначе, чем при запуске той же коман- ды без использования sudo. Имейте в виду, что такое ее поведение можно пере- определить с помощью различных параметров. Также отметьте, что sudo можно использовать для запуска интерактивного сеанса с привилегиями суперпользо- вателя (почти так же, как командой su -), передав ей параметр -i. Подробности ищите на странице справочного руководства (man) для sudo.
138
Глава 9. Привилегии
Чтобы увидеть, какие привилегии дает команда sudo, вызовите ее с параметром -l:
[me@linuxbox ~]$ sudo -l User me may run the following commands on this host: (ALL) ALL
UBUNTU И SUDO
Обычные пользователи иногда сталкиваются с необходимостью выполнить не- которую операцию, требующую привилегий суперпользователя. К числу таких операций относится установка и обновление программного обеспечения, правка системных конфигурационных файлов и доступ к устройствам. В мире Windows эта проблема часто решается передачей пользователям административных привиле- гий, что позволяет им решать подобные задачи. Однако программы, запускаемые такими пользователями, получают те же привилегии. В большинстве случаев это именно то, что нужно, но это также дает возможность беспрепятственной работы вредоносному программному обеспечению, такому как вирусы.
В мире Unix, вследствие многопользовательской природы этой операционной системы, всегда проводилась четкая грань между обычными пользователями и администраторами. Идеология Unix заключается в том, чтобы предоставлять привилегии суперпользователя, только когда они действительно необходимы. Для этого часто используются команды su и sudo.
Еще несколько лет тому назад большинство дистрибутивов Linux использовали с этой целью команду su. Команда su не требует настройки, как команда sudo, а наличие учетной записи root — давняя традиция в Unix. Вместе это порождает проблему. Пользователи могут испытывать соблазн действовать от имени root без всякой необходимости. Фактически некоторые пользователи вообще работают в своих системах, регистрируясь исключительно как root, чтобы избежать появ- ления раздражающих сообщений «permission denied» (доступ запрещен). Такой подход ухудшает защищенность Linux, низводя ее до уровня Windows. Не самое лучшее решение.
Создатели Ubuntu предприняли иной подход. По умолчанию Ubuntu запрещает регистрироваться в системе с учетной записью root (не позволяя устанавливать пароль для этой учетной записи), а для получения привилегий суперпользователя предлагает использовать sudo. Начальная учетная запись пользователя обладает полным доступом к привилегиям суперпользователя через sudo и может наделять аналогичными привилегиями другие, вновь создаваемые учетные записи.
Изменение идентичности
139
chown — изменение владельца и группы файла
Команда chown используется для изменения владельца и группы файла или ката- лога. Для использования этой команды необходимы привилегии суперпользова- теля. Команда chown имеет следующий синтаксис:
chown [владелец][:[группа]] файл…
chown может изменить владельца и/или группу файла в зависимости от первого аргумента. В табл. 9.7 приводится несколько примеров команды.
Таблица 9.7. Примеры аргументов команды chown
Аргумент
Результаты
bob
Изменит принадлежность файла, назначив владельцем пользователя bob
bob:users
Изменит принадлежность файла, назначив владельцем пользователя bob и группу users
:admins
Изменит принадлежность файла, назначив группу admins
bob:
Изменит принадлежность файла, назначив владельцем пользователя bob и группу этого пользователя
Представьте, что существуют два пользователя: janet, имеющий доступ к приви- легиям суперпользователя, и tony, лишенный таких привилегий. Пользователю janet нужно скопировать файл из своего домашнего каталога в домашний каталог пользователя tony. Поскольку пользователь janet хочет, чтобы пользователь tony смог редактировать файл, janet должен изменить владельца скопированного фай- ла, назначив владельцем tony:
[janet@linuxbox ~]$ sudo cp myfile.txt ~tony Пароль: [janet@linuxbox ~]$ sudo ls -l ~tony/myfile.txt -rw-r–r– 1 root root 8031 2018-03-20 14:30 /home/tony/myfile.txt [janet@linuxbox ~]$ sudo chown tony: ~tony/myfile.txt [janet@linuxbox ~]$ sudo ls -l ~tony/myfile.txt -rw-r–r– 1 tony tony 8031 2018-03-20 14:30 /home/tony/myfile.txt
Здесь видно, как пользователь janet копирует файл из своего каталога в домашний каталог пользователя tony. Далее janet заменяет владельца файла root (результат использования sudo) на tony. Добавив двоеточие в конец первого аргумента, janet
140
Глава 9. Привилегии
одновременно изменяет группу, которой принадлежит файл, на основную группу пользователя tony, которая, так уж получилось, носит то же имя tony.
Заметили ли вы, что после первого использования команда sudo не предложила пользователю janet вновь ввести пароль? Это объясняется тем, что в большин- стве конфигураций sudo продолжает «доверять» пользователю в течение несколь- ких минут (пока не истечет время ее действия).
chgrp — изменение группы файла
В старых версиях Unix команда chown изменяла только владельца файла, но не группу. Чтобы изменить группу, предоставлялась другая команда, chgrp. Она дей- ствует практически так же, как chown, но имеет больше ограничений.
Использование привилегий
Теперь, когда мы разобрались, как действует механизм привилегий, самое время научиться пользоваться ими. Далее демонстрируется решение типичной задачи — настройка общего каталога. Представьте себе двух пользователей, bill и karen. Оба имеют коллекции музыкальных произведений и хотели бы настроить общий каталог, где могли бы хранить файлы в формате Ogg Vorbis или MP3. Пользова- тель bill имеет доступ к привилегиям суперпользователя через sudo.
Первое, что нужно сделать, — это создать группу, куда будут входить оба пользо- вателя, bill и karen. С помощью графического инструмента для управления поль- зователями bill создает группу с именем music и добавляет в нее пользователей bill и karen, как показано на рис. 9.2.
Рис. 9.2. Создание новой группы
Использование привилегий
141
Далее bill создает каталог для музыкальных файлов:
[bill@linuxbox ~]$ sudo mkdir /usr/local/share/Music Пароль:
Поскольку bill манипулирует файлами за пределами своего домашнего каталога, ему необходимы привилегии суперпользователя. После создания каталог полу- чает следующие права доступа и владельца:
[bill@linuxbox ~]$ ls -ld /usr/local/share/Music drwxr-xr-x 2 root root 4096 2018-03-21 18:05 /usr/local/share/Music
Как видите, каталогом владеет root, и для него установлен режим доступа 755. Чтобы сделать каталог общим, bill должен изменить группу каталога и права до- ступа для группы:
[bill@linuxbox ~]$ sudo chown :music /usr/local/share/Music [bill@linuxbox ~]$ sudo chmod 775 /usr/local/share/Music [bill@linuxbox ~]$ ls -ld /usr/local/share/Music drwxrwxr-x 2 root music 4096 2018-03-21 18:05 /usr/local/share/Music
И что все это означает? А означает это следующее: владельцем каталога /usr/ local/share/Music является root, и члены группы music получают права на запись и чтение в этом каталоге. Группа music включает пользователей bill и karen; то есть bill и karen могут создавать файлы в каталоге /usr/local/share/Music. Другие пользователи могут просматривать содержимое каталога, но не могут создавать файлы в нем.
Но остается нерешенной еще одна проблема. С текущими разрешениями файлы и каталоги внутри каталога Music будут создаваться с обычными разрешениями для пользователей bill и karen:
[bill@linuxbox ~]$ > /usr/local/share/Music/test_file [bill@linuxbox ~]$ ls -l /usr/local/share/Music -rw-r–r– 1 bill bill 0 2018-03-24 20:03 test_file
В действительности здесь наблюдаются две проблемы. Во-первых, маска umask в этой системе имеет значение 0022, что не позволяет членам группы записывать в файлы, принадлежащие другим членам группы. Это не проблема, если общий каталог хранит только файлы, но так как в данном каталоге предполагается хра- нить музыкальные произведения, а музыкальные произведения обычно принято организовывать в иерархии по исполнителям и альбомам, членам группы может понадобиться создавать файлы в каталогах, принадлежащих другим членам. Нам нужно изменить маску umask для пользователей bill и karen на 0002.
142
Глава 9. Привилегии
Во-вторых, каждый файл и каталог, созданный одним членом группы, будет при- надлежать основной группе пользователя, а не группе music. Исправить этот недо- статок можно установкой бита setgid на каталог:
[bill@linuxbox ~]$ sudo chmod g+s /usr/local/share/Music [bill@linuxbox ~]$ ls -ld /usr/local/share/Music drwxrwsr-x 2 root music 4096 2018-03-24 20:03 /usr/local/share/Music
Теперь можно проверить, устранили ли проблему вновь добавленные разрешения. bill устанавливает маску umask в значение 0002, удаляет предыдущий провероч- ный файл и создает новый проверочный файл и каталог:
[bill@linuxbox ~]$ umask 0002 [bill@linuxbox ~]$ rm /usr/local/share/Music/test_file [bill@linuxbox ~]$ > /usr/local/share/Music/test_file [bill@linuxbox ~]$ mkdir /usr/local/share/Music/test_dir [bill@linuxbox ~]$ ls -l /usr/local/share/Music drwxrwsr-x 2 bill music 4096 2018-03-24 20:24 test_dir -rw-rw-r– 1 bill music 0 2018-03-24 20:22 test_file [bill@linuxbox ~]$
И файл и каталог теперь созданы с правильными правами доступа, позволяющи- ми всем членам группы music создавать файлы и каталоги внутри каталога Music.
Осталась только проблема с маской umask. Дело в том, что установленная маска действует лишь до конца сеанса и сбрасывается по его завершении. В главе 11 мы узнаем, как сохранить действие измененной маски umask между сеансами.
Изменение своего пароля
Последняя тема этой главы: изменение собственного пароля (и паролей других пользователей при наличии привилегий суперпользователя). Для установки и из- менения пароля используется команда passwd. Она имеет следующий синтаксис:
passwd [пользователь]
Чтобы изменить свой пароль, просто введите команду passwd. Вам будет предло- жено ввести старый, а затем новый пароль:
[me@linuxbox ~]$ passwd Смена пароля для me. (текущий) пароль UNIX: Введите новый пароль UNIX:
Заключение
143
Команда пытается вынудить пользователей вводить «сильные» пароли. Это озна- чает, что она будет отвергать слишком короткие пароли, очень похожие на преды- дущие пароли, пароли, являющиеся словарными словами или легко угадываемые:
[me@linuxbox ~]$ passwd Смена пароля для me. (текущий) пароль UNIX: Введите новый пароль UNIX: BAD PASSWORD: is too similar to the old one Введите новый пароль UNIX: Выберите пароль большей длины Введите новый пароль UNIX: BAD PASSWORD: it is based on a dictionary word
При наличии привилегий суперпользователя можно передать команде passwd ар- гумент с именем пользователя, чтобы установить пароль для этого пользователя. Суперпользователю доступна также возможность блокировки учетных записей, установки времени действия пароля и многое другое. За подробностями обращай- тесь к странице справочного руководства (man) для команды passwd.
Заключение
В этой главе мы увидели, как в Unix-подобных системах, таких как Linux, осу- ществляется управление разрешениями, дающими пользователям права досту- па на чтение, запись и выполнение к файлам и каталогам. Основные идеи этой системы разрешений были выработаны на самых ранних этапах развития Unix и выдержали испытание временем. Однако следует заметить, что традиционный механизм разрешений в Unix-подобных системах не обладает высокой избира- тельностью аналогичных механизмов в более современных системах.
10 Процессы
Современные операционные системы обычно являются многозадачными, в том смысле, что создают иллюзию одновременного решения множества задач, быстро переключаясь с выполнения одной программы на другую. Ядро Linux управляет всем этим посредством процессов. Именно с помощью процессов Linux организует приостановку программ в ожидании, пока наступит их очередь использовать про- цессор.
Иногда компьютер становится «вялым», или приложение вообще перестает от- кликаться на команды пользователя. Сейчас мы познакомимся с некоторыми ин- струментами командной строки, позволяющими увидеть, что делают программы, и завершить процессы, вышедшие из-под контроля.
В этой главе будут представлены следующие команды:
ps — выводит список процессов, выполняющихся в текущий момент;
top — выводит задачи;
jobs — выводит список активных заданий;
bg — переводит задание в фоновый режим работы;
fg — переводит задание в режим работы на переднем плане;
kill — посылает сигнал процессу;
killall — останавливает процессы по именам;
shutdown — останавливает или перезагружает систему.
Как действует процесс
В момент запуска системы ядро инициирует выполнение нескольких собственных задач в виде процессов и запускает программу с названием init. В свою очередь
Просмотр списка процессов
145
init выполняет последовательность сценариев командной оболочки (находятся в /etc), называемых сценариями начальной загрузки (init scripts), которые запу- скают все системные службы. Многие из этих служб реализованы как программы- демоны (daemon programs), то есть программы, действующие в фоновом режиме и выполняющие свою работу без участия пользователя. Поэтому, даже в отсут- ствие зарегистрированных пользователей, система выполняет определенные слу- жебные процедуры.
Принцип, по которому программа может запускать другие программы, выражает- ся правилом: родительский процесс запускает дочерний процесс.
Ядро хранит информацию обо всех процессах, чтобы упорядочить их работу. На- пример, каждому процессу присваивается номер, который называют идентифи- катором процесса (Process ID, PID). Идентификаторы процессов присваиваются в порядке возрастания, при этом процесс init всегда получает идентификатор PID 1. Ядро также следит за памятью, выделенной каждому процессу, и за готов- ностью процессов возобновить выполнение. Подобно файлам, процессы также имеют идентификаторы владельца и пользователя, эффективный (или действую- щий) идентификатор пользователя и т. д.
Просмотр списка процессов
Чаще всего для просмотра списка процессов используется команда ps (существу- ют и другие команды). Программа ps имеет множество параметров, но в самом простейшем случае она используется следующим образом:
[me@linuxbox ~]$ ps PID TTY TIME CMD 5198 pts/1 00:00:00 bash 10129 pts/1 00:00:00 ps
В этом примере команда вывела список с двумя процессами: процесс 5198 и процесс 10129 — программы bash и ps соответственно. Как можно заметить, по умолчанию ps выводит не очень много информации, только процессы, связанные с текущим сеан- сом. Чтобы увидеть больше, следует передать дополнительные параметры, но пре- жде чем мы сделаем это, давайте рассмотрим другие поля в выводе команды ps. Поле TTY — это сокращение от teletype (телетайп), оно содержит информацию об управ- ляющем терминале процесса. В Unix в этом поле выводится тип терминала. Поле TIME содержит объем процессорного времени, потребленного процессом. Как ви- дите, ни один из процессов не является слишком обременительным для компьютера.
Если добавить параметр x, можно получить более богатую информацию о проис- ходящем в системе:
146
Глава 10. Процессы
[me@linuxbox ~]$ ps x PID TTY STAT TIME COMMAND 2799 ? Ssl 0:00 /usr/libexec/bonobo-activation-server –ac 2820 ? Sl 0:01 /usr/libexec/evolution-data-server-1.10 – 15647 ? Ss 0:00 /bin/sh /usr/bin/startkde 15751 ? Ss 0:00 /usr/bin/ssh-agent /usr/bin/dbus-launch – 15754 ? S 0:00 /usr/bin/dbus-launch –exit-with-session 15755 ? Ss 0:01 /bin/dbus-daemon –fork –print-pid 4 –pr 15774 ? Ss 0:02 /usr/bin/gpg-agent -s –daemon 15793 ? S 0:00 start_kdeinit –new-startup +kcminit_start 15794 ? Ss 0:00 kdeinit Running… 15797 ? S 0:00 dcopserver –nosid и еще много других процессов…
Дополнительный параметр x (обратите внимание на отсутствие дефиса) сообщает команде ps, что та должна вывести все процессы, независимо от того, какие терми- налы (если таковые имеются) управляют ими. Символ ? в поле TTY указывает на отсутствие управляющего терминала. Таким образом, параметр x позволяет уви- деть все процессы в системе, которыми мы владеем.
Так как в системе одновременно выполняется множество процессов, ps произво- дит довольно длинные списки. Часто бывает полезно передать вывод ps команде less через конвейер, чтобы его проще было просматривать. Некоторые комбина- ции параметров приводят к выводу очень длинных строк, поэтому нелишним бу- дет также распахнуть окно эмулятора терминала на весь экран.
В этом примере в выводе появился новый столбец — STAT. Название STAT — это сокращение от state (состояние), столбец содержит информацию о текущем со- стоянии процесса, как показано в табл. 10.1.
Таблица 10.1. Состояния процессов
Состояние
Значение
R
S
D
T
Z
Выполняется. Процесс выполняется или готов к выполнению
Приостановлен. Процесс временно не выполняется; скорее всего, находит- ся в ожидании определенного события, такого как нажатие клавиши или прибытие сетевого пакета
Приостановлен без возможности прерывания. Процесс ожидает заверше- ния операции ввода/вывода, например, дисковым устройством
Остановлен. Процесс принудительно остановлен (подробнее об этом рас- сказывается ниже)
Недействующий процесс-«зомби». Это дочерний процесс, который завер- шился, но не был удален родителем
Просмотр списка процессов
147
Состояние
Значение
for information
~ ~ type :q to exit ~ type :help or for on-line help ~ type :help version7 for version info ~ ~ Running in Vi compatible mode ~ type :set nocp for Vim defaults ~ type :help cp-default for info on this ~ ~ ~
РЕЖИМ СОВМЕСТИМОСТИ
В примере выше, где показан экран, который выводится сразу после запуска vim, можно заметить текст: «Running in Vi compatible mode» (запущен в режиме совме- стимости с vi). Это означает, что vim был запущен в режиме, близко повторяющем обычное поведение vi, а не в расширенном режиме vim. Чтобы беспрепятственно следить за нитью повествования в этой главе, запустите vim в расширенном ре- жиме. Для этого в вашем распоряжении имеется пара возможностей. Попробуйте запустить редактор командой vim вместо vi. Если этот прием сработает, подумайте о том, чтобы добавить псевдоним vi=‘vim’ в свой файл .bashrc. Или выполните следующую команду, чтобы добавить строку в конфигурационный файл vim:
echo “set nocp” » ~/.vimrc
В разных дистрибутивах Linux vim упакован по-разному. В некоторых по умолчанию устанавливается минимальная версия vim, поддерживающая лишь ограниченный набор возможностей. Поэтому, выполняя примеры из этой главы, вы можете столкнуться с отсутствием некоторых возможностей, — в этом случае просто установите полную версию vim.
Так же как при знакомстве с nano, которое произошло в предыдущей главе, снача- ла научимся выходить из редактора. Для этого введите следующую команду (об- ратите внимание: двоеточие — это часть команды):
:q
176
Глава 12. Плавное введение в vi
В окне терминала должно появиться приглашение к вводу командной оболочки. Если по какой-то причине выход из vi не получился (скорее всего, потому что вы внесли какие-то изменения и еще не сохранили их), сообщите vi, что вы действи- тельно хотите выйти, добавив в команду восклицательный знак:
:q!
СОВЕТ
Если вы «заблудились» в vi, попробуйте дважды нажать ESC, чтобы вернуться на верный путь.
Режимы редактирования
Давайте снова запустим vi, но на этот раз укажем имя несуществующего файла. Именно так можно с помощью vi создать новый файл:
[me@linuxbox ~]$ rm -f foo.txt [me@linuxbox ~]$ vi foo.txt
В случае успеха на экране должно появиться следующее:
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ “foo.txt” [New File]
Режимы редактирования
177
Начальные символы тильды (~) сообщают об отсутствии текста в соответствующих строках. Таким способом vi сообщает нам, что файл пуст. Не вводите пока ничего!
Вторая важная вещь, которую нужно усвоить (после того, как вы научились вы- ходить), — vi является режимным редактором. Сразу после запуска vi оказыва- ется в командном режиме. В этом режиме практически каждая клавиша является командой, поэтому если вы начнете ввод, vi может запутаться сам и запутать вас.
Переход в режим вставки
Чтобы добавить какой-то текст в файл, необходимо сначала перейти в режим вставки. Для этого нажмите клавишу i. Вслед за этим, если vim работает в обычном расширенном режиме, в нижней части экрана появится надпись (она не появится, если редактор работает в режиме совместимости с vi):
– INSERT –
Теперь можно ввести какой-нибудь текст. Попробуйте, например:
Съешь же ещё этих мягких французских булок, да выпей чаю.
Чтобы выйти из режима вставки и вернуться в командный режим, нажмите ESC.
Сохранение изменений
Чтобы сохранить изменения в файл, введите ex-команду, находясь в командном режиме. Для этого нажмите клавишу :. После этого в нижней части должен по- явиться символ двоеточие:
:
Чтобы выполнить запись изменений в файл, вслед за двоеточием введите w и на- жмите ENTER:
:w
Файл будет записан на жесткий диск, и в нижней части появится подтверждение:
“foo.txt” [New] 1L, 46C written
СОВЕТ
Если заглянуть в документацию к vim, можно заметить, что (по непонятной при- чине) командный режим в ней называется нормальным режимом, а ex-команды называются командным режимом. Имейте эту неточность в виду.
178
Глава 12. Плавное введение в vi
Перемещение курсора
Находясь в командном режиме, vi предлагает большое число команд управления курсором, часть из которых также используется программой less. В табл. 12.1 пе- речислены некоторые из этих команд.
Таблица 12.1. Клавиши управления курсором
Клавиша
Перемещает курсор
l или стрелка вправо
Вправо на один символ
h или стрелка влево
Влево на один символ
j или стрелка вниз
Вниз на одну строку
k или стрелка вверх
Вверх на одну строку
0 (ноль)
В начало текущей строки
^
$
w
W
b
B
К первому непробельному символу в текущей строке
В конец текущей строки
В начало следующего слова или к знаку препинания
В начало следующего слова, минуя знаки препинания
В начало предыдущего слова или к знаку препинания
В начало предыдущего слова, минуя знаки препинания
CTRL+F или Page Down
Вниз на одну страницу
CTRL+B или Page Up
Вверх на одну страницу
числоG
G
К строке с указанным номером (например, команда 1G выполнит переход к первой строке в файле)
К последней строке в файле
Почему для перемещения курсора были выбраны клавиши h, j, k и l? Потому что, когда был написан редактор vi, не все видеотерминалы имели кнопки со стрел- ками на клавиатуре. Таким образом, опытные пользователи, хорошо владеющие клавиатурой, могли управлять курсором, не отрывая пальцев от клавиш.
Основы редактирования
179
Многие команды в vi могут начинаться с числа, как команда G в табл. 12.1. До- бавляя число в команду, можно указать, сколько раз она должна быть выполнена. Например, команда 5j переместит курсор на пять строк вниз.
Основы редактирования
Редактирование в основном заключается в нескольких простых операциях, та- ких как вставка текста, удаление текста и перемещение фрагментов текста с при- менением операций вырезания и вставки. Конечно же, vi поддерживает все эти операции своим неповторимым способом. vi поддерживает ограниченную форму отмены. Если нажать клавишу u в командном режиме, vi отменит последнее вы- полненное изменение. Это пригодится нам, когда мы будем пробовать некоторые простые команды редактирования.
Добавление текста в конец
vi поддерживает несколько способов входа в режим вставки. Мы уже использова- ли команду i для вставки текста.
Давайте вернемся к нашему файлу foo.txt:
Съешь же ещё этих мягких французских булок, да выпей чаю.
Если попытаться добавить текст в конец приложения, можно обнаружить, что команда i не позволяет сделать это, не давая переместить курсор за конец стро- ки. vi поддерживает команду добавления текста в конец, разумно названную a. Если переместить курсор в конец строки и ввести a, курсор переместится за ко- нец строки и vi перейдет в режим вставки. Это позволит нам добавить следую- щий текст:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно.
Не забудьте нажать ESC, чтобы выйти из режима вставки.
Поскольку добавлять текст в конец строки требуется довольно часто, vi предла- гает сокращенную команду для перемещения в конец строки и перехода в режим добавления. Это команда A. Давайте попробуем с ее помощью добавить еще не- сколько строк в наш файл.
Сначала командой 0 (ноль) переместите курсор в начало строки. Затем введите A и добавьте следующие строки текста:
180
Глава 12. Плавное введение в vi
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Строка 5
Снова нажмите клавишу ESC, чтобы выйти из режима вставки.
Как видите, команда A очень удобна, потому что помещает курсор в конец строки перед переходом в режим вставки.
Вставка строки
Другой способ вставки текста — вставка строк. Он позволяет вставить пустую строку между двумя имеющимися строками и перейти в режим вставки. Данный способ предлагает два варианта вставки, перечисленные в табл. 12.2.
Таблица 12.2. Команды вставки строк
Команда
Вставляет
o
O
Строку ниже текущей
Строку выше текущей
Рассмотрим действие этих команд на следующих примерах: поместите курсор в строку с текстом Строка 3 и введите o.
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3
Строка 4 Строка 5
Под третьей строкой появилась пустая строка, и редактор перешел в режим встав- ки. Выйдите из режима вставки нажатием ESC. Введите u, чтобы отменить измене- ния. Введите O, чтобы вставить пустую строку выше курсора:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2
Строка 3 Строка 4 Строка 5
Основы редактирования
181
Выйдите из режима вставки нажатием ESC и введите u, чтобы отменить изме- нения.
Удаление текста
Как можно догадаться, vi предлагает несколько способов удаления текста, и все они требуют нажатия одной или двух клавиш. Первый способ: команда x удаляет символ в позиции курсора. Команде x может предшествовать число, определяю- щее количество удаляемых символов. Команда d более универсальна. Команде d также может предшествовать число, определяющее количество операций удале- ния. Кроме того, команда d всегда сопровождается командой перемещения курсо- ра, управляющей размером удаляемой области. В табл. 12.3 приводится несколько примеров команды удаления.
Таблица 12.3. Команды удаления текста
Команда
Удалит
x
3x
dd
5dd
dW
d$
d0
d^
dG
Текущий символ
Текущий символ и следующие за ним два символа
Текущую строку
Текущую строку и следующие за ней четыре строки
От символа в текущей позиции курсора до начала следующего слова
От символа в текущей позиции курсора до конца текущей строки
От символа в текущей позиции курсора до начала строки
От символа в текущей позиции курсора до первого непробельного символа в строке
От текущей строки до конца файла
d20G
От текущей строки до 20-й строки файла
Поместите курсор на слово Это в первой строке. Вводите x, пока текст до конца предложения не будет удален. Затем введите несколько раз команду u, чтобы от- менить удаление.
ПРИМЕЧАНИЕ
Настоящий редактор vi поддерживает отмену только самой последней команды. vim поддерживает отмену множества команд.
182
Глава 12. Плавное введение в vi
Теперь давайте проведем операцию удаления еще раз, но теперь воспользуемся командой d. Снова установите курсор на слово Это и введите dW, чтобы удалить слово:
Съешь же ещё этих мягких французских булок, да выпей чаю. классно. Строка 2 Строка 3 Строка 4 Строка 5
Введите d$, чтобы удалить все от текущей позиции курсора до конца строки:
Съешь же ещё этих мягких французских булок, да выпей чаю. Строка 2 Строка 3 Строка 4 Строка 5
Введите dG, чтобы удалить все от текущей строки до конца файла:
~ ~ ~ ~ ~
Введите u три раза, чтобы отменить операции удаления.
Вырезание, копирование и вставка текста
Команда d не просто удаляет текст, она «вырезает» его. Каждый раз, когда выпол- няется команда d, удаленный текст копируется в буфер вставки (своего рода бу- фер обмена — clipboard), откуда позднее его можно извлечь командой p и вставить правее позиции курсора или левее — командой P.
Команда y выполняет копирование (yank) текста в буфер вставки почти так же, как команда d. В табл. 12.4 перечислены некоторые примеры комбинирования коман ды y с разными командами перемещения курсора.
Таблица 12.4. Команды копирования текста
Команда
Скопирует
yy
5yy
Текущую строку
Текущую и следующие четыре строки
Основы редактирования
183
Команда
Скопирует
yW
y$
y0
y^
yG
От текущей позиции курсора до начала следующего слова
От текущей позиции курсора до конца текущей строки
От текущей позиции курсора до начала строки
От текущей позиции курсора до первого непробельного символа в строке
От текущей строки до конца файла
y20G
От текущей строки до 20-й строки файла
Давайте попробуем что-нибудь скопировать и вставить. Поместите курсор на пер- вую строку и введите yy, чтобы скопировать текущую строку. Далее, переместите курсор в последнюю строку (G) и введите p, чтобы вставить скопированную стро- ку ниже текущей:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Строка 5 Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно.
Введите команду u, чтобы отменить изменение. Оставив курсор в последней стро- ке, введите P, чтобы вставить текст выше текущей строки:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 5
Попробуйте другие команды y из табл. 12.4 и посмотрите, как действуют коман- ды p и P. Закончив эксперименты, верните файл в исходное состояние.
Объединение строк
vi очень строго относится к понятию строки. Обычно он не дает возможности пе- реместить курсор в конец строки и удалить символ конца строки, чтобы объеди- нить текущую строку со следующей за ней. По этой причине в vi была добавлена специальная команда J (не путайте с командой j, которая перемещает курсор на одну строку вниз) для объединения строк.
184
Глава 12. Плавное введение в vi
Если поместить курсор в третью строку и ввести команду J, получится следующее:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Строка 5
Поиск и замена
Редактор vi имеет возможность перемещать курсор, опираясь на результаты по- иска. Он может это делать в пределах одной строки или всего файла. Он может также выполнять замену текста с подтверждением или без подтверждения поль- зователя.
Поиск в пределах строки
Команда f выполняет поиск в строке и перемещает курсор к следующему вхожде- нию указанного символа. Например, команда fa переместит курсор к следующему вхождению символа a в текущей строке. После выполнения операции поиска сим- вола в строке ее можно повторить, введя точку с запятой.
Поиск во всем файле
Для перемещения курсора к следующему вхождению слова или фразы использует- ся команда /. Она действует точно так же, как в программе less, о которой расска- зывалось в главе 3. После ввода команды / в нижней части экрана появится прямой слеш, вслед за которым нужно ввести искомое слово или фразу и нажать ENTER. По- сле этого курсор переместится к следующему вхождению искомой строки. Поиск следующего вхождения той же строки можно повторить командой n. Например:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Строка 5
Поместите курсор в первую строку и введите
/Строка
затем нажмите ENTER. Курсор переместится в строку 2. Затем введите команду n, и курсор переместится в строку 3. С каждой следующей командой n курсор будет
Поиск и замена
185
перемещаться вниз по файлу, пока не достигнет последнего вхождения искомого фрагмента. В примерах выше мы использовали для поиска только слова и фразы, однако vi позволяет применять регулярные выражения — очень мощное средство выражения сложных шаблонов текста. Мы подробно обсудим регулярные выра- жения в главе 19.
Глобальный поиск и замена
Для выполнения поиска с заменой (в vi эта операция называется подстановкой) в диапазоне строк или во всем файле vi использует ex-команды. Например, за- менить слово Строка словом строка во всем файле можно следующей командой:
:%s/Строка/строка/g
Давайте разобьем эту команду на элементы и рассмотрим их по отдельности (табл. 12.5).
Таблица 12.5. Пример синтаксиса команды глобального поиска с заменой
Элемент
Значение
:
%
s
Символ «двоеточие» начинает ex-команду
Определяет диапазон строк, где будет выполняться поиск. % — со- кращение, означающее «от первой строки до последней». В этом примере можно было бы указать диапазон номеров строк 1,5 (по- тому что в нашем файле всего пять строк) или 1,$, что означает «от строки с номером 1 до последней». Если диапазон строк не указан, операция применяется только к текущей строке
Определяет операцию — в данном случае подстановку (substitution), или поиск с заменой
/Строка/строка/
Шаблон поиска и текст замены
g
Означает global (глобально), в том смысле, что подстановка вы- полняется для всех вхождений искомой строки в каждой строке. Если элемент g опустить, операция выполнит замену только перво- го вхождения искомого фрагмента в каждой строке
После выполнения поиска с заменой наш файл будет выглядеть так:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. строка 2 строка 3 строка 4 строка 5
186
Глава 12. Плавное введение в vi
В команде подстановки можно указать, что она должна запрашивать подтвержде- ние у пользователя перед заменой. Для этого добавьте символ c в конец команды. Например:
:%s/строка/Строка/gc
Эта команда вернет содержимое файла в прежнее состояние, но перед каждой за- меной vi будет останавливаться и спрашивать подтверждение, выдавая следую- щее сообщение:
заменить на Строка? (y/n/a/q/l/^E/^Y)
В круглых скобках перечислены возможные варианты ответов, описание которых приводится в табл. 12.6.
Таблица 12.6. Клавиши подтверждения замены
Клавиша
Действие
y
n
a
Выполнить замену
Пропустить найденное вхождение
Выполнить замену этого и всех последующих вхождений
q или ESC
Завершить операцию
l
Выполнить замену этого вхождения и завершить операцию. Сокра- щенно от last (последняя)
CTRL+E, CTRL+Y
Прокрутить вниз или вверх соответственно. Эти команды удобно использовать для просмотра контекста найденного вхождения перед заменой
Редактирование нескольких файлов
Иногда бывает необходимо редактировать сразу несколько файлов. Например, может понадобиться внести изменения в файлы или скопировать содержимое из одного файла в другой. Редактор vi позволяет открыть несколько файлов, пере- числив их в командной строке:
vi файл1 файл2 файл3…
Редактирование нескольких файлов
187
Давайте закроем текущий сеанс работы vi и создадим новый файл для редактиро- вания. Введите :wq, чтобы выйти из vi с сохранением изменений в тексте. Далее, создайте новый файл в домашнем каталоге, который мы будем использовать в на- ших экспериментах. Создайте файл, захватив в него вывод команды ls:
[me@linuxbox ~]$ ls -l /usr/bin > ls-output.txt
Теперь откройте в vi старый и новый файлы:
[me@linuxbox ~]$ vi foo.txt ls-output.txt
После запуска vi вы увидите на экране первый файл:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Строка 5
Переключение между файлами
Чтобы переключиться с одного файла на следующий, выполните ex-команду:
:bn
Чтобы вернуться обратно, в предыдущий файл, выполните:
:bp
Теперь мы можем переключаться между файлами, но vi проводит политику, за- прещающую переключаться между файлами, если в текущем файле имеются несохраненные изменения. Чтобы заставить vi переключиться между файлами с потерей всех несохраненных изменений, добавьте в команду восклицательный знак (!).
В дополнение к методам переключения между файлами, описанным выше, vim (и некоторые версии vi) предоставляет дополнительные ex-команды, упрощаю- щие управление множеством файлов. Например, командой :buffers можно вы- вести список редактируемых файлов. В этом случае список появляется в нижней части экрана:
:buffers
1 %a “foo.txt” line 1 2 “ls-output.txt” line 0 Нажмите ENTER или введите команду для продолжения
188
Глава 12. Плавное введение в vi
Чтобы перейти к другому буферу (файлу), введите :buffer и номер искомого бу- фера. Например, переключиться с буфера 1, содержащего файл foo.txt, на буфер 2, содержащий файл ls-output.txt, можно командой:
:buffer 2
после выполнения этой команды на экране появится второй файл.
Открытие дополнительных файлов для редактирования
Также существует возможность добавлять файлы в текущий сеанс редактирова- ния. Команда :e (сокращенно от edit — редактировать) с именем файла откроет дополнительный файл. Завершите текущий сеанс редактирования и вернитесь в командную строку.
Запустите vi снова, но на этот раз с единственным файлом:
[me@linuxbox ~]$ vi foo.txt
Чтобы добавить второй файл, введите:
:e ls-output.txt
и на экране должен появиться второй файл. Первый файл останется открытым в редакторе, в чем легко убедиться:
:buffers 1 # “foo.txt” line 1 2 %a “ls-output.txt” line 0 Нажмите ENTER или введите команду для продолжения
Копирование содержимого из одного файла в другой
Часто в процессе редактирования множества файлов бывает необходимо скопи- ровать фрагмент текста из одного файла в другой. Это легко сделать с помощью обычных команд копирования и вставки, представленных выше. Посмотрим, как можно это осуществить. Сначала, в случае с использованием двух наших файлов, переключитесь на буфер 1 (foo.txt), выполнив команду:
:buffer 1
Редактирование нескольких файлов
189
В результате на экране должно появиться следующее:
Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Строка 5
Далее переместите курсор на первую строку и введите yy, чтобы скопировать строку.
Переключитесь на второй буфер командой:
:buffer 2
Теперь на экране должен появиться некий список файлов (здесь показана только часть):
total 343700 -rwxr-xr-x 1 root root 31316 2017-12-05 08:58 [ -rwxr-xr-x 1 root root 8240 2017-12-09 13:39 411toppm -rwxr-xr-x 1 root root 111276 2018-01-31 13:36 a2p -rwxr-xr-x 1 root root 25368 2017-10-06 20:16 a52dec -rwxr-xr-x 1 root root 11532 2017-05-04 17:43 aafire -rwxr-xr-x 1 root root 7292 2017-05-04 17:43 aainfo
Переместите курсор на первую строку и вставьте строку, скопированную в преды- дущем файле, введя команду p:
total 343700 Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. -rwxr-xr-x 1 root root 31316 2017-12-05 08:58 [ -rwxr-xr-x 1 root root 8240 2017-12-09 13:39 411toppm -rwxr-xr-x 1 root root 111276 2018-01-31 13:36 a2p -rwxr-xr-x 1 root root 25368 2017-10-06 20:16 a52dec -rwxr-xr-x 1 root root 11532 2017-05-04 17:43 aafire -rwxr-xr-x 1 root root 7292 2017-05-04 17:43 aainfo
Вставка целого файла в другой файл
Кроме того, мы можем вставить файл целиком в другой файл. Для выполнения этого приема завершите сеанс vi и запустите новый, с одним файлом:
[me@linuxbox ~]$ vi ls-output.txt
190
Глава 12. Плавное введение в vi
На экране снова появится список файлов:
total 343700 -rwxr-xr-x 1 root root 31316 2017-12-05 08:58 [ -rwxr-xr-x 1 root root 8240 2017-12-09 13:39 411toppm -rwxr-xr-x 1 root root 111276 2018-01-31 13:36 a2p -rwxr-xr-x 1 root root 25368 2017-10-06 20:16 a52dec -rwxr-xr-x 1 root root 11532 2017-05-04 17:43 aafire -rwxr-xr-x 1 root root 7292 2017-05-04 17:43 aainfo
Переместите курсор в третью строку и введите следующую ex-команду:
:r foo.txt
Команда :r (сокращенно от read — читать) вставит указанный файл перед позици- ей курсора. Теперь экран должен выглядеть так:
total 343700 -rwxr-xr-x 1 root root 31316 2017-12-05 08:58 [ -rwxr-xr-x 1 root root 8240 2017-12-09 13:39 411toppm Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Строка 2 Строка 3 Строка 4 Строка 5 -rwxr-xr-x 1 root root 111276 2018-01-31 13:36 a2p -rwxr-xr-x 1 root root 25368 2017-10-06 20:16 a52dec -rwxr-xr-x 1 root root 11532 2017-05-04 17:43 aafire -rwxr-xr-x 1 root root 7292 2017-05-04 17:43 aainfo
Сохранение результатов работы
И здесь vi предлагает нам несколько способов сохранения отредактированных файлов. Мы уже знакомы с ex-командой :w, но существуют и другие команды, ко- торые могут оказаться полезными.
В командном режиме можно ввести ZZ, чтобы сохранить текущий файл и выйти из vi. Аналогично, ex-команда :wq сочетает в себе команды :w и :q, первая из которых сохраняет файл, а вторая закрывает редактор.
В команде :w можно также указать имя файла. В этом случае она будет действовать как команда Save As (Сохранить как). Например, если вы редактируете foo.txt и хоти- те сохранить альтернативную версию с именем foo1.txt, введите следующую команду:
:w foo1.txt
Заключение
191
ПРИМЕЧАНИЕ
Эта команда сохранит файл с новым именем, но она не изменит имя текущего редактируемого файла. Если после этого продолжить редактирование, вы будете редактировать foo.txt, а не foo1.txt.
Заключение
Освоив эти основные навыки, вы сможете решать простейшие задачи админи- стрирования типичной системы Linux, связанные с редактированием текста. Вре- мя, потраченное на изучение приемов использования vim, обязательно окупится в будущем. vi-подобные редакторы глубоко укоренились в культуре Unix, и мы увидим множество других программ, подвергшихся их влиянию. Ярким приме- ром такого влияния может служить программа less.
13 Настройка
приглашения к вводу
В этой главе мы рассмотрим, казалось бы, такую незначительную деталь, как при- глашение к вводу командной оболочки (prompt). Кроме того, мы познакомимся с некоторыми внутренними особенностями работы командной оболочки и самой программы эмулятора терминала.
Как и многое в Linux, приглашение к вводу командной оболочки можно настраи- вать в очень широких пределах, и хотя мы принимаем это приглашение как дан- ность, оно в действительности оказывается очень полезным средством для тех, кто умеет управлять им.
Устройство строки приглашения к вводу
По умолчанию строка приглашения к вводу имеет следующий вид:
[me@linuxbox ~]$
Обратите внимание, что она содержит имя пользователя, имя хоста (сетевое имя компьютера) и имя текущего рабочего каталога. Но как она образовалась? Все, оказывается, очень просто. Форма приглашения к вводу определяется в перемен- ной окружения PS1 (сокращенно от prompt string 1 — строка приглашения 1). Уви- деть содержимое переменной PS1 можно с помощью команды echo:
[me@linuxbox ~]$ echo $PS1 [\u@\h \W]$
Устройство строки приглашения к вводу
193
ПРИМЕЧАНИЕ
Не волнуйтесь, если вы увидите нечто отличающееся от примера, приведенного выше. Все дистрибутивы Linux определяют приглашение к вводу по-своему, а некоторые содержат весьма экзотические определения.
Мы видим, что PS1 содержит несколько символов, например: квадратные скобки, знак @ и знак доллара, но все остальное — сплошная абракадабра. Наиболее догад- ливые из вас сразу поймут, что символы, экранированные слешем, — специальные символы, как те, что мы видели в главе 7. В табл. 13.1 приводится неполный список символов, которые командная оболочка интерпретирует специальным образом в строке приглашения.
Таблица 13.1. Экранированные последовательности, используемые в строке приглашения
Последова- тельность
Отображаемое значение
\a
\d
\h
\H
\j
\l
\n
\r
\s
\t
\T
@
\A
Звонок. Заставляет компьютер издавать звуковой сигнал
Текущая дата в формате: день недели месяц число; например, «Mon May 26» (Пн Май 26)
Имя хоста локальной машины минус имя домена
Полное имя хоста
Число заданий, действующих в текущем сеансе
Имя текущего устройства терминала
Символ перевода строки
Возврат каретки
Имя программы командной оболочки
Текущее время в 24-часовом формате
Текущее время в 12-часовом формате
Текущее время в 12-часовом формате AM/PM
Текущее время в 24-часовом формате, часы:минуты
194
Глава 13. Настройка приглашения к вводу
Последова- тельность
Отображаемое значение
Таблица 13.1 (окончание)
\u
\v
\V
\w
\W
!
#
$
[
]
Имя пользователя
Номер версии командной оболочки
Номер версии и выпуска командной оболочки
Имя текущего рабочего каталога
Последняя часть в имени текущего рабочего каталога
Номер текущей команды в истории
Число команд, введенных в текущем сеансе командной оболочки
Выводит символ $, если пользователь не является суперпользовате- лем, иначе выводит символ #
Отмечает начало последовательности из одного или более непечата- емых символов. Используется для внедрения непечатаемых сим- волов, управляющих поведением эмулятора терминала, например, перемещает курсор или изменяет цвет текста
Отмечает конец последовательности непечатаемых символов
Альтернативные варианты оформления приглашения
Имея список специальных символов, можно попробовать изменить оформле- ние приглашения. Для начала сохраните исходное определение, чтобы его мож- но было восстановить позднее. Для этого скопируйте значение переменной PS1 в другую переменную:
[me@linuxbox ~]$ ps1_old="$PS1"
Здесь создается новая переменная с именем ps1_old, и ей присваивается значение переменной PS1. Убедиться, что значение скопировано, можно с помощью коман- ды echo:
[me@linuxbox ~]$ echo $ps1_old [\u@\h \W]$
Альтернативные варианты оформления приглашения
195
Это позволит вам в любой момент восстановить исходное оформление приглаше- ния, выполнив обратную процедуру:
[me@linuxbox ~]$ PS1="$ps1_old"
Теперь, когда все готово, давайте посмотрим, что получится, если определить пу- стую строку приглашения:
[me@linuxbox ~]$ PS1=
Если определить приглашение как пустую строку, мы ничего не увидим. Стро- ка приглашения просто исчезнет! В действительности она все еще существует, но поскольку она пустая, на экране ничего не отображается, — собственно, как мы и просили. Так как пустая строка приглашения дезориентирует, давайте опреде- лим минимальное оформление:
PS1="$ "
Так лучше. По крайней мере, теперь видно, где мы находимся. Обратите внимание на завершающий пробел внутри кавычек. Он обеспечивает дополнительное про- странство на экране между знаком доллара и курсором.
Добавим в строку приглашения сигнал:
$ PS1="[\a]$ "
Теперь при каждом выводе строки приглашения вы должны слышать короткий звуковой сигнал. Постоянно звучащий сигнал может раздражать, но в некоторых случаях он может быть полезен, например, если нужно получать звуковое опо- вещение об удачном завершении долго выполняющихся команд. Обратите вни- мание, что мы добавили последовательности [ и ]. Поскольку управляющий ASCII-символ «звонок» (\a) не является «печатаемым», то есть не перемещает курсор при выводе, мы должны сообщить об этом командной оболочке bash, что- бы та могла правильно определить длину строки приглашения.
А теперь попробуйте сделать приглашение более информативным, добавив имя хоста и время суток:
$ PS1="\A \h $ " 17:33 linuxbox $
Добавление времени суток может пригодиться, если есть необходимость зафикси- ровать, в какой момент закончилось выполнение задачи. Наконец, сделайте при- глашение похожим на оригинальное:
196
Глава 13. Настройка приглашения к вводу
17:37 linuxbox $ PS1="$ " $
Попробуйте использовать другие последовательности из табл. 13.1 и посмотрите, сможете ли вы получить свою уникальную строку приглашения к вводу.
Добавление цвета
Большинство программ эмуляторов терминалов реагируют на некоторые по- следовательности непечатаемых символов, например, управляющие атрибутами символов (такими, как цвет, жирность и мигание) и позицией курсора. О позиции курсора мы поговорим чуть позже, а сейчас займемся цветом.
НЕДОРАЗУМЕНИЯ С ТЕРМИНАЛАМИ
В стародавние времена, когда дискеты были большими, а терминалы подключа- лись к удаленным компьютерам, существовало великое многообразие моделей терминалов, и все они работали по-разному. Они имели разные клавиатуры и по- разному интерпретировали управляющую информацию. В Unix и в Unix-подобных системах имеются две очень сложные подсистемы (которые называются termcap и terminfo), решающие все проблемы, связанные с управлением терминалами. Если заглянуть в самые потаенные кладовые настроек эмулятора терминала, можно обнаружить параметр, определяющий тип эмулируемого терминала.
Чтобы заставить терминалы говорить на едином языке, Американский националь- ный институт стандартов (American National Standards Institute, ANSI) разработал набор последовательностей символов для управления видеотерминалами. Заслу- женные пользователи DOS еще помнят файл ANSI.SYS, который применялся для интерпретации этих последовательностей.
Цветом символов можно управлять, посылая эмулятору терминала экранирован- ные последовательности ANSI внутри потока символов, предназначенных для вы- вода на экран. Экранированные последовательности не «выводятся» на экран; они интерпретируются терминалом как инструкции. Как показано в табл. 13.1, для включения непечатаемых символов используются последовательности [ и ]. Экранированные последовательности ANSI начинаются с восьмеричного кода 033 (код, генерируемый клавишей ESC), за которым следует необязательный атри- бут символа и инструкция. Например, вот как выглядит код, определяющий текст как простой (атрибут = 0), черного цвета
\033[0;30m.
Добавление цвета
197
В табл. 13.2 перечислены поддерживаемые цвета текста. Обратите внимание, что цвета делятся на две группы, отличаясь наличием атрибута жирного текста (1), из-за которого возникает впечатление более «светлых» (light) цветов.
Таблица 13.2. Экранированные последовательности, используемые для определения цвета текста
Последовательность
Цвет
Последовательность
Цвет
\033[0;30m
\033[0;31m
\033[0;32m
Черный
Красный
Зеленый
\033[1;30m
Темно-серый
\033[1;31m
\033[1;32m
Светло-красный
Светло-зеленый
\033[0;33m
Коричневый
\033[1;33m
Желтый
\033[0;34m
Синий
\033[1;34m
Светло-синий
\033[0;35m
Пурпурный
\033[1;35m
Светло-пурпурный
\033[0;36m
Бирюзовый
\033[1;36m
Светло-бирюзовый
\033[0;37m
Светло-серый
\033[1;37m
Белый
Давайте попробуем окрасить строку приглашения в красный цвет (здесь она вы- глядит как серая). Добавьте в начало экранированную последовательность:
$ PS1="[\033[0;31m]$ " $
Получилось, но обратите внимание, что весь текст, который вводится с клавиату- ры вслед за строкой приглашения, также окрашивается в красный цвет. Для устра- нения этого эффекта добавьте еще одну экранированную последовательность в конец определения приглашения — этим вы сообщите эмулятору терминала, что тот должен восстановить нормальный цвет:
$ PS1="[\033[0;31m]$[\033[0m] " $
Так лучше!
Кроме того, существует возможность изменить цвет фона, для чего предназначе- ны экранированные последовательности, перечисленные в табл. 13.3. Цвет фона не поддерживает атрибут жирного текста.
198
Глава 13. Настройка приглашения к вводу
Таблица 13.3. Экранированные последовательности, используемые для определения цвета фона
Последовательность
Цвет
Последовательность
Цвет
\033[0;40m
\033[0;41m
\033[0;42m
\033[0;43m
Черный
Красный
Зеленый
\033[0;44m
\033[0;45m
\033[0;46m
Синий
Пурпурный
Бирюзовый
Коричневый
\033[0;47m
Светло-серый
Чтобы вывести приглашение на красном фоне, достаточно изменить первую экра- нированную последовательность:
$ PS1="[\033[0;41m]$[\033[0m] " $
Попробуйте другие цвета и посмотрите, что из этого получится!
ПРИМЕЧАНИЕ
Помимо атрибутов символов нормального (0) и жирного (1) текста, есть также атрибут подчеркивания (4), мигания (5) и инверсии (7). В целях воспитания хо- рошего вкуса многие эмуляторы терминалов не поддерживают атрибут мигания.
Перемещение курсора
Экранированные последовательности можно использовать для позиционирова- ния курсора. Этот прием часто используется для отображения времени или дру- гой информации в разных местах на экране, например вверху, при каждом выводе приглашения к вводу. В табл. 13.4 перечислены экранированные последователь- ности, управляющие позицией курсора.
Таблица 13.4. Экранированные последовательности, управляющие позицией курсора
Последовательность
Действие
\033[l;cH
\033[nA
\033[nB
\033[nC
Перемещает курсор в строку l и позицию c
Перемещает курсор вверх на n строк
Перемещает курсор вниз на n строк
Перемещает курсор вперед на n символов
Перемещение курсора
199
Последовательность
Действие
\033[nD
\033[2J
\033[K
\033[s
\033[u
Перемещает курсор назад на n символов
Очищает экран и помещает курсор в левый верхний угол (строка 0, позиция 0)
Очищает экран от позиции курсора до конца текущей строки
Сохраняет текущую позицию курсора
Восстанавливает сохраненную позицию курсора
Используя эти коды, можно сконструировать строку приглашения, рисующую красный прямоугольник с часами в верхней части экрана (время отображается желтым цветом). Код, определяющий строку приглашения, на этот раз выглядит немного устрашающе:
PS1="[\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u]$ "
В табл. 13.5 приведены отдельные части этого определения, они помогут понять, как это работает.
Таблица 13.5. Экранированные последовательности, управляющие позицией курсора
Последовательность
Действие
[
\033[s
\033[0;0H
\033[0;41m
\033[K
Начинает последовательность непечатаемых символов. Ис- тинное назначение этой последовательности — позволить bash правильно вычислить длину строки приглашения на экране. Без этого функция редактирования командной строки неправильно позиционировала бы курсор
Сохраняет позицию курсора. Это необходимо, чтобы вернуться в местоположение строки приглашения после вывода прямоу- гольника с часами в верхней части экрана. Будьте внимательны: некоторые эмуляторы терминалов не поддерживают эту после- довательность
Перемещает курсор в левый верхний угол, в строку 0, позицию 0
Устанавливает красный цвет фона
Очищает экран от текущей позиции курсора (в левом верхнем углу) до конца строки. Поскольку теперь установлен красный цвет фона, строка окрашивается в красный цвет. Обратите внимание, что последовательность очистки экрана до конца строки не изме- няет позицию курсора, который остается в левом верхнем углу
\033[1;33m
Устанавливает желтый цвет текста
200
Глава 13. Настройка приглашения к вводу
Таблица 13.5 (окончание)
Последовательность
Действие
\t
\033[0m
\033[u
]
Выводит текущее время. Хотя это «печатаемый» элемент, он находится в непечатаемом блоке строки приглашения, потому что нам не нужно, чтобы командная оболочка bash учитывала размер часов при расчете истинного размера отображаемой строки приглашения
Выключает цвет. Сбрасывает настройки цвета для текста и фона
Восстанавливает позицию курсора, сохраненную ранее
Завершает последовательность непечатаемых символов
$
Строка приглашения
Сохранение определения приглашения
Я думаю, что мало у кого возникло желание вводить это монструозное определе- ние каждый раз, поэтому нам нужно где-то сохранить строку приглашения. Со- хранить определение можно в файле .bashrc. Для этого добавьте следующие две строки в файл:
PS1="[\033[s\033[0;0H\033[0;41m\033[K\033[1;33m\t\033[0m\033[u]$ " export PS1
Заключение
Хотите — верьте, хотите — нет, но со строкой приглашения можно творить чуде- са, задействовав функции и сценарии, которые мы пока не рассматривали. Все, что было описано выше, — хорошее начало. Не все захотят возиться с изменением приглашения к вводу, оформление по умолчанию тоже выглядит неплохо. Но тем из вас, кому нравится копаться в мелочах, командная оболочка предоставляет воз- можность творчески провести несколько часов.
Часть III ТИПИЧНЫЕ ЗАДАЧИ И ОСНОВНЫЕ ИНСТРУМЕНТЫ
14 Управление пакетами
Общаясь с другими членами сообщества Linux, мы услышим массу мнений о том, какой дистрибутив Linux лучше. Часто обзоры дистрибутивов выглядят довольно глупыми, скатываясь к сравнению, например, привлекательности обоев рабочего стола (некоторые отвергают Ubuntu, потому что им не нравится цветовая схема по умолчанию!) и других тривиальных особенностей.
Самой важной отличительной чертой дистрибутива является система управления пакетами и активность сообщества, поддерживающего дистрибутив. Поработав с Linux достаточно долгое время, легко заметить, насколько динамичен программ- ный ландшафт этой системы. Он находится в постоянном движении. Большин- ство создателей основных дистрибутивов Linux выпускают новые версии каждые шесть месяцев, а множество отдельных программ обновляется каждый день. Что- бы не отставать от этой лавины программного обеспечения, нам нужен хороший инструмент для управления пакетами.
Управление пакетами (package management) — это методика установки и управле- ния программным обеспечением в системе. В наши дни большинство может удов- летворить все свои потребности в программном обеспечении, устанавливая пакеты, подготовленные создателями соответствующих дистрибутивов Linux. Это отлича- ется от ситуации, возникшей в первые годы развития Linux, когда для установки программ требовалось загружать и компилировать исходный код. Нельзя сказать, что было бы неправильно устанавливать программы из исходных кодов; в действи- тельности наличие доступа к исходному коду является самым большим достоин- ством в Linux. Это предоставляет возможность исследовать и улучшать систему. Просто работать с заранее скомпилированными пакетами проще и быстрее.
В этой главе мы рассмотрим некоторые инструменты командной строки, исполь- зуемые для управления пакетами. В то время как все основные дистрибутивы предоставляют мощные и современные программы с графическим интерфейсом
Как действует система пакетов
203
для управления системой, умение работать с программами командной строки по-прежнему востребовано. Они способны выполнять задачи, многие из которых сложно (если вообще возможно) выполнить с использованием их аналогов с гра- фическим интерфейсом.
Системы пакетов
Разные дистрибутивы используют различные системы пакетов, и, как правило, па- кеты, подготовленные для одного дистрибутива, несовместимы с другими. В боль- шинстве дистрибутивов используется одна из двух основных технологий упаковки: разработанная создателями дистрибутива Debian с пакетами .deb и разработанная создателями дистрибутива Red Hat с пакетами .rpm. Существует несколько важных исключений, таких как Gentoo, Slackware и Arch, но в большинстве других дистри- бутивов используется одна из двух основных систем, что показано в табл. 14.1.
Таблица 14.1. Основные системы пакетов
Система пакетов
Дистрибутивы (неполный список)
Debian (.deb)
Red Hat (.rpm)
Debian, Ubuntu, Linux Mint, Raspbian
Fedora, CentOS, Red Hat Enterprise Linux, OpenSUSE
Как действует система пакетов
Способ распространения программ, используемый в индустрии патентованного программного обеспечения, обычно включает покупку установочного носителя, такого как «установочный диск», и последующий запуск мастера установки ново- го приложения в систему.
Linux действует иначе. Практически все программное обеспечение для системы Linux находится в Интернете. Большая его часть предоставляется создателями дистрибутивов в форме файлов пакетов, а остальная часть доступна в исходном коде, который можно установить вручную. Мы еще поговорим об установке про- грамм путем компиляции исходного кода в главе 23.
Файлы пакетов
Основной единицей программного обеспечения в системе пакетов является файл пакета. Файл пакета — это сжатая коллекция файлов, составляющих программ- ный пакет. Пакет может состоять из множества программ и файлов с данными,
204
Глава 14. Управление пакетами
поддерживающих программы. Помимо файлов для установки, файл пакета вклю- чает также метаданные с информацией о пакете, например текстовым описанием пакета и его содержимого. Дополнительно многие пакеты включают сценарии для выполнения настроек до и после установки пакета.
Файлы пакетов создаются людьми, ответственными за сопровождение пакетов (package maintainer), часто (но не всегда) являющимися сотрудниками компании- производителя дистрибутива. Ответственный за пакет получает программное обеспечение в исходном коде от поставщика (автора программы), компилирует его и создает метаданные для пакета вместе со всеми необходимыми сценария- ми установки. Часто ответственный за сопровождение пакета вносит изменения в оригинальный исходный код с целью улучшения интеграции программы с дру- гими компонентами дистрибутива Linux.
Репозитории
Некоторые проекты самостоятельно создают пакеты и дистрибутивы своего про- граммного обеспечения, и все же большинство пакетов в наше время собирается создателями дистрибутивов и заинтересованными третьими сторонами. Готовые пакеты помещаются в центральный репозиторий дистрибутива, где они становят- ся доступными для пользователей. Репозиторий может содержать тысячи паке- тов, специально собранных для дистрибутива.
Для дистрибутива может поддерживаться несколько разных репозиториев с про- граммным обеспечением, находящимся на разных этапах разработки. Например, дистрибутивы обычно имеют тестовый репозиторий, содержащий недавно соз- данные пакеты, которые предназначены для смельчаков, пытающихся отыскать ошибки до того, как пакеты попадут в основной дистрибутив. Нередко дистрибу- тивы имеют репозиторий для разработки, куда помещаются пакеты, продолжа- ющие разрабатываться и предназначенные для включения в ближайший выпуск дистрибутива.
Дистрибутив может также иметь сторонние репозитории. Они необходимы для распространения программного обеспечения, которое по юридическим причинам, связанным с патентами или законами об управлении цифровыми правами (Digital Rights Management, DRM), не может быть включено в дистрибутив. Самым из- вестным случаем является поддержка шифрования DVD, которая считается не- законной в Соединенных Штатах. Сторонние репозитории располагаются в стра- нах, где патенты или законы, ограничивающие распространение программного обеспечения, отсутствуют или действуют иначе. Эти репозитории обычно полно- стью независимы от поддерживаемого ими дистрибутива, и для их использования нужно знать об их существовании и вручную включать их в конфигурационные файлы с настройками системы управления пакетами.
Как действует система пакетов
205
Зависимости
Программы редко действуют в одиночку; чаще они полагаются на наличие других программных компонентов. Стандартные операции, такие как ввод/вывод, на- пример, выполняются процедурами, которые совместно используются многими программами. Эти процедуры хранятся в так называемых разделяемых библиоте- ках (shared libraries), предоставляющих важные услуги нескольким программам. Если пакету требуется некий общий ресурс, такой как разделяемая библиотека, про него говорят, что он имеет зависимость. Современные системы управления пакетами поддерживают некоторые методы разрешения зависимостей, — это га- рантирует, что после установки пакета в системе будут также установлены все его зависимости.
Высоко- и низкоуровневые инструменты управления пакетами
Системы управления пакетами обычно включают инструменты двух типов:
низкоуровневые инструменты, решающие такие задачи, как установка и удале-
ние файлов пакетов;
и высокоуровневые инструменты, выполняющие поиск в метаданных и разре-
шение зависимостей.
В этой главе мы посмотрим, какие инструменты входят в состав систем на ос- нове Debian (таких, как Ubuntu и многих других), а также в состав последних продуктов Red Hat. Несмотря на то что все дистрибутивы на основе Red Hat опираются на одну и ту же низкоуровневую программу (rpm), они используют разные высокоуровневые инструменты. В ходе обсуждения мы познакомимся с высокоуровневой программой yum, используемой в дистрибутивах Fedora, Red Hat Enterprise Linux и CentOS. Другие дистрибутивы на основе Red Hat предо- ставляют высокоуровневые инструменты, сопоставимые по своим возможно- стям (табл. 14.2).
Таблица 14.2. Инструменты управления пакетами
Дистрибутивы
На основе Debian
Fedora, Red Hat Enterprise Linux, CentOS
Низкоуровневые инструменты
Высокоуровневые инструменты
dpkg
rpm
apt-get, apt, aptitude
yum, dnf
206
Глава 14. Управление пакетами
Типичные задачи управления пакетами
С помощью инструментов командной строки для управления пакетами можно выполнить множество разных операций. Мы рассмотрим наиболее типичные из них. Вы должны знать, что низкоуровневые инструменты поддерживают также создание файлов пакетов, но эта тема выходит за рамки данной книги.
В следующем обсуждении под термином имя_пакета будет подразумеваться фак- тическое имя пакета, а под термином файл_пакета — имя файла пакета.
Поиск пакета в репозитории
Используя высокоуровневые инструменты для поиска метаданных в репозито- рии, можно найти пакет по его имени или описанию (табл. 14.3).
Таблица 14.3. Команды поиска пакетов
Дистрибутив
Команды
Debian
Red Hat
apt-get update apt-cache search искомая_строка
yum search искомая_строка
Вот пример поиска текстового редактора emacs в системе Red Hat с помощью коман ды yum:
yum search emacs
Установка пакета из репозитория
Высокоуровневые инструменты позволяют загрузить пакет из репозитория и установить его с полным разрешением всех зависимостей (табл. 14.4).
Таблица 14.4. Команды установки пакетов
Дистрибутив
Команды
Debian
Red Hat
apt-get update apt-cache install имя_пакета
yum install имя_пакета
Типичные задачи управления пакетами
207
Вот пример установки текстового редактора emacs в системе Debian из репозито- рия apt:
apt-get update; apt-get install emacs
Установка пакета из файла пакета
Если файл пакета загружен из источника, не являющегося репозиторием, его можно установить непосредственно (без разрешения зависимостей) с использо- ванием низкоуровневого инструмента (табл. 14.5).
Таблица 14.5. Низкоуровневые команды установки пакетов
Дистрибутив
Команды
Debian
Red Hat
dpkg -i файл_пакета
rpm -i файл_пакета
Пример: если с некоторого сайта, не являющегося репозиторием, был загружен файл emacs-22.1-7.fc7-i386.rpm, его можно установить в систему Red Hat командой
rpm -i emacs-22.1-7.fc7-i386.rpm
ПРИМЕЧАНИЕ
Поскольку этот прием установки основан на использовании низкоуровневой программы rpm, он не выполняет разрешения зависимостей. Если программа rpm обнаружит неразрешенную зависимость, она завершится с сообщением об ошибке.
Удаление пакета
Пакеты можно удалять с помощью и низкоуровневых, и высокоуровневых ин- струментов. Примеры использования высокоуровневых инструментов приводят- ся в табл. 14.6.
Таблица 14.6. Команды удаления пакетов
Дистрибутив
Команды
Debian
Red Hat
apt-get remove имя_пакета
yum erase имя_пакета
208
Глава 14. Управление пакетами
Пример: удалить пакет emacs из системы Debian можно командой:
apt-get remove emacs
Обновление пакетов из репозитория
Наиболее типичной задачей управления пакетами является поддержание системы в актуальном состоянии обновлением пакетов до последних версий. Высокоуровне- вые инструменты способны выполнять эту важную задачу за один шаг (табл. 14.7).
Таблица 14.7. Команды обновления пакетов
Дистрибутив
Команды
Debian
Red Hat
apt-get update; apt-get upgrade
yum update
Пример: следующая команда применит все обновления, доступные для пакетов, установленных в системе на основе Debian:
apt-get update; apt-get upgrade
Обновление пакета из файла пакета
Если обновленная версия пакета была загружена из источника, не являющегося репозиторием, ее можно установить, заменив предыдущую версию (табл. 14.8).
Таблица 14.8. Низкоуровневые команды обновления пакетов
Дистрибутив
Команды
Debian
Red Hat
dpkg -i файл_пакета
rpm -U файл_пакета
Пример: обновить установленную программу emacs до версии, содержащей в фай- ле пакета emacs-22.1-7.fc7-i386.rpm, в системе Red Hat можно командой
rpm -U emacs-22.1-7.fc7-i386.rpm
ПРИМЕЧАНИЕ
dpkg не имеет параметра, отвечающего за обновление пакета вместо установки, как в программе rpm.
Типичные задачи управления пакетами
209
Список установленных пакетов
Команды в табл. 14.9 можно использовать для вывода списка всех пакетов, уста- новленных в системе.
Таблица 14.9. Команды вывода списка пакетов
Дистрибутив
Команды
Debian
Red Hat
dpkg –l
rpm –qa
Определение, установлен ли пакет
С помощью низкоуровневых инструментов из табл. 14.10 можно определить, был ли установлен определенный пакет.
Таблица 14.10. Команды определения состояния пакетов
Дистрибутив
Команды
Debian
Red Hat
dpkg -s имя_пакета
rpm –q имя_пакета
Пример: определить, был ли установлен пакет emacs в системе Debian, можно командой
dpkg -s emacs
Вывод информации об установленном пакете
Если известно имя установленного пакета, с помощью команд из табл. 14.11 мож- но получить описание пакета.
Таблица 14.11. Команды получения информации о пакетах
Дистрибутив
Команды
Debian
Red Hat
apt-cache show имя_пакета
yum info имя_пакета
210
Глава 14. Управление пакетами
Пример: получить описание пакета emacs в системе Debian можно командой
apt-cache show emacs
Поиск пакета по установленному файлу
Определить, в составе какого пакета был установлен некий файл, можно с помо- щью команд из табл. 14.12.
Таблица 14.12. Команды идентификации принадлежности файлов
Дистрибутив
Команды
Debian
Red Hat
dpkg -S имя_файла
rpm –qf имя_файла
Пример: узнать, в составе какого пакета был установлен файл /usr/bin/vim в си- стеме Red Hat, можно командой
rpm -qf /usr/bin/vim
Заключение
В последующих главах мы исследуем множество программ, решающих широкий спектр прикладных задач. Хотя большинство этих программ обычно устанавлива- ется по умолчанию, иногда возникает необходимость установить дополнительные пакеты. С вновь обретенными знаниями (и пониманием) особенностей управ- ления пакетами вы без труда сможете установить дополнительные программы и управлять ими.
МИФ ОБ УСТАНОВКЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ В LINUX
Те, кто прежде использовал другие платформы, иногда становятся жертвами мифов о сложности установки программного обеспечения в Linux и верят, что многообразие систем управления пакетами, используемых разными дистрибу- тивами, является серьезной помехой. Вообще-то и правда — помехой, только не для пользователей, а для производителей патентованного программного обеспечения, желающих распространять свои программы только в виде дво- ичных файлов.
Заключение
211
Программная экосистема Linux основана на идеологии открытости исходного кода. Если разработчик программного обеспечения выпустит исходный код своего про- дукта, почти наверняка человек, связанный с дистрибутивом, упакует этот про- дукт и включит его в репозиторий. Этот подход гарантирует хорошую интеграцию продукта с дистрибутивом, и пользователь сможет получить все необходимое ему программное обеспечение в одном месте, вместо того чтобы искать отдельные программы по разным веб-сайтам. В последнее время крупные поставщики про- приетарных платформ начали создавать магазины приложений, имитирующие эту идею.
Драйверы устройств распространяются почти так же, только они не выделяются в отдельные пакеты в репозитории дистрибутива, а включаются в ядро Linux. Мож- но сказать, что в Linux нет такого понятия, как «диск с драйверами». Либо ядро поддерживает данное устройство, либо нет, а ядро Linux поддерживает огромное число устройств. В действительности намного больше, чем Windows. Конечно, едва ли вас утешит информация, что нужное вам устройство не поддерживается ядром. Однако если такое случится, ищите причину. Отсутствие драйвера под- держки обычно обусловлено одной из следующих причин:
• Устройство слишком новое. Так как многие производители аппаратного обе- спечения не очень активно поддерживают Linux, задача написать драйвер для включения в ядро ложится на членов сообщества Linux. А это требует времени.
• Устройство чересчур экзотическое. Не все дистрибутивы включают все возможные драйверы устройств. Для каждого дистрибутива настраивается свое ядро, и так как ядра настраиваются до мелочей (благодаря чему от- крывается возможность использовать Linux в самых разных устройствах, от наручных часов до больших ЭВМ), создатели дистрибутива могли пропустить ваше устройство. Найдя и загрузив исходный код драйвера, вы (да, да — вы) сможете скомпилировать и установить драйвер самостоятельно. Это не очень сложно, скорее утомительно. О компиляции программного обеспечения мы поговорим в последующих главах.
• Производители аппаратного обеспечения что-то скрывают. Произво- дитель не выпустил либо исходный код драйвера для Linux, либо документа- цию, на основе которой можно было бы написать драйвер. Это означает, что производитель аппаратного обеспечения пытается сохранить программные интерфейсы устройства в секрете. Так как мы предпочитаем не использовать засекреченные устройства в своих компьютерах, я предлагаю удалить это не- толерантное устройство и отправить его в кучу из других бесполезных гаджетов.
15 Устройства хранения
В предыдущих главах мы познакомились с приемами работы с данными на уровне файлов. В этой главе мы будем рассматривать данные на уровне устройств. Linux обладает удивительными возможностями работы с устройствами хранения, такими как жесткие диски, сетевые хранилища или виртуальные устройства хранения, на- пример RAID (Redundant Array of Independent Disks — избыточный массив из неза- висимых дисков) и LVM (Logical Volume Manager — диспетчер логических томов).
Однако поскольку эта книга не о системном администрировании, мы не будем пытаться охватить эту тему во всех подробностях, а всего лишь познакомимся с некоторыми понятиями и ключевыми командами, которые используются для управления устройствами хранения данных.
Для выполнения упражнений к этой главе нам понадобится флеш-диск (флешка), подключаемый к порту USB компьютера и диск CD-RW (для систем, оборудован- ных пишущим приводом CD-ROM).
Мы познакомимся со следующими командами:
mount — монтирует файловые системы;
umount — размонтирует файловые системы;
fsck — проверяет и восстанавливает файловые системы;
fdisk — инструмент для работы с таблицей разделов;
mkfs — создает файловые системы;
dd — выполняет запись данных блоками непосредственно в устройство;
genisoimage (mkisofs) — создает файл образа ISO 9660;
wodim (cdrecord) — записывает данные на оптический носитель;
md5sum — вычисляет контрольную сумму MD5.
Монтирование и размонтирование устройств хранения
213
Монтирование и размонтирование устройств хранения
Последние достижения Linux на настольных компьютерах сделали управление устройствами хранения чрезвычайно простым для обычных пользователей. До- статочно подключить устройство к компьютеру, и оно тут же готово к работе. Раньше (года этак до 2004-го) все необходимые операции требовалось выпол- нять вручную. В серверных системах эти операции по большей части все еще вы- полняются вручную, потому что серверы часто предъявляют особые требования к устройствам хранения и настройкам.
Первый шаг в управлении устройствами хранения — подключение самого устройства к дереву файловой системы. Этот процесс называется монтировани- ем и позволяет устройству участвовать в работе операционной системы. Как рас- сказывалось в главе 2, Unix-подобные операционные системы, такие как Linux, поддерживают единое дерево файловой системы, к разным точкам которого под- ключаются дополнительные устройства. Этот подход отличается от используемо- го в других операционных системах, таких как Windows, где каждому устройству соответствует отдельное дерево файлов и каталогов (например, C:, D:\ и т. д.).
В файле с именем /etc/fstab (сокращенно от «file system table» — таблица файло- вых систем) перечисляются устройства (обычно разделы жесткого диска), мон- тируемые на этапе загрузки. Ниже приводится пример содержимого /etc/fstab из системы Fedora:
LABEL=/12 / ext4 defaults 1 1 LABEL=/home /home ext4 defaults 1 2 LABEL=/boot /boot ext4 defaults 1 2 tmpfs /dev/shm tmpfs defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 sysfs /sys sysfs defaults 0 0 proc /proc proc defaults 0 0 LABEL=SWAP-sda3 swap swap defaults 0 0
Большинство файловых систем из перечисленных в приведенном примере явля- ются виртуальными, и наше обсуждение к ним неприменимо. Наибольший инте- рес для нас в рамках исследования данной темы представляют первые три:
LABEL=/12 / ext4 defaults 1 1 LABEL=/home /home ext4 defaults 1 2 LABEL=/boot /boot ext4 defaults 1 2
Это разделы жесткого диска. Каждая строка включает шесть полей, описание ко- торых приводится в табл. 15.1.
214
Глава 15. Устройства хранения
Таблица 15.1. Поля в файле /etc/fstab
Поле
Содержит
Описание
1
Устройство
Традиционно это поле содержит фактическое имя файла устройства, связанного с физическим устройством, такое как /dev/hda1 (первый раздел ведущего (master) устройства на первом канале IDE). Но, учитывая, что в современных ком- пьютерах может быть множество динамически подключаемых устройств (таких, как устройства USB), многие современные дистрибутивы Linux связывают устройства с текстовыми метками. Такая метка (записываемая в устройство хранения во время форматирования) может быть простым текстом или случайно сгенерированным универсально уникальным идентификатором (Universally Unique Identifier, UUID). Эта метка читается операционной системой в момент подключе- ния устройства. Благодаря этому становится неважно, с каким файлом устройства связано физическое устройство, оно в лю- бом случае будет идентифицировано верно
2
3
4
5
6
Точка монтиро- вания
Каталог в файловой системе, к которому подключается устрой- ство
Тип файловой системы
Параметры
Linux позволяет монтировать множество типов файловых систем. Наиболее близкой к Linux является файловая система ext4, но точно так же поддерживаются другие типы, такие как FAT16 (msdos), FAT32 (vfat), NTFS (ntfs), CD-ROM (iso9660), и пр.
Файловые системы могут монтироваться с разными параметра- ми. Например, можно смонтировать файловую систему в режи- ме «только для чтения» или запретить выполнять какие-либо программы из нее (очень полезная мера предосторожности для съемных носителей)
Частота
Порядок
Единственное число, определяющее, когда должно выполнять- ся резервное копирование файловой системы командой dump
Единственное число, определяющее, в каком порядке файло- вая система должна проверяться командой fsck
Просмотр списка смонтированных файловых систем
Для монтирования файловых систем используется команда mount. Если ввести команду без аргументов, она выведет список файловых систем, смонтированных в настоящий момент:
[me@linuxbox ~]$ mount /dev/sda2 on / type ext4 (rw)
Монтирование и размонтирование устройств хранения
215
proc on /proc type proc (rw) sysfs on /sys type sysfs (rw) devpts on /dev/pts type devpts (rw,gid=5,mode=620) /dev/sda5 on /home type ext4 (rw) /dev/sda1 on /boot type ext4 (rw) tmpfs on /dev/shm type tmpfs (rw) none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw) sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw) fusectl on /sys/fs/fuse/connections type fusectl (rw) /dev/sdd1 on /media/disk type vfat (rw,nosuid,nodev,noatime, uhelper=hal,uid=500,ut f8,shortname=lower) twin4:/musicbox on /misc/musicbox type nfs4 (rw,addr=192.168.1.4)
Список имеет следующий формат: устройство on точка_монтирования type тип_ файловой_системы (параметры). Например, первая строка соответствует устрой- ству /dev/sda2, смонтированному как корневая файловая система типа ext4, до- ступная для чтения и записи (параметр rw). В конце списка можно заметить две интересные записи. Предпоследняя запись соответствует 2-гигабайтной SD-карте памяти в устройстве для чтения карт памяти, смонтированной в каталог /media/ disk, последняя запись соответствует сетевому приводу, смонтированному в ката- лог /misc/musicbox.
Для первого эксперимента возьмем привод CD-ROM. Сначала посмотрим, что имеется в системе, перед тем как вставить компакт-диск:
[me@linuxbox ~]$ mount /dev/mapper/VolGroup00-LogVol00 on / type ext4 (rw) proc on /proc type proc (rw) sysfs on /sys type sysfs (rw) devpts on /dev/pts type devpts (rw,gid=5,mode=620) /dev/hda1 on /boot type ext4 (rw) tmpfs on /dev/shm type tmpfs (rw) none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw) sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw)
Этот список получен в системе CentOS 5, где для создания корневой файловой системы используется диспетчер LVM. Подобно многим современным дистрибу- тивам Linux, эта система пытается автоматически монтировать компакт-диски. Вставив в привод компакт-диск, мы увидим следующее:
[me@linuxbox ~]$ mount /dev/mapper/VolGroup00-LogVol00 on / type ext4 (rw) proc on /proc type proc (rw) sysfs on /sys type sysfs (rw) devpts on /dev/pts type devpts (rw,gid=5,mode=620) /dev/hda1 on /boot type ext4 (rw)
216
Глава 15. Устройства хранения
tmpfs on /dev/shm type tmpfs (rw) none on /proc/sys/fs/binfmt_misc type binfmt_misc (rw) sunrpc on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw) /dev/sdc on /media/live-1.0.10-8 type iso9660 (ro,noexec,nosuid,nodev,uid=500)
Это практически тот же список, с одной дополнительной записью. Последняя за- пись в списке сообщает, что компакт-диск в приводе CD-ROM (устройство /dev/ sdc в этой системе) смонтирован в каталог /media/live-1.0.10-8 и имеет файловую систему iso9660 (типичную для компакт-дисков). Обратите внимание на имя устройства. Когда вы будете проводить эксперимент в своей системе, очень веро- ятно, что имя устройства у вас будет отличаться.
ВНИМАНИЕ
В примерах, демонстрируемых ниже, особое внимание обращайте на фактиче- ские имена устройств в вашей системе и не используйте имена, приводящиеся в примерах здесь! Также отметьте, что аудиодиск — это не то же самое, что CD- ROM. Аудиодиск не имеет файловой системы и потому не может быть смонти- рован в общепринятом смысле.
Теперь, когда мы знаем имя устройства для привода CD-ROM, размонтируем диск и повторно смонтируем его в другой каталог в дереве файловой системы. Для это- го необходимо получить права суперпользователя (способом, соответствующим вашей системе) и размонтировать диск командой umount:
[me@linuxbox ~]$ su - Password: [root@linuxbox ~]# umount /dev/sdc
Следующий шаг: создать новую точку монтирования диска. Точка монтирова- ния — это самый обычный каталог где-то в дереве файловой системы. В таком ка- талоге нет ничего необычного. Он даже не должен быть пустым каталогом, правда, монтирование устройства в непустой каталог сделает его прежнее содержимое недоступным, пока устройство не будет размонтировано. Итак, создадим новый каталог:
[root@linuxbox ~]# mkdir /mnt/cdrom
И наконец, смонтируем CD-ROM в новую точку монтирования. Параметр -t по- зволяет указать тип файловой системы:
[root@linuxbox ~]# mount -t iso9660 /dev/sdc /mnt/cdrom
Монтирование и размонтирование устройств хранения
217
ПОЧЕМУ ВАЖНО РАЗМОНТИРОВАТЬ УСТРОЙСТВА
Если взглянуть на вывод команды free, показывающей статистику использования памяти, можно увидеть статистику с названием buffers (буферы). Компьютерные системы проектируются так, чтобы работать максимально быстро. Но медленные устройства препятствуют этому. Ярким примером служат принтеры. Даже самый быстрый принтер выглядит чрезвычайно медлительным по компьютерным стан- дартам. Компьютеры работали бы крайне медленно, если бы действительно были вынуждены ждать, пока принтер завершит печать страницы. В давние времена (когда персональные компьютеры еще не были многозадачными) это представляло настоящую проблему. При попытке распечатать электронную таблицу или тексто- вый документ компьютер мог стать недоступным до конца печати. Компьютер не мог посылать данные принтеру быстрее, чем тот мог их обработать, а принтеры не могли работать быстрее, потому что не могли быстро печатать. Эта проблема была решена созданием буфера печати, устройства, содержащего некоторый объем ОЗУ и находящегося между компьютером и принтером. При наличии буфе- ра печати компьютер мог послать данные в буфер печати, который сохранял их в быстрой памяти ОЗУ, и компьютер возвращался к работе, не дожидаясь конца печати. В то же время буфер печати мог передавать данные принтеру из своей памяти со скоростью, приемлемой для принтера.
Идея буферизации широко используется для увеличения производительности компьютеров — необходимость работы с медленными устройствами не должна ухудшать производительность системы. Операционные системы хранят дан- ные, прочитанные с устройства и предназначенные для записи в устройство, так долго, насколько это возможно, и используют их, прежде чем фактически обратиться к медленному устройству. В системе Linux, например, можно за- метить, что при продолжительной работе она заполняет всю память. Это не означает, что Linux «использует» всю память, это означает лишь то, что Linux использует в своих интересах всю доступную память и буферизует как можно больше данных.
Буферизация позволяет очень быстро выполнять запись в устройства хранения, потому что запись в физическое устройство откладывается «на потом». Данные, предназначенные для устройства, накапливаются в памяти. Время от времени операционная система записывает эти данные в физическое устройство.
Размонтирование устройства влечет за собой запись всех оставшихся данных в это устройство, чтобы его можно было безопасно извлечь. Если носитель извлечь, не выполнив размонтирования, есть вероятность, что не все данные, предназначен- ные для устройства, будут записаны в него. Иногда эти данные могут включать жизненно важные обновления каталогов, отсутствие которых может привести к повреждению файловой системы — одной из самых больших неприятностей, которые могут случиться с компьютером.
218
Глава 15. Устройства хранения
После этого можно исследовать содержимое компакт-диска в новой точке монти- рования:
[root@linuxbox ~]# cd /mnt/cdrom [root@linuxbox cdrom]# ls
Обратите внимание, что происходит при попытке размонтировать компакт-диск:
[root@linuxbox cdrom]# umount /dev/sdc umount: /mnt/cdrom: device is busy
В чем причина? Устройство нельзя размонтировать, если оно используется каким- то пользователем или другим процессом. В данном случае мы изменили текущий рабочий каталог, перенеся его в точку монтирования компакт-диска, что и стало причиной занятости устройства. Эту проблему легко исправить, перенеся текущий рабочий каталог куда-нибудь в другое место за пределами точки монтирования:
[root@linuxbox cdrom]# cd [root@linuxbox ~]# umount /dev/hdc
Теперь устройство было успешно размонтировано.
Определение названий устройств
Иногда сложно определить название (имя) устройства. В прошлом это было про- ще. Устройство всегда находилось в одном месте и никогда не менялось. Unix- подобные системы именно так и действовали. Во времена, когда разрабатывалась система Unix, для «смены дискового устройства» требовалось использовать подъ- емник, чтобы извлечь из комнаты с ЭВМ устройство размером со стиральную ма- шину. В последние годы типовая аппаратная конфигурация настольного компью- тера стала намного динамичнее, и система Linux вынуждена быть более гибкой, чем ее предшественницы.
В примерах, приведенных выше, мы использовали способность современной си- стемы Linux «как по волшебству» монтировать устройства, чтобы узнавать их названия постфактум. Но как быть тем, кто управляет сервером или каким-то другим окружением, где автоматическое монтирование не поддерживается? Как в этом случае определить название устройства?
Сначала давайте посмотрим, как система выбирает названия для устройств. Если вывести содержимое каталога /dev (где живут все устройства), можно увидеть значительное число устройств:
[me@linuxbox ~]$ ls /dev
Монтирование и размонтирование устройств хранения
219
Содержимое списка показывает некоторые шаблоны в именовании устройств, не- полный список которых приводится в табл. 15.2.
Таблица 15.2. Названия устройств хранения данных в Linux
Шаблон
Устройство
/dev/fd*
/dev/hd*
Приводы гибких дисков
Диски IDE (PATA) в старых системах. Обычно материнские платы содержат два разъема, или канала IDE, к каждому из которых можно подключить шлейф, рассчитанный на два устройства. Первое устройство, подключен- ное к такому шлейфу, называется ведущим устройством (master device), а второе — подчиненным устройством (slave device). Имена устройств упорядочены так, что ведущее устройство, подключенное к первому каналу, получает имя /dev/hda, ведомое устройство, подключенное к перво- му каналу, получает имя /dev/hdb; ведущее устройство, подключенное ко второму каналу, получает имя /dev/hdc, и т. д. Цифра в конце определяет номер раздела на устройстве. Например, имя /dev/hda1 соответствует первому разделу на первом жестком диске в системе, тогда как имя /dev/ hda соответствует всему устройству в целом
/dev/lp*
Принтеры
/dev/sd*
Диски SCSI. В последних версиях системы Linux ядро интерпретирует все дисковые устройства (включая жесткие диски PATA/SATA, флеш-диски и съемные накопители USB, такие как портативные музыкальные плееры и цифровые камеры) как диски SCSI. В остальном система именования напоминает прежнюю систему /dev/hd*, описанную выше
/dev/sr*
Приводы оптических дисков (приводы CD/DVD, как пишущие, так и нет)
Кроме того, во многих системах можно увидеть такие символические ссылки, как /dev/cdrom, /dev/dvd и /dev/floppy, которые ссылаются на фактические файлы устройств и предусмотрены для удобства.
Если вам доведется работать в системе, которая не монтирует автоматически съемные носители, вы можете использовать следующий прием для определения названий таких устройств после их подключения. Сначала запустите мониторинг содержимого файла /var/log/messages или /var/log/syslog в режиме реального вре- мени (для этого могут потребоваться права суперпользователя):
[me@linuxbox ~]$ sudo tail -f /var/log/messages
Эта команда выведет несколько последних строк из файла и приостановится. Далее подключите извлекаемое устройство. В этом примере мы использовали
220
Глава 15. Устройства хранения
16-мегабайтный флеш-диск. Практически сразу же ядро обнаружит новое устрой- ство и проверит его:
Jul 23 10:07:53 linuxbox kernel: usb 3-2: new full speed USB device using uhci_hcd and address 2 Jul 23 10:07:53 linuxbox kernel: usb 3-2: configuration #1 chosen from 1 choice Jul 23 10:07:53 linuxbox kernel: scsi3 : SCSI emulation for USB Mass Storage devices Jul 23 10:07:58 linuxbox kernel: scsi scan: INQUIRY result too short (5), using 36 Jul 23 10:07:58 linuxbox kernel: scsi 3:0:0:0: Direct-Access Easy Disk 1.00 PQ: 0 ANSI: 2 Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] 31263 512-byte hardware sectors (16 MB) Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] Write Protect is off Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] Assuming drive cache: write through Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] 31263 512-byte hardware sectors (16 MB) Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] Write Protect is off Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] Assuming drive cache: write through Jul 23 10:07:59 linuxbox kernel: sdb: sdb1 Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] Attached SCSI removable disk Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: Attached scsi generic sg3 type 0
Когда вывод опять приостановится, нажмите CTRL+C, чтобы вернуться в пригла- шение командной строки. Наибольший интерес для нас представляют строки с упоминанием имени устройства [sdb], соответствующего нашим ожиданиям в отношении названия устройства диска SCSI. Cледующие две строки являются для нас особенно показательными:
Jul 23 10:07:59 linuxbox kernel: sdb: sdb1 Jul 23 10:07:59 linuxbox kernel: sd 3:0:0:0: [sdb] Attached SCSI removable disk
Они сообщают, что имя /dev/sdb соответствует всему устройству, а имя /dev/ sdb1 — первому разделу на этом устройстве. Как видите, работая с Linux, иногда приходится проводить массу интересных детективных расследований!
СОВЕТ
Прием с использованием команды tail -f /var/log/messages демонстрирует отличный способ наблюдения за происходящим в системе в режиме реального времени.
Создание новых файловых систем
221
Зная имя устройства, можно смонтировать флеш-диск:
[me@linuxbox ~]$ sudo mkdir /mnt/flash [me@linuxbox ~]$ sudo mount /dev/sdb1 /mnt/flash [me@linuxbox ~]$ df Filesystem 1K-blocks Used Available Use% Mounted on /dev/sda2 15115452 5186944 9775164 35% / /dev/sda5 59631908 31777376 24776480 57% /home /dev/sda1 147764 17277 122858 13% /boot tmpfs 776808 0 776808 0% /dev/shm /dev/sdb1 15560 0 15560 0% /mnt/flash
Имя устройства сохраняется неизменным, пока оно остается физически подклю- ченным к компьютеру и до перезагрузки компьютера.
Создание новых файловых систем
Представьте, что вам нужно отформатировать флеш-диск и вместо файловой си- стемы FAT32 создать на нем файловую систему, родную для Linux. Для этого сле- дует выполнить две операции:
- Создать (при необходимости) новое распределение разделов, если имеющееся
вас не устраивает.
- Создать новую, пустую файловую систему.
ВНИМАНИЕ
Следующее упражнение производит форматирование флеш-диска. Используйте диск, не содержащий ничего, что вам было бы нужно, потому что вся информа- ция на диске будет стерта! И снова: убедитесь, что используете имя устройства, верное для вашей системы, а не то, которое показано в примере. Игнорирование этого предупреждения может привести к форматированию (то есть к стиранию) другого диска!
Управление разделами с помощью fdisk
Программа fdisk позволяет напрямую выполнять низкоуровневые операции с дисковыми устройствами (такими, как жесткие диски и флеш-диски). С по- мощью этого инструмента можно изменять, удалять и создавать разделы на устройстве. Чтобы приступить к работе с флеш-диском, его нужно сначала
222
Глава 15. Устройства хранения
размонтировать (если прежде он был смонтирован) и затем запустить программу fdisk, как показано ниже:
[me@linuxbox ~]$ sudo umount /dev/sdb1 [me@linuxbox ~]$ sudo fdisk /dev/sdb
Обратите внимание, что здесь нужно указать имя, соответствующее устройству целиком, то есть всему устройству, без номера раздела. После запуска программы вы увидите следующее приглашение:
Команда (m для справки):
После ввода команды m на экране появится меню программы:
Команда описание a изменить флаг загрузочного раздела b изменить вложенную BSD-метку диска c переключить флаг совместимости с DOS d удалить раздел l список известных типов разделов m показать это меню n добавить новый раздел p показать таблицу разделов q выйти без сохранения изменений s создать новую пустую таблицу разделов Sun t изменить тип раздела u изменить единицы отображения/ввода v проверить таблицу разделов w сохранить таблицу на диск и выйти x дополнительные функции (только для экспертов)
Команда (m для справки):
Первое, что следует сделать, — исследовать список имеющихся разделов. Для это- го введите команду p, она выведет таблицу разделов на устройстве:
Command (m for help): p Disk /dev/sdb: 16 MB, 16006656 bytes 1 heads, 31 sectors/track, 1008 cylinders Units = cylinders of 31 * 512 = 15872 bytes
Device Boot Start End Blocks Id System /dev/sdb1 2 1008 15608+ b W95 FAT32
Обратите внимание, что устройство имеет объем 16 Мбайт и единственный раз- дел (1), занимающий 1006 цилиндров из 1008 доступных на устройстве. Раздел
Создание новых файловых систем
223
идентифицирован как раздел Windows 95 FAT32. Некоторые программы исполь- зуют этот идентификатор, ограничивая виды операций с диском, но чаще изме- нение идентификатора не влечет серьезных последствий. Однако ради демон- страции мы изменим его, чтобы показать, что это раздел Linux. Для этого нужно сначала узнать, какой идентификатор обозначает разделы Linux. В листинге, при- веденном выше, мы видели, что существующий раздел имеет идентификатор b (столбец Id). Чтобы увидеть список известных типов разделов, вернитесь к меню программы и обратите внимание на пункт:
l список известных типов разделов
Если ввести команду l, появится длинный список допустимых типов разделов. Среди них можно увидеть идентификатор b типа существующего раздела и иден- тификатор 83 для Linux. Вернемся обратно к меню программы, где можно увидеть команду изменения идентификатора раздела:
t изменить тип раздела
Введите t и затем новый идентификатор:
Command (m for help): t Selected partition 1 Hex code (type L to list codes): 83 Changed system type of partition 1 to 83 (Linux)
Это все изменения, которые нам нужно было сделать. До этого момента никаких изменений на самом устройстве не было произведено (все изменения пока просто зафиксированы в памяти программы, а не на физическом устройстве), поэтому теперь запишем измененную таблицу разделов на устройство и выйдем. Для этого введите команду w:
Command (m for help): w The partition table has been altered!
Calling ioctl() to re-read partition table.
WARNING: If you have created or modified any DOS 6.x partitions, please see the fdisk manual page for additional information. Syncing disks. [me@linuxbox ~]$
Если бы мы решили оставить устройство в неизменном состоянии, то могли бы ввести команду q и покинуть программу без записи изменений на устройство.
224
Глава 15. Устройства хранения
Предупреждающее сообщение, выглядящее зловещим, можно просто игнори- ровать1.
Создание новой файловой системы с помощью mkfs
Завершив редактирование разделов (довольно простое, хотя так бывает не всегда), мы создадим на флеш-диске новую файловую систему. Для этого воспользуемся программой mkfs (сокращенно от make filesystem — создать файловую систему), спо- собной создавать разные файловые системы. Чтобы создать на устройстве файло- вую систему ext4, следует передать команде параметр -t с типом файловой системы ext4, затем указать имя устройства и раздел, который требуется отформатировать:
[me@linuxbox ~]$ sudo mkfs -t ext4 /dev/sdb1 mke2fs 2.23.2 (12-Jul-2011) Filesystem label= OS type: Linux Block size=1024 (log=0) Fragment size=1024 (log=0) 3904 inodes, 15608 blocks 780 blocks (5.00%) reserved for the super user First data block=1 Maximum filesystem blocks=15990784 2 block groups 8192 blocks per group, 8192 fragments per group 1952 inodes per group Superblock backups stored on blocks: 8193
Writing inode tables: done Creating journal (1024 blocks): done Writing superblocks and filesystem accounting information: done
This filesystem will be automatically checked every 34 mounts or 180 days, whichever comes first. Use tune2fs -c or -i to override. [me@linuxbox ~]$
Когда выбирается тип файловой системы ext4, программа выводит массу ин- формации. Чтобы восстановить на устройстве оригинальную файловую систему FAT32, следует указать тип файловой системы vfat:
[me@linuxbox ~]$ sudo mkfs -t vfat /dev/sdb1
1 Оно гласит: «ВНИМАНИЕ: если вы создали или изменили разделы DOS 6.x, прочитай- те дополнительную информацию на странице справочного руководства для команды fdisk». — Примеч. пер.
Проверка и восстановление файловой системы
225
Эту процедуру с редактированием разделов и форматированием можно повторять с любыми дополнительными устройствами хранения, подключаемыми к системе. Хотя в данном примере мы работали с маленьким флеш-диском, ту же процеду- ру можно применить и к внутренним жестким дискам, и к другим извлекаемым устройствам хранения, таким как жесткие USB-диски.
Проверка и восстановление файловой системы
Знакомясь с файлом /etc/fstab, мы видели некие странные цифры в конце каждой строки. Каждый раз, когда система загружается, она проверяет целостность фай- ловых систем перед их монтированием. Эту проверку выполняет программа fsck (сокращенно от filesystem check — проверка файловой системы). Последнее число в каждой записи в файле fstab определяет порядок проверки файловых систем. В примере, приведенном выше, видно, что корневая файловая система проверяет- ся первой, вслед за ней проверяются файловые системы home и boot. Устройства с нулем в последнем поле не проверяются стандартными механизмами.
Программа fsck может не только проверить целостность, но и восстановить по- врежденные файловые системы с той или иной степенью успеха в зависимости от масштаба повреждений. В Unix-подобных системах восстановленные фрагменты файлов помещаются в каталог lost+found, находящийся в корне каждой файловой системы.
Проверить наш флеш-диск (который предварительно необходимо размонтиро- вать) можно с помощью следующей команды:
[me@linuxbox ~]$ sudo fsck /dev/sdb1 fsck 1.40.8 (13-Mar-2016) e2fsck 1.40.8 (13-Mar-2016) /dev/sdb1: clean, 11/3904 files, 1661/15608 blocks
В настоящее время файловые системы повреждаются крайне редко, если нет ника- ких проблем с аппаратной частью, таких как выход из строя привода диска. В боль- шинстве файловых систем обнаруженные на этапе загрузки повреждения вызывают остановку системы с выводом предложения запустить fsck перед продолжением.
ЧТО ТАКОЕ FSCK?
В культуре Unix слово «fsck» часто используется взамен распространенного руга- тельства, в котором три буквы совпадают с буквами в имени команды. Это показа- тельно — вы почти наверняка будете произносить упомянутое слово, оказавшись в ситуации, вынуждающей запустить fsck.
226
Глава 15. Устройства хранения
Непосредственное перемещение данных между устройствами
Обычно на компьютерах мы работаем с данными, организованными в файлы, однако точно так же можно работать с данными в «низкоуровневой» форме. Если взгля- нуть на содержимое диска, можно увидеть, что оно состоит из множества «блоков» данных, которые операционная система интерпретирует как файлы и каталоги. Если бы мы умели интерпретировать диски как простые коллекции блоков данных, мы смогли бы выполнять множество полезных задач, таких как клонирование дисков.
Эту задачу решает программа dd. Она копирует блоки данных из одного места в другое. По историческим причинам команда имеет уникальный синтаксис:
dd if=входной_файл of=выходной_файл [bs=размер_блока [count=число_блоков]]
ВНИМАНИЕ
dd — очень мощная команда. Ее название происходит от data definition (опреде- ление данных), но иногда его расшифровывают как destroy disk (уничтожить диск), потому что пользователи часто допускают ошибки в параметрах if и of. Всегда дважды проверяйте их, прежде чем нажать ENTER!
Представьте, что у вас есть два флеш-диска USB одинакового размера и вам нужно создать точную копию первого диска на втором. Допустим, что после подключе- ния к компьютеру им назначаются имена устройств /dev/sdb и /dev/sdc соответ- ственно. В этом случае скопировать содержимое первого диска на второй можно следующей командой:
dd if=/dev/sdb of=/dev/sdc
Как вариант, если к компьютеру подключено только первое устройство, можно скопировать его содержимое в обычный файл, который впоследствии использо- вать для восстановления или копирования:
dd if=/dev/sdb of=flash_drive.img
Создание образа компакт-диска
Запись на компакт-диски (CD-R или CD-RW) выполняется в два этапа.
Создается файл образа ISO, являющийся точным образом файловой системы
компакт-диска.
Файл образа записывается на носитель (то есть на сам компакт-диск).
Непосредственное перемещение данных между устройствами
227
Создание образа-копии компакт-диска
Чтобы создать ISO-образ имеющегося компакт-диска, необходимо с помощью dd прочитать все блоки с данными с этого компакт-диска и скопировать их в ло- кальный файл. Например, допустим, что у нас есть компакт-диск с дистрибути- вом Ubuntu, и мы хотим создать файл ISO-образа, который потом можно будет использовать для создания нескольких копий. Вставив компакт-диск в привод CD-ROM и определив имя устройства (пусть это будет /dev/cdrom), мы сможем создать файл ISO-образа следующим способом:
dd if=/dev/cdrom of=ubuntu.iso
Этот прием также применим к дискам DVD с данными, но он не будет работать с аудиодисками, так как для хранения данных на них файловая система не ис- пользуется. Если вы хотите скопировать аудиодиск, обратитесь к команде cdrdao.
Создание образа из коллекции файлов
Создать файл ISO-образа, включающий содержимое некоего каталога, можно с помощью программы genisoimage. Для этого сначала создадим каталог со все- ми необходимыми файлами для включения в образ и затем командой genisoimage создадим файл образа. Например, если предположить, что вы создали каталог ~/cd-rom-files и наполнили его файлами для записи на компакт-диск, следующая команда создаст файл образа с именем cd-rom.iso:
genisoimage -o cd-rom.iso -R -J ~/cd-rom-files
Параметр -R требует добавить метаданные расширений Rock Ridge, позволяющих использовать длинные имена файлов и права доступа к файлам в стиле POSIX. Аналогично, параметр -J включает расширения Joliet, разрешающие использовать длинные имена файлов в Windows.
ЧТО В ИМЕНИ ТВОЕМ…
В руководствах по созданию и записи оптических дисков, таких как CD-ROM и DVD, которых в избытке на просторах Интернета, часто можно встретить упомина- ние двух программ, mkisofs и cdrecord. Эти программы были некогда частью популярного пакета cdrtools, созданного Йоргом Шиллингом (Jörg Schilling). Летом 2006-го мистер Шиллинг изменил лицензию в части, касающейся пакета cdrtools, из-за чего она, по мнению многих в сообществе пользователей Linux, стала несовместимой с GNU GPL. Как результат, на основе cdrtools был создан альтернативный проект, включающий программы wodim и genisoimage взамен cdrecord и mkisofs соответственно.
228
Глава 15. Устройства хранения
Запись образа компакт-диска
После подготовки файла образа его можно записать на оптический носитель. Большинство команд, обсуждаемых ниже, применимы и для записи на носители CD-ROM и DVD.
Непосредственное монтирование файла ISO-образа
Существует один трюк, позволяющий монтировать ISO-образы, хранящиеся на жестком диске, и работать с ними, как если бы это были оптические носители. Параметр -o loop, добавленный в команду mount (вместе с обязательным пара- метром -t iso9660, определяющим тип файловой системы), позволяет смонти- ровать файл образа в дерево файловой системы, как если бы это было обычное устройство:
mkdir /mnt/iso_image mount -t iso9660 -o loop image.iso /mnt/iso_image
В примере, приведенном выше, мы создали точку монтирования с именем /mnt/ iso_image и затем смонтировали в нее файл образа image.iso. После монтирования образа с ним можно работать как с настоящим диском CD-ROM или DVD. Не за- будьте размонтировать образ, когда он станет не нужен.
Очистка перезаписываемых компакт-дисков
Перезаписываемые компакт-диски CD-RW нужно стирать, или очищать, перед повторным использованием. Для этого воспользуемся командой wodim, указав ей имя устройства пишущего привода компакт-дисков и тип очистки. Программа wodim предлагает несколько типов очистки. Для минимальной (и самой быстрой) очистки следует указать тип fast:
wodim dev=/dev/cdrw blank=fast
Запись образа
Записать образ можно с помощью все той же программы wodim, указав ей имя устройства пишущего привода компакт-дисков и имя файла образа:
wodim dev=/dev/cdrw image.iso
Дополнительные сведения
229
Помимо имени устройства и файла образа программа wodim поддерживает массу дополнительных параметров. Чаще других используются параметры -v (обеспе- чивает вывод подробной информации в ходе записи) и -dao (выполняет запись на диск в режиме disc-at-once — диск целиком). Режим «диск целиком» следует ис- пользовать, если вы собираетесь воспроизводить диски в коммерческих целях. По умолчанию wodim использует режим track-at-once (по одной дорожке), который хорошо подходит для записи музыкальных треков.
Заключительное замечание
В этой главе мы познакомились с основами управления устройствами хранения данных. Эту тему можно было бы продолжать и продолжать. Linux поддерживает широкий спектр устройств хранения и файловых систем, а также предлагает раз- нообразные возможности для взаимодействий с другими системами.
Дополнительные сведения
Часто бывает полезно проверить целостность ISO-образа, загруженного из Ин- тернета. В большинстве случаев распространители ISO-образов сопровождают их файлами с контрольными суммами. Контрольная сумма — это результат экзоти- ческих математических вычислений в виде числа, представляющего содержимое целевого файла. Если содержимое файла образа изменится хотя бы в одном бите, его контрольная сумма будет отличаться от указанной распространителем. Для вычисления контрольной суммы чаще всего используется программа md5sum, воз- вращающая уникальное шестнадцатеричное число:
md5sum image.iso 34e354760f9bb7fbf85c96f6a3f94ece image.iso
Загрузив образ, запустите md5sum для него и сравните результат работы md5sum со значением, указанным распространителем.
Помимо проверки целостности загруженного файла, программу md5sum можно использовать для проверки вновь записанного оптического носителя. Для этого сначала вычислите контрольную сумму для файла образа, а затем — для носителя. Вся хитрость проверки носителя заключается в том, чтобы ограничить вычисле- ния частью оптического носителя, содержащей образ. Для этого определите число
230
Глава 15. Устройства хранения
2048-байтных блоков в образе (запись на оптические носители всегда выполня- ется блоками по 2048 байт) и прочитайте с носителя ровно столько блоков. Для некоторых типов носителей это не обязательно. Например, компакт-диск, запи- санный в режиме disc-at-once, можно проверить так:
md5sum /dev/cdrom 34e354760f9bb7fbf85c96f6a3f94ece /dev/cdrom
Многие типы носителей, такие как DVD, требуют точного вычисления числа бло- ков. Следующий пример демонстрирует проверку целостности файла образа dvd- image.iso и диска в устройстве /dev/dvd привода DVD. Вам понятно, как работает эта команда?
md5sum dvd-image.iso; dd if=/dev/dvd bs=2048 count=$(( $(stat -c “%s” dvd-image. iso) / 2048 )) | md5sum
16 Сети
Когда дело доходит до сетевых возможностей, трудно представить что-то, что было бы невозможно для Linux. Linux используется для создания всех видов се- тевых систем, программных компонентов и устройств, включая брандмауэры, маршрутизаторы, серверы имен, сетевые устройства хранения данных (Network- Attached Storage, NAS) и так далее и тому подобное.
Насколько обширна тема сетей, настолько же обширна коллекция команд, ко- торые можно использовать для настройки и управления ими. Мы сосредоточим свое внимание лишь на тех из них, которые чаще всего используются на практике. В число команд, выбранных для исследования в этой главе, входят команды, ис- пользуемые для мониторинга сетей и передачи файлов. Дополнительно мы иссле- дуем программу ssh, используемую для входа в удаленные системы. В этой главе рассматриваются следующие команды:
ping — посылает пакеты ICMP ECHO_REQUEST узлам в сети;
traceroute — выводит трассировку маршрута пакетов к сетевому узлу;
ip — отображает информацию о маршрутах, устройствах, политиках маршрутиза- ции и туннелях и позволяет управлять ими;
netstat — выводит список сетевых соединений, таблицы маршрутов, статистику интерфейсов, маскируемые соединения и сведения о членстве в широковещатель- ных группах;
ftp — программа передачи файлов через Интернет;
wget — неинтерактивный загрузчик файлов из сети;
ssh — клиент OpenSSH SSH (программа для входа в удаленные системы).
232
Глава 16. Сети
Далее предполагается, что вы имеете некоторые базовые знания о сетях. В нашем мире повсеместного распространения Интернета каждый пользователь компью- тера должен иметь представление о том, как действуют сети, хотя бы на элемен- тарном уровне. Для полноценного использования этой главы вы должны знать следующие термины:
IP-адрес (адрес протокола Интернета).
Имя хоста и домена.
URI (Uniform Resource Identifier — унифицированный идентификатор ре-
сурса).
ПРИМЕЧАНИЕ
Для доступа к некоторым командам из рассматриваемых ниже может потребо- ваться установить дополнительные пакеты (в зависимости от дистрибутива) из репозиториев вашего дистрибутива, и некоторые из них могут требовать привилегий суперпользователя.
Исследование и мониторинг сети
Даже если вы не являетесь системным администратором, бывает полезно уметь оценивать производительность и функционирование сети.
ping
Команда ping является самой простой сетевой командой. Она посылает специаль- ные сетевые пакеты IMCP ECHO_REQUEST указанному сетевому узлу. Боль- шинство сетевых устройств принимает эти пакеты и отвечает на них, — это позво- ляет проверить сетевые соединения.
ПРИМЕЧАНИЕ
Многие сетевые устройства (в том числе и компьютеры с Linux) могут на- страиваться так, чтобы игнорировать эти пакеты. Обычно это делается для по вышения безопасности и отчасти — чтобы ввести в заблуждение потен- циального злоумышленника. Кроме того, многие брандмауэры блокируют трафик IMCP.
Исследование и мониторинг сети
233
Например, с помощью команды ping можно проверить достижимость сетевого узла http://www.linuxcommand.org/ (один из моих любимых сайтов ;-)):
[me@linuxbox ~]$ ping linuxcommand.org
Сразу после запуска программа ping начинает посылать пакеты с определенным интервалом (по умолчанию 1 секунда), пока ее выполнение не будет прервано:
[me@linuxbox ~]$ ping linuxcommand.org PING linuxcommand.org (66.35.250.210) 56(84) bytes of data. 64 bytes from vhost.sourceforge.net (66.35.250.210): icmp_seq=1 ttl=43 time=107 ms 64 bytes from vhost.sourceforge.net (66.35.250.210): icmp_seq=2 ttl=43 time=108 ms 64 bytes from vhost.sourceforge.net (66.35.250.210): icmp_seq=3 ttl=43 time=106 ms 64 bytes from vhost.sourceforge.net (66.35.250.210): icmp_seq=4 ttl=43 time=106 ms 64 bytes from vhost.sourceforge.net (66.35.250.210): icmp_seq=5 ttl=43 time=105 ms 64 bytes from vhost.sourceforge.net (66.35.250.210): icmp_seq=6 ttl=43 time=107 ms
— linuxcommand.org ping statistics — 6 packets transmitted, 6 received, 0% packet loss, time 6010ms rtt min/avg/max/mdev = 105.647/107.052/108.118/0.824 ms
После прерывания нажатием CTRL+C (в данном примере после шестого пакета) ping выводит результаты своей работы. Если сеть функционирует должным об- разом, число потерянных пакетов (packet loss) будет составлять ноль процентов. Успешная работа ping может служить признаком того, что сетевые компоненты (интерфейсные карты, кабели, маршрутизаторы и шлюзы) находятся в рабочем состоянии.
traceroute
Программа traceroute (в некоторых системах используется похожая на нее про- грамма tracepath) выводит список всех «переходов» (hops) на пути сетевого тра- фика между локальной системой и указанным узлом сети. Например, увидеть, как выглядит маршрут к сайту http://www.slashdot.org/, можно с помощью следующей команды:
[me@linuxbox ~]$ traceroute slashdot.org
Ее вывод выглядит примерно так:
traceroute to slashdot.org (216.34.181.45), 30 hops max, 40 byte packets 1 ipcop.localdomain (192.168.1.1) 1.066 ms 1.366 ms 1.720 ms 2 * * *
234
Глава 16. Сети
3 ge-4-13-ur01.rockville.md.bad.comcast.net (68.87.130.9) 14.622 ms 14.885 ms 15.169 ms 4 po-30-ur02.rockville.md.bad.comcast.net (68.87.129.154) 17.634 ms 17.626 ms 17.899 ms 5 po-60-ur03.rockville.md.bad.comcast.net (68.87.129.158) 15.992 ms 15.983 ms 16.256 ms 6 po-30-ar01.howardcounty.md.bad.comcast.net (68.87.136.5) 22.835 ms 14.23 3 ms 14.405 ms 7 po-10-ar02.whitemarsh.md.bad.comcast.net (68.87.129.34) 16.154 ms 13.600 ms 18.867 ms 8 te-0-3-0-1-cr01.philadelphia.pa.ibone.comcast.net (68.86.90.77) 21.951 ms 21.073 ms 21.557 ms 9 pos-0-8-0-0-cr01.newyork.ny.ibone.comcast.net (68.86.85.10) 22.917 ms 21 .884 ms 22.126 ms 10 204.70.144.1 (204.70.144.1) 43.110 ms 21.248 ms 21.264 ms 11 cr1-pos-0-7-3-1.newyork.savvis.net (204.70.195.93) 21.857 ms cr2-pos-0-0-3-1. newyork.savvis.net (204.70.204.238) 19.556 ms cr1-pos-0-7-3-1.newyork.savvis.net (204.70.195.93) 19.634 ms 12 cr2-pos-0-7-3-0.chicago.savvis.net (204.70.192.109) 41.586 ms 42.843 ms cr2- tengig-0-0-2-0.chicago.savvis.net (204.70.196.242) 43.115 ms 13 hr2-tengigabitethernet-12-1.elkgrovech3.savvis.net (204.70.195.122) 44.215 ms 41.833 ms 45.658 ms 14 csr1-ve241.elkgrovech3.savvis.net (216.64.194.42) 46.840 ms 43.372 ms 47.041 ms 15 64.27.160.194 (64.27.160.194) 56.137 ms 55.887 ms 52.810 ms 16 slashdot.org (216.34.181.45) 42.727 ms 42.016 ms 41.437 ms
Здесь можно видеть, что на пути между нашей тестовой системой и http://www. slashdot.org/ находится 16 маршрутизаторов. Для маршрутизаторов, предостав- ляющих идентификационную информацию, выводятся имена хостов, IP-адреса и информация о производительности, которая включает три интервала времени, понадобившихся для передачи/подтверждения пакетов между локальной систе- мой и маршрутизатором. Для маршрутизаторов, не предоставляющих идентифи- кационной информации (например, из-за особенностей настройки маршрутиза- тора, заторов в сети, действий брандмауэров и т. д.), выводятся звездочки, как это можно видеть в строке, соответствующей второму переходу. Иногда, когда инфор- мация о маршрутизаторе блокируется, это ограничение можно преодолеть, пере- дав команде traceroute параметр -T или -I.
ip
Программа ip — это многофункциональный инструмент для настройки пара- метров подключения к сети, использующий весь спектр сетевых функций, до- ступных в современных ядрах Linux. Она пришла на замену ныне устаревшей
Исследование и мониторинг сети
235
программе ifconfig. С помощью ip можно исследовать сетевые интерфейсы и та- блицу маршрутизации системы.
[me@linuxbox ~]$ ip a 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether ac:22:0b:52:cf:84 brd ff:ff:ff:ff:ff:ff inet 192.168.1.14/24 brd 192.168.1.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::ae22:bff:fe52:cf84/64 scope link valid_lft forever preferred_lft forever
Пример, приведенный выше, показывает, что наша тестовая система имеет два се- тевых интерфейса. Первый, с именем lo, — это петлевой интерфейс (loopback), виртуальный интерфейс, который система использует, чтобы разговаривать «сама с собой»; второй, с именем eth0, — это интерфейс Ethernet.
Выполняя причинно-следственную диагностику, первое, на что следует обра- тить внимание, — наличие слова UP в первой строке с информацией о каждом интерфейсе, указывающего, что сетевой интерфейс включен, и присутствие допустимого IP-адреса в поле inet во третьей строке. Для систем, использую- щих протокол динамической настройки хостов (Dynamic Host Configuration Protocol, DHCP), наличие допустимого IP-адреса в этом поле подтвердит нор- мальную работу DHCP.
netstat
Программа netstat используется для исследования различных настроек сети и статистик. С помощью множества параметров этой команды можно просматри- вать самые разные аспекты настройки сети. С помощью параметра -ie, например, можно исследовать сетевые интерфейсы в системе:
[me@linuxbox ~]$ netstat -ie eth0 Link encap:Ethernet HWaddr 00:1d:09:9b:99:67 inet addr:192.168.1.2 Bcast:192.168.1.255 Mask:255.255.255.0
236
Глава 16. Сети
inet6 addr: fe80::21d:9ff:fe9b:9967/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:238488 errors:0 dropped:0 overruns:0 frame:0
TX packets:403217 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:100
RX bytes:153098921 (146.0 MB) TX bytes:261035246 (248.9 MB)
Memory:fdfc0000-fdfe0000
lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:2208 errors:0 dropped:0 overruns:0 frame:0 TX packets:2208 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:111490 (108.8 KB) TX bytes:111490 (108.8 KB)
Использование параметра -r позволит получить таблицу маршрутизации ядра. По этой таблице можно судить, как настроена передача пакетов между сетями:
[me@linuxbox ~]$ netstat -r Таблица маршутизации ядра протокола IP Destination Gateway Genmask Flags MSS Window irtt Iface 192.168.1.0 * 255.255.255.0 U 0 0 0 eth0 default 192.168.1.1 0.0.0.0 UG 0 0 0 eth0
В этом простом примере представлена типичная таблица маршрутизации для клиентской машины, подключенной к локальной сети (Local Area Network, LAN), находящейся за брандмауэром/маршрутизатором. В первой строке демонстри- руется адрес назначения 192.168.1.0. IP-адреса, оканчивающиеся нулем, соот- ветствуют целым сетям, а не отдельным узлам в них, поэтому такой адрес под- разумевает: «любой узел в локальной сети». Следующее поле, Gateway (шлюз), определяет имя или IP-адрес шлюза (маршрутизатора) для выхода текущего узла в указанную сеть. Звездочка в этом поле указывает, что использовать шлюз не тре- буется.
В последней строке в качестве адреса назначения указано слово default (по умол- чанию). Эта строка управляет трафиком, адресованным любым сетям, не перечис- ленным в таблице. В данном примере видно, что роль шлюза выполняет марш- рутизатор с адресом 192.168.1.1, который, по всей видимости, знает, что делать с трафиком.
Так же как ip, программа netstat имеет множество параметров, из которых мы рассмотрели только пару. Полный их список вы найдете на странице справочного руководства (man) для netstat.
Передача файлов по сети
237
Передача файлов по сети
Что толку от сети, если не знать, как перемещать файлы через нее? Существует множество программ, перемещающих данные по сети. Сейчас мы рассмотрим две из них, а еще несколько — в последующих разделах.
ftp
Одна из по-настоящему «классических» программ — ftp — получила свое имя от используемого ею протокола, протокола передачи файлов (File Transfer Protocol, FTP). Протокол FTP широко используется в Интернете для передачи файлов. Он поддерживается большинством веб-браузеров, если не всеми, и вам часто будут встречаться идентификаторы URI, начинающиеся с префикса протоко- ла ftp://.
Программа ftp появилась задолго до веб-браузеров. Она использовалась для об- мена данными с серверами FTP, компьютерами, хранящими файлы, которые мож- но выгружать и загружать по сети.
Протокол FTP (в своем первоначальном виде) небезопасен, потому что пере- сылает имена и пароли в открытом текстовом виде. То есть они не шифруются, и любой, кто способен перехватить сетевой трафик, сможет увидеть их. По этой причине практически все операции по протоколу FTP в Интернете выполняются анонимными северами FTP. Анонимный сервер позволяет любому желающему подключиться с учетной записью anonymous без пароля.
В следующем примере показан типичный сеанс работы с программой ftp для загрузки ISO-образа с дистрибутивом Ubuntu из каталога /pub/cd_images/ Ubuntu-18.04 анонимного сервера FTP fileserver.
[me@linuxbox ~]$ ftp fileserver Connected to fileserver.localdomain. 220 (vsFTPd 2.0.1) Name (fileserver:me): anonymous 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> cd pub/cd_images/Ubuntu-18.04 250 Directory successfully changed. ftp> ls 200 PORT command successful. Consider using PASV.
238
Глава 16. Сети
150 Here comes the directory listing. -rw-rw-r– 1 500 500 733079552 Apr 25 03:53 ubuntu-18.04-desktop- amd64.iso 226 Directory send OK. ftp> lcd Desktop Local directory now /home/me/Desktop ftp> get ubuntu-18.04-desktop-amd64.iso local: ubuntu-18.04-desktop-amd64.iso remote: ubuntu-18.04-desktop-amd64.iso 200 PORT command successful. Consider using PASV. 150 Opening BINARY mode data connection for ubuntu-18.04-desktop-amd64.iso (733079552 bytes). 226 File send OK. 733079552 bytes received in 68.56 secs (10441.5 kB/s) ftp> bye
В табл. 16.1 приводится описание команд, вводившихся в ходе этого сеанса.
Таблица 16.1. Примеры команд интерактивного сеанса ftp
Команда
ftp fileserver
anonymous
cd pub/cd_images/Ubuntu-18.04
ls
lcd Desktop
Значение
Вызывает программу ftp и предлагает ей подключиться к серверу fileserver
Имя пользователя для входа. После ввода имени пользователя появится приглашение ввести пароль. Некоторые серверы прини- мают пустой пароль. Другие могут требовать пароль в формате адреса электронной почты. В данном случае попробуйте ввести что- нибудь похожее на user@example.com
Выполняет переход в каталог в удаленной системе, где находится требуемый файл. Обратите внимание, что на большинстве ано- нимных серверов FTP файлы, доступные для загрузки любым желающим, находятся где-то в каталоге pub
Выводит содержимое каталога в удаленной системе
Выполняет переход в каталог ~/Desktop в ло- кальной системе. В этом примере программа ftp была вызвана в текущем рабочем ката- логе ~. Данная команда назначает текущим рабочий каталог ~/Desktop
Передача файлов по сети
239
Команда
Значение
get ubuntu-18.04-desktop-amd64.iso
bye
Посылает удаленной системе запрос на передачу файла ubuntu-18.04-desktop-amd64. iso локальной системе. Так как в локальной системе текущим рабочим выбран каталог ~/Desktop, файл будет загружен в него
Выходит из удаленной системы и завершает сеанс программы ftp. Так же можно исполь- зовать команды quit и exit
Если в приглашении ftp> ввести команду help, программа выведет список под- держиваемых команд. С помощью программы ftp можно выполнять множество обычных операций с файлами на сервере, правда, при наличии достаточных при- вилегий. Это не очень удобно, но выполнимо.
lftp — более удачная версия ftp
ftp — не единственный клиент FTP командной строки. В действительности та- ких клиентов множество. Одним из лучших (и более популярным) считается lftp Александра Лукьянова (Alexander Lukyanov). Этот клиент действует почти так же, как традиционная программа ftp, но имеет множество дополнительных функций, включая поддержку нескольких протоколов (в том числе и HTTP), возможность автоматического восстановления прервавшейся загрузки, вы- полнение операций в фоновом режиме, автодополнение путей по клавише Tab и многое другое.
wget
wget — еще одна популярная программа командной строки для загрузки файлов. Ее удобно использовать для загрузки содержимого веб- и FTP-сайтов. С помо- щью wget можно загрузить один файл, несколько файлов и даже целый сайт. На- пример, загрузить первую страницу сайта http://www.linuxcommand.org/ можно командой:
[me@linuxbox ~]$ wget http://linuxcommand.org/index.php –11:02:51– http://linuxcommand.org/index.php => `index.php’ Распознаётся linuxcommand.org… 66.35.250.210 Подключение к linuxcommand.org|66.35.250.210|:80… соединение установлено.
240
Глава 16. Сети
HTTP-запрос отправлен. Ожидание ответа… 200 OK Длина: 3808 (3.7K) [text/html] Сохранение в: “index.php”
[ ] 3,120 --.--K/s
11:02:51 (161.75 MB/s) - `index.php’ сохранён [3808/30808]
Большинство параметров, поддерживаемых программой wget, позволяет орга- низовать рекурсивную загрузку, загрузку файлов в фоновом режиме (позволяет выйти из системы без остановки загрузки) и догружать частично загруженные файлы. Эти возможности хорошо описаны на странице справочного руководства (man).
Безопасные взаимодействия с удаленными узлами
Уже много лет Unix-подобные операционные системы поддерживают возмож- ность удаленного администрирования по сети. На первом этапе, еще до повсемест- ного распространения Интернета, существовала пара популярных программ для входа в удаленные сетевые узлы: rlogin и telnet. Однако эти программы страдали тем же фатальным недостатком, что и программа ftp; все данные (включая имена пользователей и пароли) они передавали в виде открытого текста. Это совершен- но недопустимо в эпоху Интернета.
ssh
Для решения описанной проблемы был разработан протокол с названием SSH (Secure Shell — безопасная командная оболочка). SSH решает две основные про- блемы безопасного взаимодействия с удаленными сетевыми узлами:
подтверждает, что удаленный узел является именно тем, за кого себя выдает (это предотвращает атаки вида «злоумышленник в середине» (man-in-the- middle));
шифрует все данные, передаваемые между локальным и удаленным узлами.
В своей работе протокол SSH опирается на два компонента. На удаленном узле действует сервер SSH, принимающий соединения на порте 22, а в локальной си- стеме действует клиент SSH, осуществляющий обмен информацией с удаленным сервером.
Безопасные взаимодействия с удаленными узлами
241
Большинство дистрибутивов Linux включают реализацию SSH с названием OpenSSH из проекта BSD. Некоторые дистрибутивы (например, Red Hat) по умол- чанию содержат пакеты с обоими компонентами, сервером и клиентом, тогда как другие (например, Ubuntu) включают только клиента. Чтобы система могла при- нимать удаленные соединения, в ней должен быть установлен пакет с реализаци- ей сервера OpenSSH-server, этот сервер должен быть настроен и запущен, и если система находится за брандмауэром, последний должен пропускать входящие со- единения на порт TCP с номером 22.
СОВЕТ
Если у вас нет удаленной системы, с которой можно было бы устанавливать соединения, но вы желаете поработать с примерами, приведенными ниже, уста- новите пакет OpenSSH-server в своей системе и используйте имя localhost в каче- стве имени удаленного узла. В этом случае ваш компьютер будет устанавливать соединения с самим собой.
Программа клиента SSH, используемая для подключения к серверам SSH, имеет достаточно очевидное имя: ssh. Подключиться к удаленному сетевому узлу с име- нем remote-sys можно с помощью программы клиента ssh, как показано ниже:
[me@linuxbox ~]$ ssh remote-sys The authenticity of host ‘remote-sys (192.168.1.4)’ can’t be established. RSA key fingerprint is 41:ed:7a:df:23:19:bf:3c:a5:17:bc:61:b3:7f:d9:bb. Are you sure you want to continue connecting (yes/no)?1
При первой попытке подключения на экран выводится предупреждение, сообща- ющее, что аутентичность удаленного узла не может быть установлена. Это объ- ясняется тем, что программа-клиент прежде никогда не подключалась к данному удаленному узлу. Чтобы принять идентификационные данные удаленного узла, введите yes в ответ на приглашение. После установки соединения пользователю будет предложено ввести пароль:
Warning: Permanently added ‘remote-sys,192.168.1.4’ (RSA) to the list of known hosts. me@remote-sys’s password:2
1 Аутентичность узла ‘remote-sys (192.168.1.4)’ не может быть установлена. Идентифика- ционный ключ RSA: 41:ed:7a:df:23:19:bf:3c:a5:17:bc:61:b3:7f:d9:bb. Вы уверены, что желаете установить соединение (да/нет)? — Примеч. пер.
2 Внимание: узел ‘remote-sys,192.168.1.4’ (RSA) добавлен в хранимый список известных уз-
лов. — Примеч. пер.
242
Глава 16. Сети
После ввода действительного пароля в терминале появится приглашение команд- ной оболочки из удаленной системы:
Last login: Tue Aug 30 13:00:48 2018 [me@remote-sys ~]$
Сеанс с удаленной командной оболочкой продолжается, пока пользователь не введет команду exit в приглашении удаленной командной оболочки, после чего соединение закроется. В этот момент возобновится сеанс локальной командной оболочки и появится ее приглашение к вводу.
К удаленной системе можно также подключиться с другим именем пользователя. Например, если локальный пользователь me имеет в удаленной системе учетную запись с именем bob, он сможет войти в удаленную систему с именем bob, выпол- нив следующую команду:
[me@linuxbox ~]$ ssh bob@remote-sys bob@remote-sys’s password: Last login: Tue Aug 30 13:03:21 2018 [bob@remote-sys ~]$
Как отмечалось выше, ssh проверяет аутентичность удаленного узла. Если уда- ленный узел не пройдет аутентификацию, появится следующее предупреж- дение:
[me@linuxbox ~]$ ssh remote-sys @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that the RSA host key has just been changed. The fingerprint for the RSA key sent by the remote host is 41:ed:7a:df:23:19:bf:3c:a5:17:bc:61:b3:7f:d9:bb. Please contact your system administrator. Add correct host key in /home/me/.ssh/known_hosts to get rid of this message. Offending key in /home/me/.ssh/known_hosts:1 RSA host key for remote-sys has changed and you have requested strict checking. Host key verification failed.1
1 Перевод:
ВНИМАНИЕ: ИЗМЕНИЛСЯ ИДЕНТИФИКАТОР УДАЛЕННОГО УЗЛА! ЕСТЬ ВЕРОЯТНОСТЬ, ЧТО КТО-ТО ЗАМЫСЛИЛ ЧТО-ТО НЕДОБРОЕ! Кто-то может подслушивать вас прямо сейчас (атака “злоумышленник в середине”)! Возможно также, что просто изменился идентификационный ключ RSA узла.
Безопасные взаимодействия с удаленными узлами
243
Это сообщение появляется в двух возможных ситуациях. Первая: злоумышлен- ник мог попытаться провести атаку вида «злоумышленник в середине». Это слу- чается редко, потому что все знают, что ssh предупреждает пользователя об этом. Более вероятная причина связана с некими изменениями в удаленной системе: например, была выполнена переустановка операционной системы или сервера SSH. Однако в интересах безопасности не следует сбрасывать со счетов первую возможность. Всегда обращайтесь к системному администратору удаленной си- стемы, когда появится это сообщение.
Убедившись в безобидности причин, вызвавших это сообщение, можно исправить проблему на стороне клиента. Для этого с помощью текстового редактора (напри- мер, vim) удалите устаревший ключ из файла ~/.ssh/known_hosts. В примере со- общения выше присутствует строчка:
Offending key in /home/me/.ssh/known_hosts:11
Это означает, что подозреваемый ключ хранится в строке 1, в файле known_hosts. Удалите эту строку из файла и позвольте программе ssh принять новые идентифи- кационные данные от удаленной системы.
Помимо открытия сеанса командной оболочки в удаленной системе ssh позволяет также выполнить единственную команду. Например, в удаленной системе remote- sys можно выполнить команду free и получить результаты в локальной системе:
[me@linuxbox ~]$ ssh remote-sys free me@twin4’s password: total used free shared buffers cached Mem: 775536 507184 268352 0 110068 154596 -/+ buffers/cache: 242520 533016 Swap: 1572856 0 1572856 [me@linuxbox ~]$
Этот прием открывает возможность для довольно интересных вариантов исполь- зования, как в следующем примере, где вывод команды ls в удаленной системе перенаправляется в локальный файл:
Удаленный узел прислал идентификационный ключ RSA: 41:ed:7a:df:23:19:bf:3c:a5:17:bc:61:b3:7f:d9:bb. Пожалуйста, свяжитесь со своим системным администратором. Добавьте правильный ключ узла в /home/me/.ssh/known_hosts, чтобы избавиться от этого сообщения. Подозреваемый ключ хранится в файле /home/me/.ssh/known_hosts:1 Идентификационный ключ RSA узла remote-sys изменился, а вы запросили строгую проверку. Проверка ключа удаленного узла завершилась неудачей. — Примеч. пер. 1 Подозреваемый ключ хранится в файле /home/me/.ssh/known_hosts:1. — Примеч. пер.
244
Глава 16. Сети
[me@linuxbox ~]$ ssh remote-sys ’ls *’ > dirlist.txt me@twin4’s password: [me@linuxbox ~]$
Обратите внимание на одиночные кавычки. Они необходимы для предотвраще- ния подстановки пути в локальной системе; нам требуется, чтобы подстановка была выполнена в удаленной системе. Аналогично, если бы нам потребовалось перенаправить вывод в файл в удаленной системе, мы могли бы поместить опера- тор перенаправления и имя файла внутрь одиночных кавычек:
[me@linuxbox ~]$ ssh remote-sys ’ls * > dirlist.txt’
СОЗДАНИЕ ТУННЕЛЯ SSH
При установке SSH-соединения с удаленным узлом между локальной и удаленной системами создается шифрованный туннель. Обычно этот туннель используется для безопасной передачи команд из локальной системы в удаленную и безопас- ной передачи результатов обратно. Помимо этой основной задачи, протокол SSH позволяет также передавать через шифрованный туннель самые разные виды сетевого трафика, создавая своего рода виртуальную частную сеть (Virtual Private Network, VPN) между локальной и удаленной системами.
Чаще всего, пожалуй, эта возможность используется для передачи трафика X Window System. Из системы с действующим X-сервером (то есть отображающей графический интерфейс) можно запустить программу-клиента X (приложение с графическим интерфейсом) в удаленной системе и отображать ее интерфейс в локальной системе. Как это делается, показано в следующем примере. Пред- ставьте, что мы работаем в системе Linux с именем linuxbox, где запущен X-сервер, и нам понадобилось запустить программу xload в удаленной системе с именем remote-sys так, чтобы графический интерфейс программы отображался в локаль- ной системе. Добиться этого можно следующим способом:
[me@linuxbox ~]$ ssh -X remote-sys me@remote-sys’s password: Last login: Mon Sep 05 13:23:11 2018 [me@remote-sys ~]$ xload
После запуска программы xload в удаленной системе ее окно появится в локаль- ной системе. В некоторых системах может понадобиться использовать параметр -Y вместо -X.
Безопасные взаимодействия с удаленными узлами
245
scp и sftp
Пакет OpenSSH включает еще две программы, способные использовать шиф- рованный туннель SSH для копирования файлов по сети. Первая, scp (secure copy — безопасное копирование), используется для копирования файлов, как уже знакомая вам программа cp. Основное отличие заключается в необходи- мости предварять пути к исходному или конечному файлу именем удаленного узла и символом двоеточие за ним. Например, скопировать документ с именем document.txt из домашнего каталога в удаленной системе remote-sys в текущий рабочий каталог в локальной системе можно так:
[me@linuxbox ~]$ scp remote-sys:document.txt . me@remote-sys’s password: document.txt 100% 5581 5.5KB/s 00:00 [me@linuxbox ~]$
По аналогии с командой ssh перед именем удаленного узла можно указать имя пользователя, если имя учетной записи в удаленной системе не совпадает с име- нем учетной записи в локальной системе:
[me@linuxbox ~]$ scp bob@remote-sys:document.txt .
Вторая программа копирования файлов через SSH-соединение — sftp. Как сле- дует из ее имени — это безопасная замена для программы ftp. sftp действует практически так же, как оригинальная программа ftp, которую мы использовали выше, только передает данные не в открытом текстовом виде, а через шифрован- ный туннель SSH. sftp имеет важное преимущество перед обычной программой ftp — она не требует, чтобы на удаленном узле работал сервер FTP. Ей необходим только сервер SSH. Это означает, что любой компьютер, к которому можно под- ключиться с помощью клиента SSH, можно также использовать в качестве FTP- подобного сервера. Ниже приводится пример сеанса работы с программой sftp:
[me@linuxbox ~]$ sftp remote-sys Connecting to remote-sys… me@remote-sys’s password: sftp> ls ubuntu-8.04-desktop-i386.iso sftp> lcd Desktop sftp> get ubuntu-8.04-desktop-i386.iso Fetching /home/me/ubuntu-8.04-desktop-i386.iso to ubuntu-8.04-desktop-i386.iso /home/me/ubuntu-8.04-desktop-i386.iso 100% 699MB 7.4MB/s 01:35 sftp> bye
246
Глава 16. Сети
СОВЕТ
Протокол SFTP поддерживается многими диспетчерами файлов с графическим интерфейсом, входящими в состав дистрибутивов Linux. В GNOME или KDE можно ввести в адресную строку идентификатор URI, начинающийся с sftp://, и работать с файлами, хранящимися в удаленной системе с действующим сер- вером SSH.
СУЩЕСТВУЮТ ЛИ КЛИЕНТЫ SSH ДЛЯ WINDOWS?
Допустим, что вы работаете за компьютером с операционной системой Windows и вам нужно зайти на свой сервер с Linux, чтобы выполнить некую работу. Как быть? Нужно просто установить в Windows программу клиента SSH! Существует довольно много таких программ. Наиболее популярной, пожалуй, является про- грамма PuTTY Симона Тэтхэма (Simon Tatham) и его команды. Программа PuTTY отображает окно терминала и позволяет пользователю Windows открыть сеанс SSH (или telnet) с удаленным узлом. Программа также предоставляет аналоги программ scp и sftp.
Программа PuTTY доступна по адресу http://www.chiark.greenend.org.uk/~sgtatham/ putty/.
Заключение
В этой главе мы рассмотрели несколько сетевых инструментов, присутствующих в большинстве систем Linux. Учитывая, что Linux широко используется в серве- рах и сетевых устройствах, существует множество других возможностей, которые можно получить, установив дополнительные программы. Но даже имеющийся базовый набор инструментов позволяет решать массу полезных задач, связанных с работой в сети.
17 Поиск файлов
Блуждая по системе Linux, мы совершенно ясно увидели, что типичная Linux- система содержит множество файлов. В связи с этим возникает вопрос: как ис- кать нужные файлы? Мы уже знаем, что файловая система в Linux организована в соответствии с определенными соглашениями, которые переходили из одного поколения Unix-подобных систем в другое, но огромное число файлов может по- рождать труднопреодолимую проблему.
В этой главе мы рассмотрим два инструмента для поиска файлов в системе:
locate — выполняет поиск файлов по именам;
find — выполняет поиск файлов в иерархии каталогов.
Мы также познакомимся с командой, часто используемой вместе с командами по- иска файлов для обработки списков с результатами:
xargs — конструирует команды на основе данных, полученных из стандартного ввода, и выполняет их.
Дополнительно в этой главе будет представлена пара команд, которые помогут нам в наших исследованиях:
touch — изменяет времена, ассоциированные с файлом;
stat — выводит статус файла или файловой системы.
locate — простой способ поиска файлов
Программа locate выполняет быстрый поиск в базе данных имен файлов и выво- дит все имена, соответствующие искомой строке. Допустим, к примеру, что нужно
248
Глава 17. Поиск файлов
найти все программы с именами, начинающимися с zip. Поскольку требуется най- ти программы, можно предположить, что имя каталога с программами оканчива- ется на bin/. Соответственно можно попробовать выполнить поиск с помощью locate, как показано ниже:
[me@linuxbox ~]$ locate bin/zip
locate выполнит поиск в базе данных имен файлов и выведет все имена, содержа- щие строку bin/zip:
/usr/bin/zip /usr/bin/zipcloak /usr/bin/zipgrep /usr/bin/zipinfo /usr/bin/zipnote /usr/bin/zipsplit
Если к результатам поиска предъявляются более строгие требования, команду locate можно объединить с другими инструментами, такими как grep, позволяю- щими осуществить более сложный поиск:
[me@linuxbox ~]$ locate zip | grep bin /bin/bunzip2 /bin/bzip2 /bin/bzip2recover /bin/gunzip /bin/gzip /usr/bin/funzip /usr/bin/gpg-zip /usr/bin/preunzip /usr/bin/prezip /usr/bin/prezip-bin /usr/bin/unzip /usr/bin/unzipsfx /usr/bin/zip /usr/bin/zipcloak /usr/bin/zipgrep /usr/bin/zipinfo /usr/bin/zipnote /usr/bin/zipsplit
Программа locate существует уже много лет, и за эти годы было создано несколько ее вариантов, получивших широкое распространение. Два из них, наиболее часто используемые в современных дистрибутивах Linux, — это slocate и mlocate, ко- торые, впрочем, являются символическими ссылками, указывающими на locate.
find — сложный способ поиска файлов
249
Разные версии locate имеют пересекающиеся множества параметров. Некоторые поддерживают поиск с использованием регулярных выражений (о которых рас- сказывается в главе 19) и групповые символы. Загляните на страницу справоч- ного руководства (man) для locate, чтобы определить, какая версия установлена у вас.
ОТКУДА БЕРЕТСЯ БАЗА ДАННЫХ ДЛЯ LOCATE?
В некоторых дистрибутивах при попытке запустить locate сразу после установки она потерпит неудачу, но если попытаться использовать ее на следующий день, все, как ни странно, будет работать как надо. Так в чем же проблема? База дан- ных для locate создается другой программой с именем updatedb. Обычно она периодически запускается как задание cron; то есть она запускается системным планировщиком cron через регулярные интервалы времени. В большинстве систем, в состав которых входит locate, программа updatedb запускается один раз в сут- ки. Поскольку база данных не обновляется непрерывно, скорее всего, locate не находит самые свежие файлы. Чтобы решить эту проблему, запустите программу updatedb вручную от имени суперпользователя.
find — сложный способ поиска файлов
В отличие от программы locate, выполняющей поиск файлов по именам, про- грамма find ищет файлы согласно заданным атрибутам в указанном каталоге (и во вложенных подкаталогах). Далее мы потратим время на исследование команды find, потому что она имеет множество интересных особенностей, с которыми мы неоднократно столкнемся, когда начнем знакомиться с приемами программиро- вания в последующих главах.
В простейшем случае программе find можно передать одно или несколько имен каталогов для поиска. Например, с ее помощью можно получить список содержи- мого домашнего каталога:
[me@linuxbox ~]$ find ~
Для большинства активных пользователей она выдаст длинный список. Так как список выводится в стандартный вывод, его можно передать по конвейеру другим программам. Воспользуемся программой wc, чтобы подсчитать число файлов:
[me@linuxbox ~]$ find ~ | wc -l 47068
250
Глава 17. Поиск файлов
Ух ты, вот это мы поработали! Вся прелесть команды find в том, что ее можно ис- пользовать для поиска файлов, соответствующих определенным критериям. Она делает это посредством (что немного странно) применения проверок, операций и параметров. Сначала рассмотрим проверки.
Проверки
Допустим, мы хотим получить список каталогов. Для этого добавим в команду следующую проверку:
[me@linuxbox ~]$ find ~ -type d | wc -l 1695
Добавив проверку -type d, мы ограничились поиском только каталогов. Но точно так же можно ограничить поиск только обычными файлами:
[me@linuxbox ~]$ find ~ -type f | wc -l 38737
В табл. 17.1 перечислены проверки типов файлов, наиболее часто используемые с командой find.
Таблица 17.1. Проверки типов файлов в find
Тип файлов
Описание
b
c
d
f
l
Специальный файл блочного устройства
Специальный файл символьного устройства
Каталог
Обычный файл
Символическая ссылка
Добавив дополнительные проверки, можно выполнять поиск файлов по размеру и имени. Давайте найдем все обычные файлы с именами, соответствующими ша- блону *.JPG, и имеющие размер больше 1 мегабайта:
[me@linuxbox ~]$ find ~ -type f -name “*.JPG” -size +1M | wc -l 840
В этом примере мы добавили проверку -name с шаблоном имени файла. Обрати- те внимание, что шаблон заключен в кавычки, чтобы предотвратить подстановку
find — сложный способ поиска файлов
251
имен файлов командной оболочкой. Далее мы добавили проверку -size со стро- кой +1M. Начальный символ «плюс» указывает, что требуется искать файлы, раз- мер которых превышает указанное число. Начальный символ «минус» изменил бы значение строки на противоположное: «меньше указанного числа». Число без знака означает: «в точности соответствует значению». Буква M в конце определяет единицы измерения — мегабайты (Megabytes). В табл. 17.2 перечислены символы, которые можно использовать для обозначения единиц измерения.
Таблица 17.2. Единицы измерения, поддерживаемые командой find
Символ
Единица измерения
b
c
w
k
M
G
Блоки размером по 512 байт (используется по умолчанию, если иное не указано явно)
Байты
2-байтные слова
Килобайты (Kilobytes, блоки по 1024 байт)
Мегабайты (Megabytes, блоки по 1 048 576 байт)
Гигабайты (Gigabytes, блоки по 1 073 741 824 байт)
Команда find поддерживает множество разнообразных проверок. В табл. 17.3 при- водится краткое описание наиболее часто используемых из них. Обратите внима- ние, что в случаях, когда требуется числовой аргумент, допустимо использование формы записи с символами + и -, обсуждавшейся выше.
Таблица 17.3. Проверки, поддерживаемые командой find
Проверка
Описание
-cmin n
-cnewer имя
-ctime n
Соответствует файлам или каталогам, содержимое или атрибуты которых последний раз изменялись точно n минут назад. Чтобы выра- зить условие «менее n минут назад», используйте -n; чтобы выразить условие «более n минут назад», используйте +n
Соответствует файлам или каталогам, содержимое или атрибуты которых последний раз изменялись позже, чем у файла с указанным именем
Соответствует файлам или каталогам, содержимое или атрибуты (то есть разрешения) которых последний раз изменялись более чем n*24 часов назад
-empty
Соответствует пустым файлам и каталогам
252
Глава 17. Поиск файлов
Таблица 17.3 (окончание)
Проверка
Описание
-group группа
Соответствует файлам или каталогам, принадлежащим указанной группе. Группа может задаваться именем или числовым идентифика- тором группы
-iname шаблон
Действует так же, как проверка -name, но различает регистр симво- лов
-inum n
-mmin n
-mtime n
Соответствует файлам с номером индексного узла (inode) n. Эту проверку удобно использовать для поиска всех жестких ссылок на определенный индексный узел
Соответствует файлам или каталогам, содержимое которых послед- ний раз изменялось n минут назад
Соответствует файлам или каталогам, содержимое которых послед- ний раз изменялось n*24 часов назад
-name шаблон
Соответствует файлам и каталогам, имена которых совпадают с ука- занным шаблоном
-newer имя
-nouser
-nogroup
-perm режим
Соответствует файлам и каталогам, содержимое которых последний раз изменялось позже, чем у файла с указанным именем. Эта провер- ка может пригодиться в сценариях, выполняющих резервное копи- рование файлов. Каждый раз, в процессе создания резервной копии, можно обновлять файл (например, файл журнала) и затем с помощью find определять, какие файлы изменились с момента последнего обновления
Соответствует файлам и каталогам, не принадлежащим какому-либо допустимому пользователю. Эту проверку можно использовать для поиска файлов, принадлежащих удаленным учетным записям, или для обнаружения следов злоумышленников
Соответствует файлам и каталогам, не принадлежащим какой-либо допустимой группе
Соответствует файлам или каталогам с указанным режимом доступа. Режим может задаваться восьмеричным числом или иметь символиче- скую форму
-samefile имя
Действует так же, как проверка -inum. Соответствует файлам с тем же номером индексного узла (inode), что и файл с указанным именем
-size n
-type c
-user имя
Соответствует файлам с размером n
Соответствует файлам с типом c
Соответствует файлам или каталогам, принадлежащим пользователю с указанным именем. Аргумент имя может быть именем или числовым идентификатором пользователя
find — сложный способ поиска файлов
253
Это не полный список. Дополнительные детали ищите на странице справочного руководства (man) для команды find.
Операторы
Несмотря на большое число проверок, поддерживаемых командой find, мы все еще нуждаемся в способе определения логических отношений между проверками. На- пример, представьте, что в некотором каталоге мы хотим найти все файлы и под- каталоги с небезопасными разрешениями. Для этого можно было бы выполнить поиск всех файлов с разрешениями, отличающимися от 0600, и каталогов с раз- решениями, отличающимися от 0700. К счастью, find поддерживает возможность комбинирования проверок с помощью логических операторов с целью определить более сложные критерии отбора. Выразить вышеупомянутую проверку можно так:
[me@linuxbox ~]$ find ~ ( -type f -not -perm 0600 ) -or ( -type d -not –perm 0700 )
Ф-фу! Как неизящно! Что все это значит? На самом деле операторы перестанут казаться избыточно сложными, как только вы с ними познакомитесь поближе (табл. 17.4).
Таблица 17.4. Логические операторы, поддерживаемые командой find
Оператор
Описание
-and
-or
-not
( )
Соответствует, если выполняются условия в проверках с обеих сторон от оператора. Можно сократить до -a. Обратите внимание, что в отсутствие операторов по умолчанию подразумевается -and
Соответствует, если выполняется условие с одной из сторон от операто- ра. Можно сократить до -o
Соответствует, если условие в проверке, следующей за оператором, не выполняется. Можно сократить до -!
Группируют проверки и операторы для формирования крупных выра- жений. Используются для управления порядком проверок. По умолча- нию проверки выполняются слева направо. Часто используются для изменения порядка проверок по умолчанию, чтобы получить желаемый результат. Даже если скобки не нужны, иногда полезно включать их, чтобы сделать команды более наглядными. Не забывайте, что круглые скобки имеют специальное значение для командной оболочки, поэтому их нужно экранировать, чтобы они были переданы команде find как аргументы. Обычно экранирование выполняют с помощью символа «обратный слеш»
254
Глава 17. Поиск файлов
Имея список операторов под рукой, попробуем разобрать команду find. На самом верхнем уровне мы видим, что проверки объединены в две группы, разделенные оператором -or:
(выражение 1) -or (выражение 2)
В этом есть определенный смысл, потому что мы хотим найти файлы с одним на- бором разрешений и каталоги — с другим. Но если выполняется поиск и файлов, и каталогов, почему используется оператор -or вместо -and? Потому, что find, вы- полняя обход файлов и каталогов, оценивает их по одному, чтобы понять, соот- ветствует ли файл или каталог указанным проверкам. Команде требуется узнать, является ли очередной элемент файлом или каталогом с «плохими» разрешения- ми. Один и тот же элемент не может соответствовать сразу двум условиям. То есть если развернуть сгруппированные выражения, можно увидеть следующее:
(файл с плохими разрешениями) -or (каталог с плохими разрешениями)
Наша следующая задача — проверить «плохие разрешения». Как это сделать? Фактически никак. Но мы можем проверить «неудовлетворительные разреше- ния», зная, что такое «удовлетворительные разрешения». В случае с файлами удовлетворительными являются разрешения 0600, для каталогов — 0700. Выра- жение, проверяющее «неудовлетворительные» разрешения, выглядит так:
-type f -and -not -perms 0600
а для каталогов так:
-type d -and -not -perms 0700
Как отмечалось в табл. 17.4, оператор -and можно просто удалить, так как он под- разумевается по умолчанию. Теперь, объединив все вместе, мы получим оконча- тельную команду:
find ~ (-type f -not -perms 0600) -or (-type d -not -perms 0700)
Однако поскольку круглые скобки имеют специальное значение для командной оболочки, их нужно экранировать, чтобы предотвратить интерпретацию скобок командной оболочкой. Для этого достаточно добавить обратный слеш перед каж- дой из них.
Логические операторы имеют еще одну важную особенность, с которой необходи- мо разобраться. Представьте, что у нас есть два выражения, разделенных логиче- ским оператором:
выражение1 -оператор выражение2
find — сложный способ поиска файлов
255
Выражение1 будет вычислено в любом случае, а вот будет ли вычислено выражение2, зависит от оператора. В табл. 17.5 показано, как это работает.
Таблица 17.5. Действие логических операторов -and/-or команды find
Результат выражения1
Оператор
Выражение2
Истина
Ложь
Истина
Ложь
-and
-and
-or
-or
Всегда вычисляется
Никогда не вычисляется
Никогда не вычисляется
Всегда вычисляется
Почему так происходит? Это сделано для повышения производительности. Возь- мем для примера оператор -and. Мы знаем, что выражение выражение1 -and выраже- ние2 не может быть истинным, если выражение1 вернет ложный результат, поэтому нет смысла вычислять выражение2. Аналогично, если имеется выражение выражение1 -or выражение2 и выражение1 вернет истинный результат, нет смысла вычислять вы- ражение2, так как уже известно, что выражение1 -or выражение2 является истинным.
Это удобно, поскольку такой порядок вычислений помогает повысить скорость выполнения. Но почему это так важно для нас? Потому, что мы можем использо- вать данную особенность для управления выполнением операций, о которых рас- сказывается далее.
Предопределенные операции
Давайте попробуем выполнить определенные действия в процессе поиска! Иметь список с результатами работы команды find уже неплохо, но представьте, что нам нужно выполнить некие операции с элементами списка. К счастью, find позволяет выполнять наши операции, основываясь на результатах поиска. Существует мно- жество предопределенных операций и несколько способов применения операций, определяемых пользователем. Для начала взгляните на неполный список предо- пределенных операций в табл. 17.6.
Таблица 17.6. Предопределенные операции, поддерживаемые командой find
Операция
Описание
-delete
-ls
Удаляет текущий найденный файл
Действует эквивалентно команде ls -dils в отношении найденного файла. Результат выводится в стандартный вывод
256
Глава 17. Поиск файлов
Таблица 17.6 (окончание)
Операция
Описание
-quit
Выводит полный путь к найденному файлу в стандартный вывод. Эта операция выполняется по умолчанию, если не указана никакая другая
Завершает выполнение команды после обнаружения первого совпадения
Поддерживаемых операций намного больше, чем показано здесь. Полный список можно найти на странице справочного руководства (man) для команды find.
В нашем первом примере мы выполнили команду:
find ~
Она выводит список всех файлов и подкаталогов, хранящихся в домашнем ката- логе. Список выводится просто потому, что в отсутствие других операций предпо- лагается операция -print. То есть эту команду можно было бы выразить так:
find ~ -print
Программу find можно использовать для удаления файлов, соответствующих определенным критериям. Например, следующая команда удалит все файлы с расширением .bak (которое часто используется для обозначений резервных ко- пий файлов):
find ~ -type f -name ‘*.bak’ -delete
Эта команда найдет в домашнем каталоге (и во вложенных подкаталогах) пользо- вателя все файлы с расширением .bak и удалит их.
ВНИМАНИЕ
Операцию -delete следует использовать с особыми предосторожностями. Всегда предварительно проверяйте команду, подставив операцию -print вместо -delete, чтобы убедиться, что она не удалит ничего лишнего.
Прежде чем продолжить, давайте посмотрим, как логические операторы воздей- ствуют на операции. Взгляните на следующую команду:
find ~ -type f -name ‘*.bak’ -print
find — сложный способ поиска файлов
257
Как видите, эта команда ищет обычные файлы (-type f) с расширением .bak (-name ‘*.bak’) и выводит относительные пути к ним в стандартный вывод (-print). Однако такой порядок работы команды определяется логическими отношения- ми между всеми проверками и операциями. Как вы помните, между проверками и операциями по умолчанию подразумевается отношение -and. Ту же команду можно выразить, добавив логические операторы:
find ~ -type f -and -name ‘*.bak’ -and -print
Теперь, имея перед глазами это определение, взгляните на табл. 17.7, где показано, как логические операторы влияют на порядок выполнения.
Таблица 17.7. Влияние логических операторов
Проверка/операция
Выполняется, когда
-type f and -name ‘*.BAK’ истинно
-name ‘*.bak’
-type f истинно
-type f
Всегда выполняется, потому что это первая проверка/опера- ция в отношении -and
Так как логические отношения между проверками и операциями определяют необ- ходимость их выполнения, можно сделать вывод, что их порядок следования играет важную роль. Например, если изменить порядок выполнения операций и проверок, поставив операцию -print на первое место, команда будет вести себя иначе:
find ~ -print -and -type f -and -name ‘*.bak’
Эта версия команды выведет каждый файл (операция -print всегда возвращает истинное значение), а затем проверит тип файла и его расширение.
Операции, определяемые пользователем
Помимо предопределенных операций можно также вызывать произвольные ко- манды. Традиционно с этой целью используется операция -exec, что показано ниже:
-exec команда {} ;
где команда — это имя команды, {} — символическое представление текущего пути к файлу и точка с запятой — обязательный разделитель, обозначающий конец
258
Глава 17. Поиск файлов
команды. Следующий пример демонстрирует использование -exec для получения эффекта, аналогичного операции -delete, обсуждавшейся выше:
-exec rm ‘{}’ ‘;’
И снова, поскольку фигурные скобки и точка с запятой имеют специальное зна- чение для командной оболочки, они должны заключаться в кавычки или экрани- роваться.
Кроме того, существует возможность выполнять пользовательские операции ин- терактивно. Если заменить операцию -exec операцией -ok, перед выполнением каждой указанной команды будет выводиться запрос:
find ~ -type f -name ‘foo*’ -ok ls -l ‘{}’ ‘;’ ? y -rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo ? y -rw-r–r– 1 me me 0 2016-09-19 12:53 /home/me/foo.txt
Эта команда ищет файлы с именами, начинающимися со строки foo, и для каж- дого найденного файла выполняет команду ls -l. Операция -ok запрашивает под- тверждение у пользователя, прежде чем выполнить команду ls.
Увеличение эффективности
Каждый раз, когда обнаруживается файл, соответствующий критериям, операция -exec запускает новый экземпляр указанной команды. Но иногда желательно объ- единить все результаты поиска и запустить единственный экземпляр команды. Например, вместо последовательности команд, такой как
ls -l файл1 ls -l файл2
предпочтительнее было бы выполнить команду
ls -l файл1 файл2
Здесь команда выполняется только один раз, а не несколько. Существует два спо- соба добиться этого: традиционный, с использованием внешней команды xargs, и альтернативный, с использованием новой возможности в самой команде find. Обсудим сначала альтернативный способ.
find — сложный способ поиска файлов
259
Если заменить завершающий символ «точка с запятой» знаком «плюс», в команде find активируется функция объединения результатов в список аргументов для вы- зова единственного экземпляра требуемой команды. Вернемся к нашему примеру. Команда:
find ~ -type f -name ‘foo*’ -exec ls -l ‘{}’ ‘;’ -rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo -rw-r–r– 1 me me 0 2016-09-19 12:53 /home/me/foo.txt
будет вызывать ls для каждого найденного файла. Изменив команду, как показа- но ниже:
find ~ -type f -name ‘foo*’ -exec ls -l ‘{}’ + -rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo -rw-r–r– 1 me me 0 2016-09-19 12:53 /home/me/foo.txt
мы получим тот же результат, но система выполнит команду ls только один раз.
xargs
Команда xargs предлагает очень интересную возможность. Она принимает вход- ные данные со стандартного ввода и преобразует их в список аргументов для ука- занной команды. В данном примере ее можно было бы использовать так:
find ~ -type f -name ‘foo*’ -print | xargs ls -l -rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo -rw-r–r– 1 me me 0 2016-09-19 12:53 /home/me/foo.txt
Здесь вывод команды find передается по конвейеру команде xargs, которая, в свою очередь, конструирует список аргументов для команды ls и выполняет ее.
ПРИМЕЧАНИЕ
Несмотря на то что в командную строку можно включить большое число аргу- ментов, оно не бесконечно. Не исключено, что в результате получится команда, слишком длинная для командной оболочки. Когда длина командной строки пре- вышает максимально допустимый размер, xargs выполнит указанную команду с максимально возможным числом аргументов и затем повторит процесс, пока не исчерпает все, что получит со стандартного ввода. Чтобы увидеть максимально воз- можную длину командной строки, выполните xargs с параметром –show-limits.
260
Глава 17. Поиск файлов
ОБРАБОТКА ФАЙЛОВ С НЕОБЫЧНЫМИ ИМЕНАМИ
Unix-подобные системы позволяют встраивать в имена файлов пробелы (и даже символы перевода строки). Это порождает проблемы при выполнении программ, таких как xargs, конструирующих списки аргументов для других про- грамм. Внутренние пробелы интерпретируются как разделители, и получившаяся коман да будет интерпретировать слова, разделенные пробелами, как отдельные аргументы. Для решения этой проблемы find и xarg предлагают использовать в качестве разделителя аргументов пустой символ (null character). В кодировке ASCII пустой символ определен как символ с нулевым кодом (в противополож- ность пробелу, например, который в кодировке ASCII определен как символ с кодом 32). Команда find поддерживает операцию -print0, которая произво- дит вывод имен файлов, разделенных пустым символом, а команда xargs имеет параметр –null, позволяющий организовать прием значений, разделенных пустым символом. Например:
find ~ -iname ‘*.jpg’ -print0 | xargs –null ls -l
Этот прием гарантирует правильную обработку любых имен файлов, даже со- держащих пробелы.
Возвращаемся в песочницу
Пришло время применить find для решения некоторых практических (почти) за- дач. Сначала создадим песочницу с множеством файлов и каталогов:
[me@linuxbox ~]$ mkdir -p playground/dir-{001..100} [me@linuxbox ~]$ touch playground/dir-{001..100}/file-{A..Z}
Какая мощь командной строки! Эти две строки создают каталог playground, со- держащий 100 подкаталогов и 26 пустых файлов в каждом. Попробуйте-ка то же самое сделать в графическом интерфейсе!
Это волшебство мы сотворили с помощью уже знакомой команды (mkdir), меха- низма подстановки в командной оболочке (фигурные скобки) и новой команды touch. Объединив команду mkdir с параметром -p (который вынуждает mkdir соз- дать родительские каталоги в указанном пути) с подстановкой фигурных скобок, мы смогли создать 100 каталогов.
Команда touch обычно используется для обновления времени последнего измене- ния файлов. Но если передать ей имя несуществующего файла, она создаст пустой файл.
find — сложный способ поиска файлов
261
В нашей песочнице мы создали 100 файлов с именем file-A. Давайте найдем их:
[me@linuxbox ~]$ find playground -type f -name ‘file-A’
Обратите внимание, что, в отличие от ls, find возвращает результаты в несортиро- ванном порядке. Порядок определяется организацией устройства хранения. Мы можем убедиться, что действительно имеем 100 файлов с именем file-A:
[me@linuxbox ~]$ find playground -type f -name ‘file-A’ | wc -l 100
А теперь выполним поиск файлов по времени их последнего изменения. Этот под- ход можно использовать для создания резервных копий или организации файлов в хронологическом порядке. Для этого сначала создадим эталонный файл, время последнего изменения которого будет использоваться для сравнения:
[me@linuxbox ~]$ touch playground/timestamp
Эта команда создаст пустой файл timestamp и установит время его последнего из- менения равным текущему времени. Мы можем убедиться в этом, использовав еще одну полезную команду, stat, которую можно рассматривать как своего рода форсированную версию ls. Команда stat выводит всю информацию о файле и его атрибутах, которой обладает система:
[me@linuxbox ~]$ stat playground/timestamp File: `playground/timestamp’ Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 803h/2051d Inode: 14265061 Links: 1 Access: (0644/-rw-r–r–) Uid: ( 1001/ me) Gid: ( 1001/ me) Access: 2018-10-08 15:15:39.000000000 -0400 Modify: 2018-10-08 15:15:39.000000000 -0400 Change: 2018-10-08 15:15:39.000000000 -0400
Если применить команду touch к файлу еще раз и затем исследовать его с по- мощью stat, мы увидим, что время последнего его изменения обновилось:
[me@linuxbox ~]$ touch playground/timestamp [me@linuxbox ~]$ stat playground/timestamp File: `playground/timestamp’ Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 803h/2051d Inode: 14265061 Links: 1 Access: (0644/-rw-r–r–) Uid: ( 1001/ me) Gid: ( 1001/ me) Access: 2018-10-08 15:23:33.000000000 -0400 Modify: 2018-10-08 15:23:33.000000000 -0400 Change: 2018-10-08 15:23:33.000000000 -0400
262
Глава 17. Поиск файлов
Далее воспользуемся командой find, чтобы обновить время последнего изменения некоторых файлов в нашей песочнице:
[me@linuxbox ~]$ find playground -type f -name ‘file-B’ -exec touch ‘{}’ ‘;’
Эта команда обновит время последнего изменения для всех файлов с именем file-B, имеющихся в песочнице. Теперь найдем с помощью find обновленные фай- лы, сравнив все файлы с эталонным файлом timestamp:
[me@linuxbox ~]$ find playground -type f -newer playground/timestamp
В результате мы получим все 100 файлов с именем file-B. Поскольку команда touch применялась ко всем файлам file-B в песочнице после обновления файла timestamp, они оказались «новее», чем timestamp, и потому были идентифицирова- ны проверкой -newer.
В заключение вернемся к проверке плохих разрешений, выполнявшейся выше, и применим ее к каталогу playground:
[me@linuxbox ~]$ find playground ( -type f -not -perm 0600 ) -or ( -type d -not -perm 0700 )
Эта команда выведет все 100 каталогов и 2600 файлов, хранящихся в playground (а также файл timestamp и сам каталог playground, всего 2702 элемента), потому что ни один из них не соответствует нашему определению «удовлетворительные разрешения». Вооружившись новыми знаниями об операторах и операциях, до- бавим в эту команду операции для применения новых разрешений к файлам и ка- талогам в песочнице:
[me@linuxbox ~]$ find playground ( -type f -not -perm 0600 -exec chmod 0600 ‘{}’ ‘;’ ) -or ( -type d -not -perm 0700 -exec chmod 0700 ‘{}’ ‘;’ )
Основываясь на повседневном опыте, следует отметить, что намного проще вве- сти две команды — одну для каталогов и одну для файлов, чем одну большую со- ставную команду, но знание, что можно действовать именно так, вам не помешает. Главное, что вы должны понять, — как можно использовать операторы и операции для решения практических задач.
Параметры
Наконец, мы добрались до параметров. Параметры помогают управлять областью поиска. Они могут включаться в выражения команды find наряду с другими про- верками и операциями. В табл. 17.8 перечислены наиболее часто используемые параметры.
Заключение
263
Таблица 17.8. Параметры команды find
Параметр
-depth
-maxdepth число_уровней
-mindepth число_уровней
-mount
-noleaf
Описание
Требует от find обработать сначала файлы в каталогах и только потом каталоги. Этот параметр автоматически применяется с операцией -delete
Устанавливает максимальное число уровней, на которое команда find может опускаться в дереве каталогов, вы- полняя проверки и операции
Устанавливает минимальное число уровней, на которое команда find должна опуститься в дереве каталогов перед выполнением проверок и операций
Требует от find не выполнять обход каталогов, в которые смонтированы другие файловые системы
Требует от find не оптимизировать поиск, опираясь на предположение, что поиск ведется в Unix-подобной фай- ловой системе. Этот параметр необходимо использовать при обходе файловых систем DOS/Windows CD-ROM
Заключение
Вы видели сами, как легко определять местоположение файлов (locate) и как сложно искать их (find). Каждая из этих команд имеет свою область применения. Найдите время, чтобы исследовать богатые возможности find. При регулярном ис- пользовании эта команда поможет вам лучше понять операции с файловой систе- мой Linux.
18 Архивация и резервное
копирование
Одной из основных задач администратора компьютерных систем является обе- спечение безопасности данных, а одним из способов решения этой задачи — свое- временное создание резервных копий системных файлов. Даже если вы не явля- етесь системным администратором, вам все равно пригодится умение создавать копии и перемещать большие коллекции файлов из одного места в другое и с од- ного устройства на другое.
В этой главе мы рассмотрим несколько программ, часто используемых для управ- ления коллекциями файлов:
Программы сжатия:
gzip — сжимает и распаковывает файлы;
bzip2 — программа поблочного сжатия файлов.
Программы архивирования:
tar — утилита архивирования на ленту;
zip — упаковывает и сжимает файлы.
И программа синхронизации файлов:
rsync — выполняет синхронизацию файлов и каталогов с удаленной системой.
Сжатие файлов
На протяжении всей истории развития вычислительных технологий не прекра- щались попытки размещения большего числа данных в меньшем объеме, будь то
Сжатие файлов
265
память, устройства хранения или полоса пропускания сети. Многие устройства и технологии, прочно вошедшие в обиход, такие как переносные плееры, телеви- дение высокой четкости или широкополосный доступ в Интернет, обязаны своим существованием эффективным технологиям сжатия данных.
Сжатие данных — это процесс устранения избыточных данных. Давайте рассмо- трим воображаемый пример. Допустим, у нас есть файл, хранящий изображение абсолютно черного квадрата размером 100 на 100 пикселов. В терминах хране- ния данных (если предположить, что каждый пиксел представлен 24 битами, или 3 байтами) изображение занимает 30 000 байт:
100 × 100 × 3 = 30 000.
Изображение, состоящее из пикселов одного цвета, содержит массу избыточных данных. Будь мы умнее, мы могли бы закодировать данные в виде простого описа- ния того факта, что изображение представлено блоком из 30 000 пикселов черного цвета. То есть вместо хранения блока данных с 30 000 нулей (черный цвет в фай- лах изображений обычно представлен нулевым значением) мы могли бы сжать данные до числа 30 000 с последующим нулем, описывающим цвет. Такая схема сжатия, она называется кодированием длин серий (run-length encoding), является одной из простейших технологий сжатия. Современные технологии не в пример сложнее и эффективнее, но главная цель осталась прежней — избавиться от из- быточных данных. Алгоритмы сжатия (математические методики, применяемые для осуществления сжатия) делятся на две основные категории:
Сжатие без потерь (lossless) и с потерями (lossy). Сжатие без потерь гаран- тирует сохранность всех данных, содержащихся в оригинале. То есть после восстановления файла из сжатой версии восстановленный файл будет иметь в точности то же содержимое, что и несжатый оригинал.
Сжатие с потерями (lossy), с другой стороны, удаляет некоторые данные во время сжатия, чтобы обеспечить более высокую степень сжатия. Восстанов- ленный файл в этом случае не будет совпадать с оригинальной версией, скорее он будет близкой аппроксимацией оригинала. Примерами сжатия с потерями могут служить формат JPEG (для изображений) и MP3 (для музыкальных произведений).
В дальнейшем обсуждении мы будем рассматривать только сжатие без потерь, по- скольку большинство данных в компьютерах потерь не допускает.
gzip
Программа gzip используется для сжатия одного или нескольких файлов. Во время работы она замещает оригинальный файл его сжатой версией. Соответствующая
266
Глава 18. Архивация и резервное копирование
программа gunzip используется для восстановления сжатых файлов до исходного состояния. Например:
[me@linuxbox ~]$ ls -l /etc > foo.txt [me@linuxbox ~]$ ls -l foo.* -rw-r–r– 1 me me 15738 2018-10-14 07:15 foo.txt [me@linuxbox ~]$ gzip foo.txt [me@linuxbox ~]$ ls -l foo.* -rw-r–r– 1 me me 3230 2018-10-14 07:15 foo.txt.gz [me@linuxbox ~]$ gunzip foo.txt [me@linuxbox ~]$ ls -l foo.* -rw-r–r– 1 me me 15738 2018-10-14 07:15 foo.txt
В этом примере мы создали текстовый файл с именем foo.txt, записав в него список содержимого каталога /etc. Далее мы запустили программу gzip, которая замени- ла оригинальный файл сжатой версией с именем foo.txt.gz. В списке содержимого каталога, который был получен с использованием шаблона foo.*, можно видеть, что исходный файл действительно был замещен сжатой версией, и эта сжатая вер- сия получилась почти в пять раз меньше оригинала. Можно также заметить, что сжатый файл имеет такие же разрешения и время, что и оригинал.
Далее мы запустили программу gunzip, чтобы распаковать файл. После этого, как видите, сжатая версия была замещена оригиналом, и снова с теми же разрешени- ями и временем.
Программа gzip имеет множество параметров, часть которых описана в табл. 18.1.
Таблица 18.1. Параметры команды gzip
Пара- метр
Длинный параметр
Описание
-c
-d
-f
-h
-l
-r
-t
–stdout
–to-stdout
–decompress
–uncompress
–force
–help
–list
–recursive
Выводит результат на стандартный вывод и сохраняет ориги- нальные файлы
Распаковывает файл. С этим параметром gzip действует как gunzip
Принудительное (force) сжатие, даже если сжатая версия оригинального файла уже существует
Выводит информацию о порядке использования
Выводит список статистик для каждого сжатого файла
Если один или несколько аргументов команды являются каталогами, выполняет рекурсивное сжатие файлов, находя- щихся в них
–test
Проверяет целостность сжатого файла
Сжатие файлов
267
Пара- метр
Длинный параметр
-v
–verbose
-число
Описание
Выводит в процессе работы сообщения с информацией о ходе сжатия
Устанавливает степень сжатия. Числом может быть любое целочисленное значение в диапазоне от 1 (высокая ско- рость работы, низкая степень сжатия) до 9 (низкая скорость работы, высокая степень сжатия). Значения 1 и 9 можно так же заменить параметрами –fast и –best соответственно. По умолчанию используется значение 6
Вернемся к нашему примеру:
[me@linuxbox ~]$ gzip foo.txt [me@linuxbox ~]$ gzip -tv foo.txt.gz foo.txt.gz: OK [me@linuxbox ~]$ gzip -d foo.txt.gz
Здесь мы заменили файл foo.txt его сжатой версией с именем foo.txt.gz. Затем про- верили целостность сжатой версии, передав параметры -t и -v. В заключение мы распаковали файл, вернув его исходное состояние.
gzip можно также использовать несколько необычным способом, через стандарт- ные ввод и вывод:
[me@linuxbox ~]$ ls -l /etc | gzip > foo.txt.gz
Эта команда создает сжатую версию списка с содержимым каталога.
Программа gunzip, которая распаковывает файлы, сжатые с помощью gzip, пред- полагает, что имена файлов оканчиваются расширением .gz, поэтому его можно не указывать при условии, что имя файла в команде не соответствует существующе- му несжатому файлу:
[me@linuxbox ~]$ gunzip foo.txt
Если цель только в том, чтобы просмотреть содержимое сжатого текстового фай- ла, сделать это можно так:
[me@linuxbox ~]$ gunzip -c foo.txt | less
ПРИМЕЧАНИЕ
Существует также программа zless. Она заменяет собой конвейер, представ- ленный выше.
268
Глава 18. Архивация и резервное копирование
bzip2
Программа bzip2 Джулиана Сюарда похожа на программу gzip, но использует иной алгоритм, который обеспечивает более высокую степень сжатия ценой сни- жения скорости работы. Во многих отношениях она действует точно так же, как gzip. Файл, сжатый с помощью bzip2, получает расширение .bz2:
[me@linuxbox ~]$ ls -l /etc > foo.txt [me@linuxbox ~]$ ls -l foo.txt -rw-r–r– 1 me me 15738 2018-10-17 13:51 foo.txt [me@linuxbox ~]$ bzip2 foo.txt [me@linuxbox ~]$ ls -l foo.txt.bz2 -rw-r–r– 1 me me 2792 2018-10-17 13:51 foo.txt.bz2 [me@linuxbox ~]$ bunzip2 foo.txt.bz2
Как видите, bzip2 можно использовать так же, как gzip. Все параметры програм- мы gzip (кроме -r), представленные выше, поддерживаются также программой bzip2. Но имейте в виду, что параметр степени сжатия (-число) имеет несколько иной смысл для bzip2. В паре с bzip2 поставляются программы bunzip2 и bzcat для распаковывания файлов.
Существует также программа bzip2recover для восстановления поврежденных файлов формата .bz2.
НЕ ПРЕВРАЩАЙТЕСЬ В ОДЕРЖИМЫХ МАНИЕЙ СЖАТИЯ
Мне иногда приходится видеть, как кто-то пытается сжать файл, уже сжатый с при- менением эффективного алгоритма сжатия, выполняя нечто подобное:
$ gzip picture.jpg
Это напрасная трата времени и дискового пространства! Если применить процедуру сжатия к уже сжатому файлу, зачастую получается файл большего размера. Это объясняется тем, что все методики сжатия добавляют в файл некоторую служебную информацию, описывающую сжатие. Если попытаться сжать файл, не содержащий избыточной информации, сжатие не приведет к экономии места, которая могла бы покрыть расходы на хранение служебной информации.
Архивирование файлов
Часто вместе со сжатием используется операция архивирования. Архивирова- ние — это процесс сбора множества файлов и упаковывание их в один большой файл. Архивирование часто применяется как один из этапов создания резервных
Архивирование файлов
269
копий системы. Оно также используется при перемещении старых данных из си- стемы в некоторое долговременное хранилище.
tar
В мире программного обеспечения для Unix-подобных систем существует про- грамма tar — классический инструмент для архивирования файлов. Ее имя, ко- торое расшифровывается как tape archive (архив на магнитной ленте), указывает, что первоначально инструмент предназначался для создания архивов на магнит- ных лентах. Он до сих пор используется для решения этой традиционной задачи, но с неменьшим успехом поддерживает другие устройства хранения. Нам часто приходится видеть имена файлов с расширением .tar или .tgz, которые обознача- ют «простые» tar-архивы и архивы, сжатые с помощью gzip соответственно. Ар- хив может состоять из группы отдельных файлов, иерархий каталогов или и того и другого. Команда tar имеет следующий синтаксис:
tar режим[параметры] путь…
где под режимом подразумевается один из нескольких режимов работы, перечис- ленных в табл. 18.2 (здесь представлены не все параметры; полный список вы най- дете на странице справочного руководства (man) для tar).
Таблица 18.2. Режимы команды tar
Режим
Описание
c
x
r
t
Создать архив из списка файлов и/или каталогов
Извлечь файлы из архива
Добавить указанный файл и/или каталог в конец архива
Вывести список содержимого архива
В программе tar используется немного непривычный способ определения пара- метров, поэтому рассмотрим несколько примеров ее использования. Для начала воссоздадим нашу песочницу, как мы это делали в предыдущей главе:
[me@linuxbox ~]$ mkdir -p playground/dir-{001..100} [me@linuxbox ~]$ touch playground/dir-{001..100}/file-{A..Z}
Далее создадим архив всей песочницы:
[me@linuxbox ~]$ tar cf playground.tar playground
270
Глава 18. Архивация и резервное копирование
Эта команда создаст tar-архив с именем playground.tar, включающий всю иерар- хию каталогов песочницы. Как видите, режим и параметр f, который использует- ся для определения имени tar-архива, можно объединять, и при этом не требуется использовать начальный дефис. Но имейте в виду, что режим всегда должен ука- зываться первым, перед любыми параметрами. Посмотреть содержимое архива можно с помощью следующей команды:
[me@linuxbox ~]$ tar tf playground.tar
Для получения более подробного списка добавим параметр v (verbose — подроб- ности):
[me@linuxbox ~]$ tar tvf playground.tar
Теперь извлечем содержимое архива в другой каталог. Для этого создадим новый каталог с именем foo, перейдем в него и извлечем содержимое tar-архива:
[me@linuxbox ~]$ mkdir foo [me@linuxbox ~]$ cd foo [me@linuxbox foo]$ tar xf ../playground.tar [me@linuxbox foo]$ ls playground
Если внимательно исследовать содержимое ~/foo/playground, можно заметить, что в результате распаковывания архива мы получили точные копии оригинальных файлов. Однако следует помнить, что если вы не действуете от имени суперпользо- вателя, файлы и каталоги, извлеченные из архива, будут принадлежать пользовате- лю, выполнившему восстановление, а не первоначальному их владельцу.
Другой интересной особенностью tar является способ обработки путей в архивах. По умолчанию используются относительные пути, а не абсолютные. Для этого программа tar просто удаляет начальный слеш во всех путях. Чтобы показать это, создадим снова наш архив, но на этот раз укажем абсолютный путь к архивируе- мому каталогу:
[me@linuxbox foo]$ cd [me@linuxbox ~]$ tar cf playground2.tar ~/playground
Как вы помните, командная оболочка заменит ~/playground полным путем /home/ me/playground после нажатия клавиши ENTER, благодаря этому мы получим пол- ный путь для нашей демонстрации. Далее извлечем архив, так же как прежде, и посмотрим, что из этого получилось:
[me@linuxbox ~]$ cd foo [me@linuxbox foo]$ tar xf ../playground2.tar
Архивирование файлов
271
[me@linuxbox foo]$ ls home playground [me@linuxbox foo]$ ls home me [me@linuxbox foo]$ ls home/me playground
Как видите, здесь при извлечении архива каталог home/me/playground был вос- создан не в корневом, а в текущем рабочем каталоге ~/foo, как было бы в случае с абсолютными путями. Это может показаться странным, но такое решение имеет свои преимущества: оно позволяет извлекать архивы в любое другое место, а не только в исходное. Повторив это упражнение с параметром, управляющим выво- дом дополнительных сообщений (v), можно получить более понятную картину происходящего.
Рассмотрим пусть и гипотетический, но все же имеющий практическую ценность пример использования tar. Представим, что нужно скопировать домашний ка- талог со всем его содержимым в другую систему и у нас имеется жесткий диск, подключаемый к порту USB, который можно использовать для переноса файлов. В современных системах Linux такие диски «как по волшебству» автоматически монтируются в каталог /media. Допустим также, что подключаемый жесткий диск имеет том с именем BigDisk. Чтобы создать требуемый архив, выполним следую- щую команду:
[me@linuxbox ~]$ sudo tar cf /media/BigDisk/home.tar /home
После записи файла следует отмонтировать диск и подключить его ко второму компьютеру. И снова он автоматически монтируется в каталог /media/BigDisk. Чтобы извлечь архив, выполните следующие команды:
[me@linuxbox2 ~]$ cd / [me@linuxbox2 /]$ sudo tar xf /media/BigDisk/home.tar
Обратите внимание, что здесь сначала выполняется переход в каталог /, чтобы извлечение производилось относительно корневого каталога, потому что все пути в архиве — относительные.
При распаковке архива можно ограничить количество извлекаемых данных. На- пример, можно извлечь из архива единственный файл:
tar xf archive.tar путь_к_файлу
Добавление в конец команды пути к файлу гарантирует извлечение только это- го файла. Можно указать несколько путей. Обратите внимание, что путь к файлу должен быть полным относительным путем в архиве. Обычно в путях к файлам
272
Глава 18. Архивация и резервное копирование
нельзя использовать групповые символы; но GNU-версия tar (именно эта вер- сия входит в состав большинства дистрибутивов Linux) поддерживает параметр –wildcards. В следующем примере используется файл playground2.tar, создан- ный выше:
[me@linuxbox ~]$ cd foo [me@linuxbox foo]$ tar xf ../playground2.tar –wildcards ‘home/me/playground/dir-*/ file-A’
Эта команда извлечет только файлы, соответствующие указанному пути с груп- повым символом dir-*.
Программа tar часто используется в сочетании с find. В следующем примере коман да find используется для поиска файлов, подлежащих включению в архив:
[me@linuxbox ~]$ find playground -name ‘file-A’ -exec tar rf playground.tar ‘{}’ ‘+’
Здесь команда find отыскивает в каталоге playground все файлы с именем file-A и затем с помощью операции -exec вызывает tar в режиме добавления в конец (r), чтобы добавить найденные файлы в архив playground.tar.
Использование tar в сочетании с find предоставляет отличный способ инкремент- ного резервного копирования дерева каталогов или всей системы. Применяя find для поиска файлов, более новых, чем эталонный файл, определяющий отметку времени, можно создать архив, содержащий только более новые файлы, чем фай- лы предыдущего архива, при этом предполагается, что время последнего измене- ния эталонного файла будет изменяться сразу после создания архива.
Программа tar способна также использовать стандартный ввод и стандартный вывод. Например:
[me@linuxbox foo]$ cd [me@linuxbox ~]$ find playground -name ‘file-A’ | tar cf - –files-from=- | gzip > playground.tgz
Здесь программа find создает список файлов и передает его по конвейеру програм- ме tar. Когда программе tar передается имя файла - (дефис), под ним подразу- мевается стандартный ввод или стандартный вывод, в зависимости от контекста. (Кстати, соглашение об использовании дефиса (-) для представления стандарт- ного ввода/вывода используется также многими другими программами.) Пара- метр –files-from (который можно заменить эквивалентным параметром -T) за- ставляет tar читать список путей из файла, а не из командной строки. Наконец, архив, произведенный программой tar, передается по конвейеру программе gzip, чтобы в результате получить сжатый архив playground.tgz. Расширение .tgz по
Архивирование файлов
273
общепринятому соглашению используется для tar-архивов, сжатых программой gzip. В некоторых случаях используется расширение .tar.gz.
В примере, приведенном выше, для сжатия архива использовалась внешняя про- грамма gzip, однако современные GNU-версии tar поддерживают возможность gzip- и bzip2-сжатия своими встроенными средствами, для чего служат параме- тры z и j соответственно. Взяв за основу предыдущий пример, его можно упро- стить, как показано ниже:
[me@linuxbox ~]$ find playground -name ‘file-A’ | tar czf playground.tgz -T -
Если, напротив, понадобится создать архив, сжатый в формате bzip2, это можно сделать так:
[me@linuxbox ~]$ find playground -name ‘file-A’ | tar cjf playground.tbz -T -
Произведя простую замену параметра сжатия z на j (и изменив расширение вы- ходного файла на .tbz, указывающее, что для сжатия использовался алгоритм bzip2), мы задействовали bzip2-сжатие.
Другой интересный пример использования поддержки стандартного ввода и выво- да командой tar связан с передачей файлов между системами по сети. Представьте, что имеется две машины, действующие под управлением Unix-подобных систем и имеющие программы tar и ssh. В этом случае можно организовать передачу ка- талога из удаленной системы (с именем remote-sys в этом примере) в локальную:
[me@linuxbox ~]$ mkdir remote-stuff [me@linuxbox ~]$ cd remote-stuff [me@linuxbox remote-stuff]$ ssh remote-sys ’tar cf - Documents’ | tar xf - me@remote-sys’s password: [me@linuxbox remote-stuff]$ ls Documents
Здесь мы скопировали каталог Documents из удаленной системы remote-sys в ка- талог с именем remote-stuff в локальной системе. Как это получилось? Во-первых, мы запустили программу tar в удаленной системе с помощью команды ssh. Как вы наверняка помните, ssh позволяет выполнить программу на удаленном ком- пьютере в сети и «увидеть» результат в локальной системе — стандартный вывод, полученный в удаленной системе, пересылается в локальную систему для обзора. Мы воспользовались этой особенностью и заставили tar создать архив (режим c) и вывести его не в файл, а в стандартный вывод (параметр f с дефисом в качестве аргумента), вследствие чего архив передается через шифрованный туннель, соз- данный программой ssh, локальной системе. В локальной системе мы вызвали tar с целью распаковать архив (режим x), полученный со стандартного ввода (все тот же параметр f с дефисом в качестве аргумента).
274
Глава 18. Архивация и резервное копирование
zip
Программа zip одновременно является и инструментом сжатия, и архиватором. Формат файлов, используемый программой, знаком пользователям Windows — программа читает и создает файлы с расширением .zip. Однако в Linux чаще дру- гих используется программа сжатия gzip, а второе место занимает bzip2. Поль- зователи Linux используют zip в основном для обмена файлами с системами Windows, а не как основной инструмент сжатия и архивирования.
В простейшем случае программа zip имеет следующий синтаксис:
zip параметры сжатый_файл файл…
Например, ниже показано, как создать zip-архив нашей песочницы:
[me@linuxbox ~]$ zip -r playground.zip playground
Без параметра -r (отвечает за рекурсивный обход каталогов) в архив будет вклю- чен только каталог playground (без своего содержимого). Расширение .zip добав- ляется к имени выходного файла автоматически, а мы включили его в пример для наглядности.
В процессе создания zip-архива программа zip обычно выводит последователь- ность сообщений, как показано ниже:
adding: playground/dir-020/file-Z (stored 0%) adding: playground/dir-020/file-Y (stored 0%) adding: playground/dir-020/file-X (stored 0%) adding: playground/dir-087/ (stored 0%) adding: playground/dir-087/file-S (stored 0%)
Эти сообщения показывают состояние каждого файла, добавленного в архив. zip добавляет файлы в архив, используя один из двух методов: либо «store» (простое сохранение) — без сжатия, как в примере, приведенном выше, либо «deflate» — со сжатием. Числовое значение, следующее за названием метода добавления, указы- вает достигнутую степень сжатия. Поскольку в нашей песочнице хранятся только пустые файлы, сжатие их содержимого не производится.
Извлечение содержимого из zip-архива выполняется просто — с помощью про- граммы unzip:
[me@linuxbox ~]$ cd foo [me@linuxbox foo]$ unzip ../playground.zip
Архивирование файлов
275
Одно важное отличие zip (от tar) состоит в том, что если указанный архив суще- ствует, он дополняется, а не замещается. То есть существующий архив сохраняет- ся, новые файлы добавляются в него, а существующие — замещаются.
Программа unzip позволяет выводить информацию о файлах и выборочно извле- кать их, достаточно только передать ей имя интересующего нас файла:
[me@linuxbox ~]$ unzip -l playground.zip playground/dir-087/file-Z Archive: ./playground.zip Length Date Time Name
0 10-05-18 09:25 playground/dir-087/file-Z
0 1 file
[me@linuxbox ~]$ cd foo [me@linuxbox foo]$ unzip ../playground.zip playground/dir-087/file-Z Archive: ../playground.zip replace playground/dir-087/file-Z? [y]es, [n]o, [A]ll, [N]one, [r]ename: y extracting: playground/dir-087/file-Z
При наличии параметра -l программа unzip просто выведет информацию о содер- жимом архива, не извлекая файл. Если имя файла (или файлов) не указано, unzip выведет список всех файлов в архиве. Для получения более подробной информа- ции следует добавить параметр -v. Обратите внимание, что когда при извлечении из архива обнаруживается конфликт с существующим файлом, перед его заменой у пользователя запрашивается разрешение.
Подобно программе tar, zip может использовать стандартный ввод и вывод, хотя реализация этой возможности имеет меньшую практическую ценность. С помо- щью параметра -@ программе zip по конвейеру передается список имен файлов:
[me@linuxbox foo]$ cd [me@linuxbox ~]$ find playground -name “file-A” | zip -@ file-A.zip
Здесь команда find генерирует список файлов, соответствующих проверке -name “file-A”, и передает его по конвейеру команде zip, которая затем создает архив file-A.zip с выбранными файлами.
zip также поддерживает запись результатов своей работы в стандартный вывод, но эта особенность имеет ограниченное применение, потому что очень немногие программы способны работать с форматом zip. К сожалению, программа unzip не принимает входные данные со стандартного ввода. Это препятствует совместному использованию zip и unzip для копирования файлов по сети, как это возможно с программой tar.
276
Глава 18. Архивация и резервное копирование
zip, в свою очередь, способна принимать данные со стандартного ввода, поэтому ее можно использовать для сжатия вывода других программ:
[me@linuxbox ~]$ ls -l /etc/ | zip ls-etc.zip - adding: - (deflated 80%)
В этом примере вывод команды ls передается по конвейеру программе zip. Так же как tar, zip интерпретирует завершающий дефис как требование «использовать стандартный ввод вместо файла».
Программа unzip позволяет направить ее результаты в стандартный вывод, для чего следует передать параметр -p (pipe — конвейер):
[me@linuxbox ~]$ unzip -p ls-etc.zip | less
Мы затронули лишь самые основные возможности программ zip и unzip. Обе они имеют множество параметров, придающих им большую гибкость, хотя некоторые из них допустимы только для определенных платформ. Для обеих программ, zip и unzip, имеются подробные страницы справочного руководства (man) с множе- ством полезных примеров; однако главное назначение этих программ — обмен файлами с системами Windows, а не сжатие и архивирование файлов в Linux, где большей популярностью пользуются tar и gzip.
Синхронизация файлов и каталогов
В задачах резервного копирования систем широко используется стратегия син- хронизации одного или нескольких каталогов с другими каталогами, находящи- мися в локальной системе (обычно на некотором извлекаемом устройстве) или в удаленной. Можно, к примеру, создать локальную копию веб-сайта, находяще- гося в разработке, и синхронизировать ее время от времени с «рабочей» копией на удаленном веб-сервере.
В мире Unix-подобных систем для решения этой задачи широко используется ин- струмент rsync. Эта программа синхронизует локальные и удаленные каталоги, используя протокол rsync remote-update (протокол удаленного обновления rsync), который позволяет rsync быстро обнаруживать различия между двумя каталога- ми и копировать минимальный объем данных, необходимый для синхронизации. Это делает программу rsync быстрой и экономичной по сравнению с другими про- граммами копирования.
Программа rsync имеет следующий синтаксис:
rsync параметры источник приемник
Синхронизация файлов и каталогов
277
где роль источника и приемника могут играть:
локальный файл или каталог;
удаленный файл или каталог в форме [пользователь@]хост:путь;
удаленный сервер rsync, определяемый идентификатором URI rsync://[поль-
зователь@]хост[:порт]/путь.
Обратите внимание, что либо источник, либо приемник должен находиться в ло- кальной системе. Копирование из удаленной системы в удаленную систему не поддерживается.
Давайте попробуем синхронизировать несколько локальных файлов. Сначала очистим наш каталог foo:
[me@linuxbox ~]$ rm -rf foo/*
Далее синхронизируем каталог playground с соответствующей копией в foo:
[me@linuxbox ~]$ rsync -av playground foo
Мы добавили два параметра: -a (для архивирования — обеспечивает рекурсивный обход и сохранение атрибутов файлов) и -v (подробный вывод), чтобы отразить каталог playground в каталог foo. В процессе выполнения команды можно просма- тривать список копируемых файлов и каталогов. В конце программа выведет ито- говое сообщение, как показано ниже, включающее общий объем скопированных данных:
sent 135759 bytes received 57870 bytes 387258.00 bytes/sec total size is 3230 speedup is 0.02
Если теперь запустить команду еще раз, результат будет другой:
[me@linuxbox ~]$ rsync -av playgound foo building file list … done
sent 22635 bytes received 20 bytes 45310.00 bytes/sec total size is 3230 speedup is 0.14
Обратите внимание на отсутствие списка файлов. Это объясняется тем, что про- грамма rsync не обнаружила различий между ~/playground и ~/foo/playground и поэтому ничего не скопировала. Если теперь изменить файл в playground и запу- стить rsync еще раз, она обнаружит изменившийся файл и скопирует только его.
[me@linuxbox ~]$ touch playground/dir-099/file-Z [me@linuxbox ~]$ rsync -av playground foo building file list … done
278
Глава 18. Архивация и резервное копирование
242 Глава 18. Архивация и резервное копирование playground/dir-099/file-Z sent 22685 bytes received 42 bytes 45454.00 bytes/sec total size is 3230 speedup is 0.14
Как видите, rsync обнаружила изменения и скопировала только изменившийся файл.
Есть одна маленькая, но важная деталь, связанная с определением источника в коман де rsync. Пусть у нас есть два каталога:
[me@linuxbox ~]$ ls source destination
Каталог source содержит один файл с именем file1, а каталог destination пуст. Если выполнить копирование source в destination так:
[me@linuxbox ~]$ rsync source destination
rsync скопирует каталог source в destination:
[me@linuxbox ~]$ ls destination source
Но если в имя каталога source добавить завершающий слеш /, rsync скопирует только содержимое каталога source, но не сам каталог:
[me@linuxbox ~]$ rsync source/ destination [me@linuxbox ~]$ ls destination file1
Это удобно, когда требуется скопировать только содержимое каталога, без созда- ния еще одного уровня в дереве каталогов приемника. Этот трюк действует подоб- но source/*, но, в отличие от последнего, обеспечит копирование всего содержимо- го каталога-источника, включая скрытые файлы.
В качестве практического примера представьте воображаемый внешний жесткий диск, использовавшийся выше с командой tar. Если после подключения такого диска к системе он снова будет смонтирован в каталог /media/BigDisk, мы смо- жем выполнить первое резервное копирование системы, cначала создав каталог /backup на внешнем устройстве, а затем вызвав rsync для копирования наиболее важных компонентов системы на внешнее устройство:
[me@linuxbox ~]$ mkdir /media/BigDisk/backup [me@linuxbox ~]$ sudo rsync -av –delete /etc /home /usr/local /media/BigDisk/ backup
Синхронизация файлов и каталогов
279
В этом примере мы скопировали каталоги /etc, /home и /usr/local из нашей си- стемы на воображаемый внешний диск. Мы добавили параметр –delete, чтобы удалить файлы, которые могут присутствовать на устройстве с резервной копией, но отсутствовать на устройстве-источнике (этот параметр не нужен при создании резервной копии в первый раз, но является полезным дополнением в последую- щих операциях копирования). Периодическое повторение процедуры подключе- ния внешнего диска и запуск этой команды rsync является неплохим (хотя и не идеальным) способом сохранения резервной копии небольшой системы. Конечно, здесь также могло бы пригодиться создание псевдонима. Определим псевдоним и добавим его в свой файл .bashrc, чтобы обеспечить возможность быстрого ре- зервного копирования:
alias backup=‘sudo rsync -av –delete /etc /home /usr/local /media/BigDisk/backup’
Теперь, чтобы выполнить всю работу, достаточно просто подключить внешний диск и ввести команду backup.
Использование rsync для копирования по сети
Одно из самых больших достоинств rsync — возможность копирования файлов по сети, об этом нам «говорит» буква r в названии rsync, что означает remote (уда- ленная). Удаленную синхронизацию можно выполнить одним из двух способов. Первый можно использовать с удаленными системами, где установлена rsync и программа удаленной командной оболочки, такая как ssh. Допустим, что в ло- кальной сети имеется другая система с огромным объемом дискового простран- ства, и мы хотели бы использовать эту систему для хранения резервной копии вместо внешнего диска. Если допустить, что в этой системе уже имеется каталог /backup, куда можно было бы сохранить наши файлы, мы могли бы выполнить резервное копирование так:
[me@linuxbox ~]$ sudo rsync -av –delete –rsh=ssh /etc /home /usr/local remotesys:/backup
Мы внесли два изменения в команду, чтобы обеспечить копирование по сети. Во- первых, добавили параметр –rsh=ssh, который требует от rsync использовать в качестве удаленной командной оболочки программу ssh. Благодаря этому для передачи данных из локальной системы в удаленную мы можем использовать шифрованный туннель SSH. Во-вторых, мы добавили имя удаленного узла (в дан- ном примере remote-sys) перед именем удаленного каталога.
Второй способ использования rsync для синхронизации файлов по сети заклю- чается в использовании сервера rysnc. rsync можно настроить на работу в режи- ме демона, принимающего входящие запросы на синхронизацию. Этот прием
280
Глава 18. Архивация и резервное копирование
часто используется для зеркалирования удаленных систем. Например, компания Red Hat Software поддерживает огромный репозиторий программных пакетов, разрабатываемых для ее дистрибутива Fedora. Для специалистов, занимающих- ся тестированием программного обеспечения, очень удобно иметь зеркало этой коллекции в ходе этапа тестирования, предшествующего этапу выпуска дистрибу- тива. Поскольку файлы в репозитории обновляются достаточно часто (порой по нескольку раз в день), неплохо было бы организовать периодическую синхрониза- цию локального зеркала вместо копирования всего объема репозитория. Один из таких репозиториев хранится в университете Georgia Tech; мы могли бы создать его зеркало с помощью локальной программы rsync и сервера rsync в Georgia Tech:
[me@linuxbox ~]$ mkdir fedora-devel [me@linuxbox ~]$ rsync -av –delete rsync://archive.linux.duke.edu/fedora/ linux/development/rawhide/Everything/x86_64/os/ fedora-devel
В этом примере мы использовали идентификатор URI удаленного сервера rsync, включающий протокол (rsync://), имя удаленного узла (archive.linux.duke.edu) и путь к репозиторию.
Заключение
Мы рассмотрели распространенные программы сжатия и архивирования, исполь- зуемые в Linux и в других Unix-подобных операционных системах. Для архивиро- вания файлов в Unix-подобных системах чаще других используется комбинация tar/gzip, тогда как команды zip/unzip в основном используются для организации обмена данными с системами Windows. Наконец, мы познакомились с програм- мой rsync (моя любимица) — очень эффективным инструментом синхронизации файлов и каталогов между системами.
19 Регулярные
выражения
В следующих нескольких главах мы познакомимся с инструментами для рабо- ты с текстом. Как вы уже знаете, текстовые данные играют важную роль в Unix- подобных системах, таких как Linux. Но прежде чем переходить к изучению возможностей этих инструментов, необходимо познакомиться с технологией, которая часто ассоциируется с самыми сложными случаями использования этих инструментов — регулярными выражениями.
Знакомясь со свойствами и особенностями командной строки, мы уже встреча- ли некоторые по-настоящему таинственные свойства и команды, такие как меха- низмы подстановки и экранирования, короткие комбинации клавиш и история команд, не говоря уже о редакторе vi. Регулярные выражения продолжают этот список и являются (пожалуй) самым загадочным из всех инструментов. Это не означает, что время на их изучение будет потрачено впустую. Как раз наоборот. Хорошее понимание регулярных выражений позволит вам творить настоящие чу- деса, хотя истинная их ценность поначалу может быть и не очевидна.
Что такое регулярные выражения?
Регулярные выражения — это всего лишь символическая форма записи, исполь- зуемая для идентификации шаблонов в тексте. Они, до определенной степени, напоминают групповые символы, используемые командной оболочкой для выбо- ра соответствующих файлов и путей, но в более широком масштабе. Регулярные выражения поддерживаются многими инструментами командной строки и боль- шинством языков программирования, чтобы упростить решение задач, связанных с обработкой текста. Однако проблема в том, что не все регулярные выражения одинаковы; разные инструменты и языки программирования используют соб- ственные «диалекты» регулярных выражений. Для целей нашего обсуждения мы
282
Глава 19. Регулярные выражения
ограничимся регулярными выражениями, как они определены в стандарте POSIX (и поддерживаются большинством инструментов командной строки) в противо- положность многим языкам программирования (особенно это относится к Perl), где используются более широкие и богатые формы записи.
grep
При работе с регулярными выражениями мы в основном будем использовать нашу старую добрую приятельницу — программу grep. Название grep в действи- тельности произошло от фразы «global regular expression print» (глобальный по- иск с помощью регулярного выражения и вывод), то есть, как видите, grep имеет некоторое отношение к регулярным выражениям. В сущности, grep просматрива- ет текстовые файлы в поисках совпадений с указанным регулярным выражением и выводит в стандартный вывод все строки с такими совпадениями.
До сих пор мы передавали программе grep фиксированные строки, например:
[me@linuxbox ~]$ ls /usr/bin | grep zip
Эта команда выведет список всех файлов из каталога /usr/bin, имена которых со- держат подстроку zip.
Программа grep имеет следующий синтаксис:
grep [параметры] регулярное_выражение [файл…]
В табл. 19.1 перечислены наиболее часто используемые параметры grep.
Таблица 19.1. Параметры команды grep
Параметр
Длинный параметр
Описание
-i
-v
-c
–ignore-case
–invert-match
–count
Игнорировать регистр символов. Требует не различать символы верхнего и нижнего реги- стров
Инвертировать критерий. Обычно grep вы- водит строки с совпадениями. Этот параметр заставляет grep выводить строки, не содержа- щие совпадений
Вывести число совпадений (или «несовпа- дений») в присутствии параметра -v вместо самих текстовых строк
grep
283
Параметр
Длинный параметр
Описание
-l
-L
-n
-h
–files-with-matches
Вместо строк с совпадениями выводить только имена файлов с найденными строками
–files-without-match
–line-number
–no-filename
Действует подобно параметру -l, но выво- дит только имена файлов, где не найдено ни одного совпадения
В начале каждой строки с совпадением выве- сти ее номер в файле
Подавить вывод имен файлов при поиске по нескольким файлам
Давайте создадим несколько текстовых файлов, чтобы наше исследование grep стало более предметным:
[me@linuxbox ~]$ ls /bin > dirlist-bin.txt [me@linuxbox ~]$ ls /usr/bin > dirlist-usr-bin.txt [me@linuxbox ~]$ ls /sbin > dirlist-sbin.txt [me@linuxbox ~]$ ls /usr/sbin > dirlist-usr-sbin.txt [me@linuxbox ~]$ ls dirlist*.txt dirlist-bin.txt dirlist-sbin.txt dirlist-usr-sbin.txt dirlist-usr-bin.txt
Ниже показано, как выполнить простой поиск в нашем списке файлов:
[me@linuxbox ~]$ grep bzip dirlist*.txt dirlist-bin.txt:bzip2 dirlist-bin.txt:bzip2recover
В этом примере grep просматривает все перечисленные файлы в поисках строки bzip и находит два совпадения, оба в файле dirlist-bin.txt. Если бы нам достаточно было получить только имена файлов с совпадениями, а не сами совпадения, мы могли бы добавить параметр -l:
[me@linuxbox ~]$ grep -l bzip dirlist*.txt dirlist-bin.txt
Напротив, получить список файлов, не содержащих совпадений, можно так:
[me@linuxbox ~]$ grep -L bzip dirlist*.txt dirlist-sbin.txt dirlist-usr-bin.txt dirlist-usr-sbin.txt
284
Глава 19. Регулярные выражения
Метасимволы и литералы
Несмотря на то что пока это не очевидно, во всех своих попытках поиска с помо- щью grep мы использовали регулярные выражения, хотя и очень простые. Регу- лярное выражение bzip, к примеру, означает, что ему соответствуют только строки в файлах, содержащие не менее четырех символов, и среди этих символов присут- ствуют символы b, z, i и p, следующие именно в таком порядке, и между ними от- сутствуют какие-либо другие символы. Символы в строке bzip — это литеральные символы, то есть они соответствуют сами себе. Помимо литералов регулярные вы- ражения могут содержать метасимволы, они используются для определения бо- лее сложных критериев сопоставления. К метасимволам регулярных выражений относятся следующие символы:
^ $ . [ ] { } - ? * + ( ) | \
Все остальные символы считаются литералами. Впрочем, в некоторых случаях символ «обратный слеш» используется для создания метапоследовательностей, а также для экранирования метасимволов, чтобы они могли интерпретироваться как литералы, а не как метасимволы.
ПРИМЕЧАНИЕ
Как видите, многие метасимволы регулярных выражений имеют также специ- альное значение для механизма подстановки командной оболочки. Поэтому, передавая регулярные выражения с метасимволами в виде аргументов команд- ной строки, следует заключать их в кавычки, чтобы предотвратить попытки командной оболочки выполнить подстановку вместо них.
Любой символ
Первый метасимвол, который мы рассмотрим, — это символ «точка», соответству- ющий любому символу. Если включить его в регулярное выражение, он будет со- ответствовать любому символу в данной позиции. Например:
[me@linuxbox ~]$ grep -h ‘.zip’ dirlist*.txt bunzip2 bzip2 bzip2recover gunzip gzip
Якоря
285
funzip gpg-zip preunzip prezip prezip-bin unzip unzipsfx
Здесь выполнен поиск в наших файлах совпадений с регулярным выражением .zip. В полученных результатах имеется пара важных моментов, которые необ- ходимо отметить. Обратите внимание, что программа zip не была найдена. Это объясняется включением в регулярное выражение метасимвола «точка», увели- чившим длину обязательного совпадения до четырех символов; так как в имени программы zip всего три символа, оно не было найдено. Кроме того, если бы в на- ших списках имелись имена файлов с расширением .zip, они также были бы най- дены, потому что символ «точка» в расширении файла интерпретировался бы как «любой символ».
Якоря
Символ крышки (^) и знак доллара ($) в регулярных выражениях интерпрети- руются как якоря. Это означает, что в их присутствии совпадение с регулярным выражением возможно, только если совпадение будет найдено в начале строки (^) или в ее конце ($).
[me@linuxbox ~]$ grep -h ‘^zip’ dirlist*.txt zip zipcloak zipgrep zipinfo zipnote zipsplit [me@linuxbox ~]$ grep -h ‘zip$’ dirlist*.txt gunzip gzip funzip gpg-zip preunzip prezip unzip zip [me@linuxbox ~]$ grep -h ‘^zip$’ dirlist*.txt zip
286
Глава 19. Регулярные выражения
Здесь выполняется поиск в списке файлов строки zip, находящейся в начале стро- ки, в конце строки и занимающей всю строку, от начала до конца. Обратите вни- мание, что регулярное выражение ^$ (начало и конец без каких-либо символов между ними) будет соответствовать пустым строкам.
В ПОМОЩЬ ЛЮБИТЕЛЯМ КРОССВОРДОВ
Даже наши ограниченные познания в области регулярных выражений могут при- нести некоторую пользу.
Моя супруга обожает разгадывать кроссворды и иногда просит меня помочь с ответом на какой-нибудь вопрос. Например: «Слово из пяти букв, третья j, по- следняя r, которое означает…» Подобные вопросы навели меня на размышления.
Знаете ли вы, что в вашей системе Linux имеется словарь? Загляните в каталог /usr/ share/dict, и вы обнаружите там один или несколько словарей. Файлы словарей, находящиеся в каталоге, — это обычные длинные списки слов, по одному в стро- ке, упорядоченные по алфавиту. В моей системе файл words содержит больше 98 500 слов. Найти возможные ответы на вопрос в кроссворде можно с помощью следующей команды:
[me@linuxbox ~]$ grep -i ‘^..j.r$’ /usr/share/dict/words Major major
Это регулярное выражение помогает найти в файле словаря все слова длиной в пять букв, где третья буква — j и последняя — r.
Выражения в квадратных скобках и классы символов
В дополнение к возможности описать в регулярном выражении совпадение с лю- бым символом в заданной позиции, используя выражения в квадратных скобках, можно также описать совпадение с одним символом из определенного множе- ства. Выражение в квадратных скобках помогает определить множество символов (включая символы, которые иначе интерпретировались бы как метасимволы), ко- торые находятся в данной позиции. В следующем примере используется множе- ство из двух символов, благодаря которому обнаруживаются соответствия с по- следовательностями bzip и gzip:
[me@linuxbox ~]$ grep -h ‘[bg]zip’ dirlist*.txt bzip2
Выражения в квадратных скобках и классы символов
287
bzip2recover gzip
Множество может содержать любое число символов. Метасимволы, заключенные в квадратные скобки, теряют свое специальное значение. Лишь два метасимво- ла интерпретируются особым образом, но при этом они имеют иной смысл. Пер- вый — символ крышки (^), который используется для обозначения отрицания; второй — дефис (-), который используется для обозначения диапазона символов.
Отрицание
Если сразу после открывающей квадратной скобки стоит символ крышки (^), остальные символы множества интерпретируются как недопустимые в данной позиции. Проверим это, изменив предыдущий пример:
[me@linuxbox ~]$ grep -h ‘[^bg]zip’ dirlist*.txt bunzip2 gunzip funzip gpg-zip preunzip prezip prezip-bin unzip unzipsfx
Включив отрицание, мы получили список файлов, имена которых содержат по- следовательность zip, которой предшествует любой символ, кроме b или g. Обра- тите внимание, что файл zip не был найден. Символ отрицания не отменяет не- обходимости присутствия символа в заданной позиции, он лишь требует, чтобы символ в этой позиции не принадлежал указанному множеству.
Символ крышки обозначает операцию отрицания, только если является первым символом в выражении в квадратных скобках; в противном случае он теряет свое специальное значение и превращается в обычный символ.
Традиционные диапазоны символов
Если необходимо сконструировать регулярное выражение, которое находило бы в наших списках все файлы с именами, начинающимися с заглавной буквы, это можно выполнить следующим образом:
[me@linuxbox ~]$ grep -h ‘^[ABCDEFGHIJKLMNOPQRSTUVWXZY]’ dirlist*.txt
288
Глава 19. Регулярные выражения
Достаточно просто поместить 26 букв в верхнем регистре в выражение в квадрат- ных скобках. Но необходимость ввода всех этих символов вызывает некоторое беспокойство, поэтому предусмотрен другой способ:
[me@linuxbox ~]$ grep -h ‘^[A-Z]’ dirlist*.txt MAKEDEV ControlPanel GET HEAD POST X X11 Xorg MAKEFLOPPIES NetworkManager NetworkManagerDispatcher
Мы сократили множество с 26 буквами до 3-символьного диапазона. Так можно выразить любой диапазон символов и даже несколько диапазонов, например, для поиска имен файлов, начинающихся с буквы или цифры:
[me@linuxbox ~]$ grep -h ‘^[A-Za-z0-9]’ dirlist*.txt
Как следует из примеров, символ «дефис» получает в диапазонах специальное значение, поэтому возникает вопрос: как включить дефис в выражение в квадрат- ных скобках, чтобы он интерпретировался как обычный символ? Для этого доста- точно поставить его в начало выражения. Например:
[me@linuxbox ~]$ grep -h ‘[A-Z]’ dirlist*.txt
Эта команда найдет все имена файлов, содержащие буквы верхнего регистра. С другой стороны, следующее выражение:
[me@linuxbox ~]$ grep -h ‘[-AZ]’ dirlist*.txt
найдет все имена файлов, содержащие дефис, букву A или букву Z.
Классы символов POSIX
Традиционные диапазоны символов — простой и эффективный способ опреде- ления наборов символов. К сожалению, они могут использоваться не со всеми программами. Мы не испытывали никаких проблем с диапазонами, используя программу grep, но могли бы столкнуться с ними при использовании других про- грамм.
Классы символов POSIX
289
Вернемся к главе 4, где демонстрировалось использование групповых символов для подстановки имен файлов. Там говорилось, что можно использовать диапазо- ны символов почти так же, как они используются в регулярных выражениях, но есть одна проблема:
[me@linuxbox ~]$ ls /usr/sbin/[ABCDEFGHIJKLMNOPQRSTUVWXYZ]* /usr/sbin/MAKEFLOPPIES /usr/sbin/NetworkManagerDispatcher /usr/sbin/NetworkManager
(В разных дистрибутивах будут получены разные списки файлов, а в некоторых даже пустой список. Эти результаты получены в Ubuntu.) Эта команда вернула ожидаемый результат — список имен файлов, начинающихся с заглавной буквы. Но следующая команда даст совершенно другой результат (здесь приведена толь- ко часть результатов):
[me@linuxbox ~]$ ls /usr/sbin/[A-Z]* /usr/sbin/biosdecode /usr/sbin/chat /usr/sbin/chgpasswd /usr/sbin/chpasswd /usr/sbin/chroot /usr/sbin/cleanup-info /usr/sbin/complain /usr/sbin/console-kit-daemon
В чем же причина? Для этой длинной истории имеется короткая версия.
Во времена, когда операционная система Unix только появилась на свет, был известен только один набор символов — ASCII, и этот факт нашел свое отра- жение в данной особенности. В ASCII первые 32 символа (с номерами 0–31) — это управляющие символы (такие, как табуляция, забой и возврат каретки). Следующие 32 (32–63) представляют печатаемые символы, включая большин- ство знаков пунктуации и цифры с нуля до девяти. Следующие 32 (с номера- ми 64–95) представляют буквы верхнего регистра и несколько знаков пунктуа- ции. Последние 31 (с номерами 96–127) представляют буквы нижнего регистра и еще несколько знаков пунктуации. Опираясь на эту классификацию, систе- мы, использующие набор ASCII, придерживались следующего порядка сопо- ставления:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
Этот порядок отличается от лексикографического, который выглядит так:
aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
290
Глава 19. Регулярные выражения
С ростом популярности Unix за пределами США возникла необходимость в под- держке символов, не входящих в алфавит американского английского. Таблица ASCII была расширена до использования 8-битных символов, и в нее добави- лись символы с номерами 128–255, используемые во многих других языках. Для поддержки этой возможности в стандарт POSIX было введено понятие региона (locale), определяющее выбор набора символов для конкретного географиче- ского региона. Узнать, какой язык настроен в вашей системе, можно с помощью команды:
[me@linuxbox ~]$ echo $LANG en_US.UTF-8
При проверке этой настройки POSIX-совместимые приложения используют лек- сикографический порядок, а не порядок следования символов в наборе ASCII. Это объясняет поведение команд, рассмотренное выше. Когда диапазон символов [A-Z] интерпретируется в лексикографическом порядке, он включает все алфа- витные символы, кроме символа a в нижнем регистре, — именно это объясняет полученный результат.
Для частичного решения этой проблемы стандарт POSIX предусматривает не- сколько классов символов, описывающих диапазоны символов. Они перечислены в табл. 19.2.
Таблица 19.2. Классы символов POSIX
Класс символов
[:alnum:]
[:word:]
Описание
Алфавитно-цифровые символы; эквивалент диапазона [A-Za-z0-9] в ASCII
То же, что и [:alnum:], с дополнительным символом подчеркива- ния (_)
[:alpha:]
Алфавитные символы; эквивалент диапазона [A-Za-z] в ASCII
[:blank:]
Включает символы пробела и табуляции
[:cntrl:]
Управляющие символы ASCII; включает символы ASCII с кодами от 0 до 31 и 127
[:digit:]
Цифры от 0 до 9
[:graph:]
Отображаемые символы; включает символы ASCII с кодами от 33 до 126
[:lower:]
Символы нижнего регистра
Простые и расширенные регулярные выражения POSIX
291
Класс символов
[:punct:]
Описание
Знаки пунктуации; эквивалент класса [-!"#$%&’()*+,./:;?@ [\]_`{|}~] в ASCII
[:print:]
Печатаемые символы; Все символы из класса [:graph:] и пробел
[:space:]
Пробельные символы, включая пробел, табуляцию, возврат каретки, перевод строки, вертикальную табуляцию и перевод формата; эквива- лент класса [ \t\r\n\v\f] в ASCII
[:upper:]
Символы верхнего регистра
[:xdigit:]
Символы, используемые для представления шестнадцатеричных цифр; эквивалент класса [0-9A-Fa-f] в ASCII
Но даже наличие классов символов не дает удобного способа выражения частич- ных диапазонов, таких как [A-M].
Используя классы символов, можно повторить пример с выводом содержимого каталога и посмотреть, насколько улучшился результат.
[me@linuxbox ~]$ ls /usr/sbin/[[:upper:]]* /usr/sbin/MAKEFLOPPIES /usr/sbin/NetworkManagerDispatcher /usr/sbin/NetworkManager
Но не забывайте, что это не является примером использования регулярных выра- жений, скорее это пример того, как командная оболочка выполняет подстановку путей. Мы рассмотрели его лишь потому, что классы символов POSIX поддержи- ваются и там и там.
Простые и расширенные регулярные выражения POSIX
Как раз когда, казалось бы, проблема путаницы с диалектами регулярных вы- ражений решена, обнаруживается, что стандарт POSIX также делит реализа- ции регулярных выражений на два вида: простые регулярные выражения (Basic Regular Expressions, BRE) и расширенные регулярные выражения (Extended Regular Expressions, ERE). Особенности, рассматривавшиеся до сих пор, поддерживаются всеми POSIX-совместимыми приложениями и приложениями, реализующими BRE. Программа grep — одна из них.
292
Глава 19. Регулярные выражения
ВОЗВРАТ К ТРАДИЦИОННОМУ ПОРЯДКУ СОРТИРОВКИ
Есть возможность вернуть систему к традиционному (ASCII) порядку сортировки, изменив значение переменной окружения LANG. Как было показано в предыдущем разделе, переменная LANG хранит название языка и набора символов, заданных в региональных настройках. Значение этой переменной первоначально определя- ется в момент, когда выбирается язык установки дистрибутива Linux.
Увидеть региональные настройки можно, выполнив команду locale:
[me@linuxbox ~]$ locale LANG=en_US.UTF-8 LC_CTYPE=“en_US.UTF-8” LC_NUMERIC=“en_US.UTF-8” LC_TIME=“en_US.UTF-8” LC_COLLATE=“en_US.UTF-8” LC_MONETARY=“en_US.UTF-8” LC_MESSAGES=“en_US.UTF-8” LC_PAPER=“en_US.UTF-8” LC_NAME=“en_US.UTF-8” LC_ADDRESS=“en_US.UTF-8” LC_TELEPHONE=“en_US.UTF-8” LC_MEASUREMENT=“en_US.UTF-8” LC_IDENTIFICATION=“en_US.UTF-8” LC_ALL=
Чтобы установить региональные настройки, обеспечивающие традиционное по- ведение системы Unix, присвойте переменной LANG значение POSIX:
[me@linuxbox ~]$ export LANG=POSIX
Имейте в виду, что в результате наших действий система будет использовать набор символов американского английского (точнее, ASCII), поэтому подумайте, действительно ли это то, что вам нужно.
Эти изменения можно сделать постоянными, добавив следующую строку в файл .bashrc:
export LANG=POSIX
Чем различаются BRE и ERE? Различия касаются наборов метасимволов. В диа- лекте BRE распознаются следующие метасимволы:
^ $ . [ ] *
Все остальные считаются литералами. В ERE во множество метасимволов (с соот- ветствующими им функциями) добавляются:
( ) { } ? + |
Чередование
293
POSIX
На протяжении 1980-х система Unix обрела популярность как коммерческая опе- рационная система, но до 1988-го в мире Unix царила полная анархия. Многие производители компьютеров лицензировали исходный код Unix у ее создателя — компании AT&T и поставляли разные версии операционной системы вместе со своими машинами. Однако в стремлении к дифференциации продуктов каждый про- изводитель добавлял свои, патентованные изменения и расширения. В результате значительно ухудшилась совместимость программного обеспечения. Как обычно, производители пытались играть в игру, победой в которой было «замыкание» клиентов на конкретном производителе. Этот период истории Unix ныне известен как Балканизация (Balkanization).
В середине 1980-х институт инженеров электроники и электротехники (Institute of Electrical and Electronics Engineers, IEEE) начал разработку единого пакета стандартов, которые должны были определить особенности работы системы Unix (и Unix-подобных). Эти стандарты, формально известные как IEEE 1003, определя- ют прикладные программные интерфейсы (Application Programming Interface, API), командную оболочку и утилиты, которые должны присутствовать в стандартной Unix-подобной системе. Название POSIX, сокращенное от «Portable Operating System Interface» (интерфейс переносимой операционной системы, где буква X добавлена для лучшего звучания), было предложено Ричардом Столлманом (да, тем самым Ричардом Столлманом) и принято IEEE.
Однако (что самое интересное) символы ( ) { } интерпретируются в BRE как ме- тасимволы, если они экранированы символом «обратный слеш», тогда как в ERE присутствие обратного слеша перед этими же метасимволами превращает их в ли- тералы.
Поскольку далее в этой главе мы рассмотрим особенности, являющиеся частью ERE, необходимо использовать другую версию grep. Традиционно диалект ERE поддерживался программой egrep, но GNU-версия grep также поддерживает рас- ширенные регулярные выражения при вызове ее с параметром -E.
Чередование
Первой особенностью расширенных регулярных выражений, которую мы об- судим, будет чередование (alternation, или выражение выбора) — оно позволяет выбирать совпадение с одним из нескольких выражений. Так же как выражения в квадратных скобках позволяют одному символу соответствовать множеству
294
Глава 19. Регулярные выражения
указанных символов, чередование позволяет находить совпадение с множеством строк или других регулярных выражений.
Для демонстрации воспользуемся комбинацией команд grep и echo. Сначала по- пробуем выполнить простое сопоставление строк:
[me@linuxbox ~]$ echo “AAA” | grep AAA AAA [me@linuxbox ~]$ echo “BBB” | grep AAA [me@linuxbox ~]$
Достаточно простой пример, в котором мы передаем по конвейеру вывод команды echo на ввод grep и видим результат. Если обнаруживается совпадение, мы видим вывод; если совпадение отсутствует, ничего не выводится.
Теперь добавим чередование, обозначаемое метасимволом вертикальной черты:
[me@linuxbox ~]$ echo “AAA” | grep -E ‘AAA|BBB’ AAA [me@linuxbox ~]$ echo “BBB” | grep -E ‘AAA|BBB’ BBB [me@linuxbox ~]$ echo “CCC” | grep -E ‘AAA|BBB’ [me@linuxbox ~]$
Здесь мы видим регулярное выражение ‘AAA|BBB’, которое означает «совпадение со строкой AAA или со строкой BBB». Так как это расширенная особенность, мы до- бавили в команду grep параметр -E (вместо этого можно было бы использовать программу egrep) и заключили регулярное выражение в кавычки, чтобы предот- вратить интерпретацию командной оболочкой символа вертикальной черты как оператора конвейера. В чередовании может быть более двух вариантов:
[me@linuxbox ~]$ echo “AAA” | grep -E ‘AAA|BBB|CCC’ AAA
Для объединения с другими элементами регулярного выражения чередование можно заключать в круглые скобки ():
[me@linuxbox ~]$ grep -Eh ‘^(bz|gz|zip)’ dirlist*.txt
Этому выражению будут соответствовать имена файлов из наших списков, начи- нающиеся с bz, gz или zip. Если отбросить круглые скобки, смысл регулярного вы- ражения изменится, и ему будут соответствовать имена, начинающиеся с bz или содержащие gz или zip:
[me@linuxbox ~]$ grep -Eh ‘^bz|gz|zip’ dirlist*.txt
Квантификаторы
295
Квантификаторы
Расширенные регулярные выражения поддерживают несколько способов опреде- ления числа совпадений с элементом.
? — совпадение с элементом ноль или один раз
Этот квантификатор фактически означает: «совпадение с предыдущим элементом не обязательно». Представьте, что нужно проверить допустимость номера телефо- на, и предполагается, что номер допустим, если представлен в одной из двух форм:
(nnn) nnn-nnnn;
nnn nnn-nnnn.
Для проверки можно было бы использовать следующее регулярное выражение:
^(?[0-9][0-9][0-9])? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$
В этом выражении за круглыми скобками следуют знаки вопроса, указывающие, что скобки могут либо отсутствовать, либо присутствовать один раз. И снова, по- скольку круглые скобки считаются метасимволами (в ERE), мы экранировали их обратными слешами, чтобы они интерпретировались как литералы.
Попробуем применить это выражение:
[me@linuxbox ~]$ echo “(555) 123-4567” | grep -E ‘^(?[0-9][0-9][0-9])? [0-9] [0-9][0-9]$’ (555) 123-4567 [me@linuxbox ~]$ echo “555 123-4567” | grep -E ‘^(?[0-9][0-9][0-9])? [0-9][0-9] [0-9]-[0-9][0-9][0-9][0-9]$’ 555 123-4567 [me@linuxbox ~]$ echo “AAA 123-4567” | grep -E ‘^(?[0-9][0-9][0-9])? [0-9][0-9] [0-9]-[0-9][0-9][0-9][0-9]$’ [me@linuxbox ~]$
Здесь регулярному выражению соответствуют обе формы записи номера телефо- на, но ему не соответствует номер, содержащий нецифровые символы.
- — совпадение с элементом ноль или более раз
Подобно метасимволу ?, звездочка (*) обозначает необязательный элемент; одна- ко, в отличие от знака вопроса (?), этот элемент может встречаться любое число раз, а не только единожды. Представьте, что нам нужно проверить, является ли строка предложением. Чтобы удовлетворять нашим требованиям, строка должна
296
Глава 19. Регулярные выражения
начинаться с большой буквы, содержать любое число букв верхнего и нижнего ре- гистра и пробелов и заканчиваться точкой. Для поиска совпадений с этим (очень приблизительным) определением предложения воспользуемся следующим регу- лярным выражением:
[[:upper:]][[:upper:][:lower:] ]*.
Выражение состоит из трех элементов: выражение в квадратных скобках с клас- сом символов [:upper:], выражение в квадратных скобках с двумя классами символов, [:upper:] и [:lower:], и пробелом, и точка, экранированная обратным слешем. Второй элемент сопровождается метасимволом *, поэтому в нашем пред- ложении ему может соответствовать любое число букв верхнего и нижнего реги- стра и пробелов, следующих за первой буквой верхнего регистра:
[me@linuxbox ~]$ echo “This works.” | grep -E ‘[[:upper:]][[:upper:][:lower:] ].’ This works. [me@linuxbox ~]$ echo “This Works.” | grep -E ‘[[:upper:]][[:upper:][:lower:] ].’ This Works. [me@linuxbox ~]$ echo “this does not” | grep -E ‘[[:upper:]][[:upper:][:lower:] ]*.’ [me@linuxbox ~]$
Первые два примера соответствуют выражению, а третий — нет, потому что в нем отсутствует обязательный первый символ верхнего регистра и заверша- ющая точка.
- — совпадение с элементом один или более раз
Метасимвол + действует почти так же, как *, но требует совпадения с предыдущим элементом не менее одного раза. Следующему регулярному выражению будут со- ответствовать только строки, состоящие из групп, насчитывающих один или не- сколько алфавитных символов и разделенных одиночными пробелами:
^([[:alpha:]]+ ?)+$
Опробуем его:
[me@linuxbox ~]$ echo “This that” | grep -E ‘^([[:alpha:]]+ ?)+$’ This that [me@linuxbox ~]$ echo “a b c” | grep -E ‘^([[:alpha:]]+ ?)+$’ a b c [me@linuxbox ~]$ echo “a b 9” | grep -E ‘^([[:alpha:]]+ ?)+$’ [me@linuxbox ~]$ echo “abc d” | grep -E ‘^([[:alpha:]]+ ?)+$’ [me@linuxbox ~]$
Квантификаторы
297
Как видите, этому выражению не соответствует строка “a b 9”, потому что она содержит неалфавитный символ; точно так же ему не соответствует строка “abc d”, потому что между символами c и d в ней присутствует больше одного пробела.
{ } — совпадение с элементом определенное число раз
Метасимволы { и } используются, чтобы выразить минимальное и максимальное число обязательных совпадений. Эти числа можно представить четырьмя воз- можными способами, как показано в табл. 19.3.
Таблица 19.3. Определение числа совпадений
Спецификатор
Значение
{n}
{n,m}
{n,}
{,m}
Предыдущий элемент соответствует, если встречается точно n раз
Предыдущий элемент соответствует, если встречается не менее n и не более m раз
Предыдущий элемент соответствует, если встречается n или более раз
Предыдущий элемент соответствует, если встречается не более m раз
Вернемся к примеру с телефонными номерами и воспользуемся этим методом определения повторений, чтобы упростить исходное регулярное выражение
^(?[0-9][0-9][0-9])? [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$
до
^(?[0-9]{3})? [0-9]{3}-[0-9]{4}$
Опробуем его:
[me@linuxbox ~]$ echo “(555) 123-4567” | grep -E ‘^(?[0-9]{3})? [0-9]{3}- [0-9]{4}$’ (555) 123-4567 [me@linuxbox ~]$ echo “555 123-4567” | grep -E ‘^(?[0-9]{3})? [0-9]{3}-[0-9]{4}$’ 555 123-4567 [me@linuxbox ~]$ echo “5555 123-4567” | grep -E ‘^(?[0-9]{3})? [0-9]{3}- [0-9]{4}$’ [me@linuxbox ~]$
298
Глава 19. Регулярные выражения
Как видите, измененная версия регулярного выражения успешно справляется с проверкой номеров, с круглыми скобками и без них, и отвергает неправильно оформленные номера.
Практические примеры применения регулярных выражений
Рассмотрим несколько уже знакомых команд и посмотрим, как они могут исполь- зовать регулярные выражения.
Проверка списка телефонов с помощью grep
В предыдущем примере мы брали телефонные номера по одному и проверяли правильность их оформления. На практике же часто приходится проверять списки телефонов, поэтому давайте создадим такой список. Для этого восполь- зуемся волшебной магией командной строки. Магией, потому что мы еще не знакомы с большинством команд, привлеченных для решения поставленной задачи, но не волнуйтесь — мы рассмотрим их в последующих главах. Вот это волшебство:
[me@linuxbox ~]$ for i in {1..10}; do echo “(${RANDOM:0:3}) ${RANDOM:0:3}- ${RANDOM:0:4}” » phonelist.txt; done
Эта команда создаст файл с именем phonelist.txt, содержащий 10 телефонных но- меров. Если повторить команду, она добавит в список еще 10 номеров. Также мож- но изменить число 10 ближе к началу команды, чтобы создать больше или меньше номеров. Однако если заглянуть в файл, можно заметить проблему:
[me@linuxbox ~]$ cat phonelist.txt (232) 298-2265 (624) 381-1078 (540) 126-1980 (874) 163-2885 (286) 254-2860 (292) 108-518 (129) 44-1379 (458) 273-1642 (686) 299-8268 (198) 307-2440
Некоторые номера оформлены неправильно, что очень хорошо для целей демон- страции их проверки с помощью grep.
Практические примеры применения регулярных выражений
299
Было бы полезно просканировать файл в поисках недопустимых номеров и вы- вести их.
[me@linuxbox ~]$ grep -Ev ‘^([0-9]{3}) [0-9]{3}-[0-9]{4}$’ phonelist.txt (292) 108-518 (129) 44-1379 [me@linuxbox ~]$
Здесь мы использовали параметр -v, чтобы обратить сопоставление и вывести только строки, не соответствующие указанному выражению. Само выражение со- держит якорные метасимволы на обоих концах и тем самым гарантирует отсут- ствие дополнительных символов слева и справа от номера. Кроме того, в отличие от примера, приведенного выше, это выражение также требует обязательного на- личия круглых скобок в номере.
Поиск необычных имен файлов с помощью find
Команда find поддерживает проверку, основанную на регулярном выражении. Су- ществует одно важное обстоятельство, которое следует помнить, используя регу- лярные выражения в командах find и grep. Если grep выводит строку, содержащую совпадение с регулярным выражением, то find требует точного совпадения пути с регулярным выражением. В следующем примере команда find использует регу- лярное выражение для поиска путей к файлам, содержащим любые символы, не входящие в следующее множество:
[-_./0-9a-zA-Z]
В результате такого поиска можно выявить имена файлов и каталогов, содержа- щие пробелы и другие, потенциально вредные символы:
[me@linuxbox ~]$ find . -regex ‘.[^-_./0-9a-zA-Z].’
Из-за требования точного совпадения всего пути мы добавили элемент .* с обоих концов выражения, замещающий любое количество любых символов (в том чис- ле и отсутствие символов). В середине выражения находится инвертированное выражение в квадратных скобках, содержащее множество символов, допустимых в именах файлов и каталогов.
Поиск файлов с помощью locate
Программа locate поддерживает простые (параметр –regexp) и расширенные (параметр –regex) регулярные выражения. Благодаря этому можно выполнять те же операции, что производились выше с файлами dirlist:
300
Глава 19. Регулярные выражения
[me@linuxbox ~]$ locate –regex ‚bin/(bz|gz|zip)’ /bin/bzcat /bin/bzcmp /bin/bzdiff /bin/bzegrep /bin/bzexe /bin/bzfgrep /bin/bzgrep /bin/bzip2 /bin/bzip2recover /bin/bzless /bin/bzmore /bin/gzexe /bin/gzip /usr/bin/zip /usr/bin/zipcloak /usr/bin/zipgrep /usr/bin/zipinfo /usr/bin/zipnote /usr/bin/zipsplit
Использовав чередование, мы нашли пути, содержащие bin/bz, bin/gz или /bin/zip.
Поиск текста в less и vim
less и vim поддерживают одинаковые способы поиска в тексте. Чтобы выполнить поиск, нажмите клавишу / и введите регулярное выражение. Воспользуемся про- граммой less, чтобы просмотреть содержимое файла phonelist.txt:
[me@linuxbox ~]$ less phonelist.txt
Затем выполним поиск с применением выражения для проверки:
(232) 298-2265 (624) 381-1078 (540) 126-1980 (874) 163-2885 (286) 254-2860 (292) 108-518 (129) 44-1379 (458) 273-1642 (686) 299-8268 (198) 307-2440 ~ ~ ~ /^([0-9]{3}) [0-9]{3}-[0-9]{4}$
less выделит строки с совпадениями, что позволит сразу увидеть недопустимые номера:
Заключение
301
(232) 298-2265 (624) 381-1078 (540) 126-1980 (874) 163-2885 (286) 254-2860 (292) 108-518 (129) 44-1379 (458) 273-1642 (686) 299-8268 (198) 307-2440 ~ ~ ~ (END)
Редактор vim поддерживает только простые регулярные выражения, поэтому вы- ражение для поиска должно выглядеть следующим образом:
/([0-9]{3}) [0-9]{3}-[0-9]{4}
Как видите, выражение практически то же самое; различия обусловлены лишь тем, что многие символы, которые в расширенной версии выражений считаются метасимволами, в простой версии интерпретируются как литералы. Они действу- ют как метасимволы, только если экранировать их символом «обратный слеш». В зависимости от конкретных настроек vim совпадения могут быть выделены или нет. Если совпадения не выделяются, попробуйте в командном режиме выполнить команду :hlsearch, чтобы активировать выделение результатов поиска.
ПРИМЕЧАНИЕ
В разных дистрибутивах vim может поддерживать или не поддерживать подсвет- ку результатов поиска в тексте. В Ubuntu, например, по умолчанию включена сильно упрощенная версия vim. В таких системах необходимо установить более полную версию vim с помощью диспетчера пакетов.
Заключение
В этой главе мы рассмотрели несколько примеров использования регулярных выражений. Круг практического применения регулярных выражений можно
302
Глава 19. Регулярные выражения
расширить еще больше, если задействовать их для поиска в других приложениях, поддерживающих такую возможность. Например, с их помощью можно выпол- нять поиск на страницах справочного руководства:
[me@linuxbox ~]$ cd /usr/share/man/man1 [me@linuxbox man1]$ zgrep -El ‘regex|regular expression’ *.gz
Программа zgrep реализует интерфейс к grep, позволяя читать сжатые файлы. В данном примере выполняется поиск в сжатых файлах первого раздела справоч- ного руководства. Результатом этой команды будет список файлов, содержащих строку regex или regular expression. Как видите, регулярные выражения поддержи- ваются множеством программ.
Простые регулярные выражения обладают одной интересной особенностью, ко- торую мы пропустили, — обратными ссылками (back references). Они будут рас- сматриваться в следующей главе.
20 Обработка текста
Все Unix-подобные операционные системы широко используют текстовые фай- лы для хранения данных разных типов. Этим объясняется такое большое разноо- бразие инструментов обработки текста. В этой главе мы рассмотрим программы, которые используются для выполнения самых разных манипуляций с текстом. В следующей главе мы продолжим знакомство со средствами обработки текста, уделив больше внимания программам форматирования текста перед печатью и программам, удовлетворяющим другие потребности человека.
В этой главе мы повторно рассмотрим уже знакомые программы и познакомимся с новыми:
cat — объединяет файлы и выводит их в стандартный вывод;
sort — сортирует строки из текстовых файлов;
uniq — сообщает о повторяющихся строках или удаляет их;
cut — удаляет фрагменты из каждой строки в файлах;
paste — выполняет слияние строк из файлов;
join — объединяет строки из двух файлов по общему полю;
comm — выполняет построчное сравнение двух сортированных файлов;
diff — выполняет построчное сравнение файлов;
patch — применяет diff-файл (файл с результатами сравнения командой diff) к оригиналу;
tr — перекодирует или удаляет символы;
sed — потоковый редактор для фильтрации и преобразования текста;
aspell — интерактивная программа проверки орфографии.
304
Глава 20. Обработка текста
Области применения текста
К настоящему моменту мы познакомились с парой текстовых редакторов (nano и vim), рассмотрели несколько конфигурационных файлов и увидели вывод не- скольких десятков команд, и все это в текстовом виде. А для чего еще использует- ся текст? Как оказывается, много для чего.
Документы
Многие люди записывают документы в простом текстовом формате. Очевидно, достаточно удобно хранить простые заметки в небольших текстовых файлах, од- нако и большие документы также можно записывать в простом текстовом фор- мате. Один из популярных подходов состоит в том, чтобы записать большой до- кумент в текстовом формате и затем использовать язык разметки для описания форматирования конечного документа. Многие научные статьи написаны подоб- ным способом, так как системы обработки текста на основе Unix были в числе пер- вых, обеспечивших улучшенную поддержку типографического оформления, так необходимого авторам в технических дисциплинах.
Веб-страницы
Самым популярным в мире форматом электронных документов является, по- жалуй, формат веб-страниц. Веб-страницы — это текстовые документы с размет- кой HTML (Hypertext Markup Language — язык разметки гипертекста) или XML (Extensible Markup Language — расширяемый язык разметки), описывающей ви- зуальный формат документа.
Электронная почта
Электронная почта является текстовой средой по своей природе. Даже нетексто- вые вложения преобразуются в текстовое представление перед передачей. В этом можно убедиться, загрузив электронное письмо и просмотрев его с помощью less. Вы увидите, что письмо начинается с заголовка, описывающего отправителя письма и промежуточные серверы, принимавшие его в процессе доставки, за кото- рым следует тело письма с его содержимым.
Вывод на принтер
В Unix-подобных системах данные выводятся на печать в простом текстовом виде, или, если страница содержит графику, она преобразуется в описание страницы
cat
305
в текстовом формате, известном как PostScript, которое затем посылается програм- ме, генерирующей графические точки для печати на бумаге.
Исходный код программ
Многие программы командной строки, имеющиеся в Unix-подобных системах, были созданы для поддержки системного администрирования и разработки про- граммного обеспечения, и программы обработки текста не исключение. Многие из них предназначались для решения задач, связанных с разработкой программного обеспечения. Важность обработки текста для программистов объясняется тем, что любое программное обеспечение начинает свое существование как текст. Исход- ный код, часть программы, которую пишет программист, всегда имеет текстовый формат.
А вот и наши старые знакомые!
В главе 6 мы познакомились с некоторыми командами, способными принимать дан- ные не только из аргументов командной строки, но и из стандартного ввода. В той главе мы очень коротко познакомились с ними, но сейчас пришло время более близ- кого знакомства — мы узнаем, как их можно использовать для обработки текста.
cat
Программа cat содержит множество интересных параметров. Многие из них ис- пользуются для улучшенного отображения текстового содержимого. Примером может служить параметр -A, используемый для отображения непечатаемых сим- волов в тексте. Иногда необходимо знать, имеются ли управляющие символы в просматриваемом тексте. Наиболее распространенными из них являются сим- волы табуляции (в противоположность пробелам) и символы возврата каретки, часто представляющие концы строк в текстовых файлах, оформленных в стиле MS-DOS. Другим распространенным вариантом является файл, содержащий строки с завершающими пробелами.
Давайте создадим файл для экспериментов, используя cat как примитивный тек- стовый процессор. Для этого введем команду cat (указав файл для перенаправ- ления вывода), а следом — наш текст, завершив строки нажатием клавиши ENTER и закончив все комбинацией CTRL+D — она сообщит программе cat, что достигнут конец файла. В этом примере мы ввели символ табуляции и добавили в конец строки несколько пробелов:
306
Глава 20. Обработка текста
[me@linuxbox ~]$ cat > foo.txt The quick brown fox jumped over the lazy dog1. [me@linuxbox ~]$
Далее, вызовем cat с параметром -A, чтобы показать текст:
[me@linuxbox ~]$ cat -A foo.txt ^IThe quick brown fox jumped over the lazy dog. $ [me@linuxbox ~]$
Как видите, символ табуляции в тексте представлен парой символов ^I. Эта обыч- ная форма записи означает «CTRL+I», то есть, как оказывается, — символ табуля- ции. Здесь также видно, что символ $ отмечает истинный конец строки, помогая увидеть дополнительные пробелы в конце строки.
ТЕКСТ В MS-DOS И UNIX
Одна из причин, почему может появиться желание использовать cat для ото- бражения непечатаемых символов в тексте, — необходимость определить при- сутствие символов возврата каретки. Откуда берутся скрытые возвраты каретки? Из DOS и Windows! В Unix и DOS концы строк в текстовых файлах оформляются по-разному. В Unix строки заканчиваются символом перевода строки (ASCII 10), тогда как в MS-DOS и ее наследницах для этой цели используется последователь- ность из возврата каретки (ASCII 13) и перевода строки.
Существует несколько способов преобразовать файлы из формата DOS в формат Unix. Во многих системах Linux имеются программы unix2dos и dos2unix для пре- образования текстовых файлов в формат DOS и обратно. Однако если в вашей системе нет программы dos2unix, не волнуйтесь. Процесс преобразования текста из формата DOS в формат Unix очень прост — достаточно лишь удалить ненужные возвраты каретки. Это можно сделать с помощью пары программ, с которыми мы познакомимся позже в этой главе.
Программа cat имеет также параметры, используемые для изменения текста. Наиболее известными являются -n, добавляющий номера строк, и -s, подавля- ющий вывод множества пустых строк, идущих подряд. Давайте продемонстри- руем их:
1 Английский аналог фразы: «Съешь же ещё этих мягких французских булок, да выпей чаю», содержащей все буквы алфавита. В этой главе, чтобы избежать полного переписы- вания всех авторских примеров, мы будем работать с ней. — Примеч. ред.
cat
307
[me@linuxbox ~]$ cat > foo.txt The quick brown fox
jumped over the lazy dog. [me@linuxbox ~]$ cat -ns foo.txt 1 The quick brown fox 2 3 jumped over the lazy dog. [me@linuxbox ~]$
В этом примере мы создали новую версию тестового файла foo.txt, содержащую две строки, разделенные двумя пустыми строками. После обработки текста ко- мандой cat с параметрами -ns одна пустая строка была удалена, а остальные стро- ки пронумерованы. Хотя это не самая сложная обработка текста, все же это об- работка.
sort
Программа sort сортирует содержимое стандартного ввода или одного или не- скольких файлов, указанных в командной строке, и записывает результаты в стан- дартный вывод. Применив тот же прием, который мы использовали совместно с командой cat, можно продемонстрировать обработку стандартного ввода:
[me@linuxbox ~]$ sort > foo.txt c b a [me@linuxbox ~]$ cat foo.txt a b c
После запуска команды мы ввели буквы c, b и a, а затем признак конца файла с по- мощью комбинации CTRL+D. Затем просмотрели получившийся файл и увидели, что строки в нем отсортированы.
Поскольку sort может принимать несколько файлов в аргументах командной строки, существует возможность объединить множество файлов в один отсорти- рованный файл. Например, если у вас имеется три файла и вам нужно объединить их в один отсортированный файл, это можно выполнить следующим образом:
sort file1.txt file2.txt file3.txt > final_sorted_list.txt
Программа sort имеет несколько интересных параметров. Их неполный список приводится в табл. 20.1.
308
Глава 20. Обработка текста
Таблица 20.1. Часто используемые параметры команды sort
Параметр
Длинный параметр
Описание
-b
–ignore-leading-blanks
-f
-n
-r
-k
-m
-o
-t
–ignore-case
–numeric-sort
–reverse
–key=поле1[,поле2]
–merge
–output=файл
–field-separator=символ
По умолчанию сортировка выполняется с учетом содержимого всей строки, на- чиная с первого символа. Этот параметр заставляет sort пропустить начальные про- белы в строках и при сортировке начинать сравнение строк с первых не пробельных символов
Выполнять сортировку без учета регистра символов
Выполнять сортировку, опираясь на числовые значения строк. Этот параметр позволяет осуществлять сортировку в соот- ветствии с числовыми значениями, а не по алфавиту
Сортировать в обратном порядке. Строки в результате будут следовать в порядке убывания, а не возрастания
Сортировать по ключевым полям, начиная с поля1 и заканчивая полем2, а не по всей строке (поясняется ниже)
Интерпретировать каждый аргумент, как имя предварительно отсортированного файла. Позволяет объединить несколько файлов в общий результат без выполнения дополнительной сортировки
Записать результат сортировки не в стан- дартный вывод, а в указанный файл
Определяет символ, разделитель полей. По умолчанию в качестве разделителя исполь- зуются пробелы и символы табуляции
Имена большинства параметров из представленных выше говорят сами за себя, однако некоторые требуют дополнительных пояснений. Прежде всего рассмо- трим параметр -n, используемый для сортировки по числовым значениям. Этот параметр позволяет сортировать строки по их числовым значениям. Продемон- стрировать действие этого параметра можно на примере сортировки результатов команды du, чтобы определить каталог, занимающий больший объем дискового пространства. Обычно команда du выводит результаты, отсортированные по име- нам каталогов:
cat
309
[me@linuxbox ~]$ du -s /usr/share/* | head 252 /usr/share/aclocal 96 /usr/share/acpi-support 8 /usr/share/adduser 196 /usr/share/alacarte 344 /usr/share/alsa 8 /usr/share/alsa-base 12488 /usr/share/anthy 8 /usr/share/apmd 21440 /usr/share/app-install 48 /usr/share/application-registry
В этом примере мы передали результаты по конвейеру программе head, чтобы ограничить число результатов первыми 10 строками. Мы можем изменить эту команду, добавив сортировку по числовым значениям, чтобы получить 10 самых объемных каталогов:
[me@linuxbox ~]$ du -s /usr/share/* | sort -nr | head 509940 /usr/share/locale-langpack 242660 /usr/share/doc 197560 /usr/share/fonts 179144 /usr/share/gnome 146764 /usr/share/myspell 144304 /usr/share/gimp 135880 /usr/share/dict 76508 /usr/share/icons 68072 /usr/share/apps 62844 /usr/share/foomatic
С помощью параметров n и r мы получили сортировку по числовым значениям в обратном порядке, в результате наибольшие значения оказались в начале спи- ска. Такой способ сортировки стал возможен, потому что числовые значения на- ходятся в начале каждой строки. Но как быть, если потребуется отсортировать строки по числовым значениям, находящимся в середине строки, как, например, в результатах команды ls -l?
[me@linuxbox ~]$ ls -l /usr/bin | head total 152948 -rwxr-xr-x 1 root root 34824 2016-04-04 02:42 [ -rwxr-xr-x 1 root root 101556 2007-11-27 06:08 a2p -rwxr-xr-x 1 root root 13036 2016-02-27 08:22 aconnect -rwxr-xr-x 1 root root 10552 2007-08-15 10:34 acpi -rwxr-xr-x 1 root root 3800 2016-04-14 03:51 acpi_fakekey -rwxr-xr-x 1 root root 7536 2016-04-19 00:19 acpi_listen -rwxr-xr-x 1 root root 3576 2016-04-29 07:57 addpart -rwxr-xr-x 1 root root 20808 2016-01-03 18:02 addr2line -rwxr-xr-x 1 root root 489704 2016-10-09 17:02 adept_batch
310
Глава 20. Обработка текста
Забудем на время, что ls может сортировать свои результаты по размеру, и выпол- ним сортировку с помощью sort:
[me@linuxbox ~]$ ls -l /usr/bin | sort -nr -k 5 | head -rwxr-xr-x 1 root root 8234216 2016-04-07 17:42 inkscape -rwxr-xr-x 1 root root 8222692 2016-04-07 17:42 inkview -rwxr-xr-x 1 root root 3746508 2016-03-07 23:45 gimp-2.4 -rwxr-xr-x 1 root root 3654020 2016-08-26 16:16 quanta -rwxr-xr-x 1 root root 2928760 2016-09-10 14:31 gdbtui -rwxr-xr-x 1 root root 2928756 2016-09-10 14:31 gdb -rwxr-xr-x 1 root root 2602236 2016-10-10 12:56 net -rwxr-xr-x 1 root root 2304684 2016-10-10 12:56 rpcclient -rwxr-xr-x 1 root root 2241832 2016-04-04 05:56 aptitude -rwxr-xr-x 1 root root 2202476 2016-10-10 12:56 smbcacls
Программа sort часто вовлекается в обработку табличных данных, таких как ре- зультат команды ls выше. Если воспользоваться терминологией баз данных, об этой таблице можно сказать, что каждая строка — это запись и каждая запись со- стоит из множества полей, таких как атрибуты файла, счетчик ссылок, имя файла, размер файла и т. д. Программа sort способна обрабатывать поля по отдельно- сти. Согласно той же терминологии баз данных, мы можем указать одно или не- сколько ключевых полей, которые должны использоваться как ключи сортировки. В примере, приведенном выше, мы добавили параметры n и r, чтобы выполнить сортировку по числовым значениям в порядке убывания, а также параметр k с ар- гументом 5, чтобы указать, что сортировка должна выполняться по пятому полю.
Параметр k очень интересен и обладает множеством любопытных свойств, но пре- жде чем приступить к их изучению, поговорим о том, как sort определяет поля. Рассмотрим очень простой текстовый файл, содержащий единственную строку с именем автора этой книги:
William Shotts
По умолчанию sort «видит» в этой строке два поля. Первое поле содержит после- довательность символов William, второе — последовательность символов Shotts, то есть пробельные символы (пробелы и символы табуляции) интерпретируются как разделители полей, и эти разделители включаются в поле при выполнении сортировки.
Взглянув еще раз на любую строку в выводе нашей команды ls, можно сказать, что она содержит восемь полей и пятое поле хранит размер файла:
-rwxr-xr-x 1 root root 8234216 2016-04-07 17:42 inkscape
Для следующей серии экспериментов возьмем файл с историей выпуска новых версий трех популярных дистрибутивов Linux в период с 2006 по 2008 год. Каждая
строка в файле содержит три поля: название дистрибутива, номер версии и дата выпуска в формате ММ/ДД/ГГГГ:
cat
311
SUSE 10.2 12/07/2006 Fedora 10 11/25/2008 SUSE 11.0 06/19/2008 Ubuntu 8.04 04/24/2008 Fedora 8 11/08/2007 SUSE 10.3 10/04/2007 Ubuntu 6.10 10/26/2006 Fedora 7 05/31/2007 Ubuntu 7.10 10/18/2007 Ubuntu 7.04 04/19/2007 SUSE 10.1 05/11/2006 Fedora 6 10/24/2006 Fedora 9 05/13/2008 Ubuntu 6.06 06/01/2006 Ubuntu 8.10 10/30/2008 Fedora 5 03/20/2006
С помощью текстового редактора (например, vim) введите эти данные и сохраните в файле с именем distros.txt.
Далее попробуем отсортировать файл и посмотрим, что из этого получится:
[me@linuxbox ~]$ sort distros.txt Fedora 10 11/25/2008 Fedora 5 03/20/2006 Fedora 6 10/24/2006 Fedora 7 05/31/2007 Fedora 8 11/08/2007 Fedora 9 05/13/2008 SUSE 10.1 05/11/2006 SUSE 10.2 12/07/2006 SUSE 10.3 10/04/2007 SUSE 11.0 06/19/2008 Ubuntu 6.06 06/01/2006 Ubuntu 6.10 10/26/2006 Ubuntu 7.04 04/19/2007 Ubuntu 7.10 10/18/2007 Ubuntu 8.04 04/24/2008 Ubuntu 8.10 10/30/2008
У нас это почти получилось. Единственная проблема возникла с сортировкой номеров версий Fedora. Так как в лексикографическом смысле 1 предшествует 5, версия 10 оказалась вверху, тогда как версия 9 — внизу.
312
Глава 20. Обработка текста
Чтобы исправить эту ошибку, выполним сортировку по нескольким ключам. Итак, нам нужно выполнить сортировку по первому полю в алфавитном порядке, а затем по второму полю в числовом порядке. Программа sort позволяет указать в командной строке несколько параметров -k, чтобы можно было определить не- сколько ключей сортировки. В действительности в ключ можно включать диа- пазон полей. Если диапазон не определен (как в примерах, приведенных выше), sort использует в качестве ключа часть строки, начинающуюся с указанного поля и простирающуюся до конца строки.
Вот как выглядит синтаксис сортировки по нескольким ключам:
[me@linuxbox ~]$ sort –key=1,1 –key=2n distros.txt Fedora 5 03/20/2006 Fedora 6 10/24/2006 Fedora 7 05/31/2007 Fedora 8 11/08/2007 Fedora 9 05/13/2008 Fedora 10 11/25/2008 SUSE 10.1 05/11/2006 SUSE 10.2 12/07/2006 SUSE 10.3 10/04/2007 SUSE 11.0 06/19/2008 Ubuntu 6.06 06/01/2006 Ubuntu 6.10 10/26/2006 Ubuntu 7.04 04/19/2007 Ubuntu 7.10 10/18/2007 Ubuntu 8.04 04/24/2008 Ubuntu 8.10 10/30/2008
Здесь для ясности использовались имена параметров в длинной форме, однако с тем же успехом можно было бы передать параметры -k 1,1 -k 2n. В аргументе для первого экземпляра параметра ключа мы указали диапазон полей, входящих в первый ключ. Так как сортировка должна выполняться только по первому полю, мы указали диапазон 1,1, что означает: «начиная с поля 1 и заканчивая полем 1». Второму экземпляру мы передали аргумент 2n, который означает: «ключом со- ртировки является второе поле, и сортировка выполняется в порядке числовых значений». В конце определения ключа допускаются однобуквенные имена па- раметров, они указывают на тип сортировки. Имена этих однобуквенных параме- тров совпадают с именами глобальных параметров программы sort: b (пропустить начальные пробелы), n (числовая сортировка), r (сортировка в обратном порядке) и т. д.
Третье поле в списке содержит дату в формате, неудобном для сортировки. В ком- пьютере даты обычно приводятся к виду ГГГГ-ММ-ДД, что упрощает сортировку
cat
313
в хронологическом порядке, но здесь используется американский формат ММ/ ДД/ГГГГ. Как же тогда отсортировать этот список в хронологическом порядке?
К счастью, sort предоставляет такую возможность. Параметр –key позволяет определять смещения внутри полей, чтобы в качестве ключей можно было исполь- зовать части полей:
[me@linuxbox ~]$ sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt Fedora 10 11/25/2008 Ubuntu 8.10 10/30/2008 SUSE 11.0 06/19/2008 Fedora 9 05/13/2008 Ubuntu 8.04 04/24/2008 Fedora 8 11/08/2007 Ubuntu 7.10 10/18/2007 SUSE 10.3 10/04/2007 Fedora 7 05/31/2007 Ubuntu 7.04 04/19/2007 SUSE 10.2 12/07/2006 Ubuntu 6.10 10/26/2006 Fedora 6 10/24/2006 Ubuntu 6.06 06/01/2006 SUSE 10.1 05/11/2006 Fedora 5 03/20/2006
Добавив параметр -k 3.7, мы сообщили программе sort, что она должна исполь- зовать для сортировки ключ, начинающийся с седьмого символа в третьем поле, который соответствует началу года. Аналогично, параметры -k 3.1 и -k 3.4 опре- деляют ключи сортировки по месяцу и дню месяца. Мы также добавили параме- тры n и r, чтобы выполнить числовую сортировку в обратном порядке. Параметр b добавлен для исключения начальных пробелов из поля с датой (число которых в разных строках отличается и тем самым влияет на результат сортировки).
В некоторых файлах в качестве разделителей используются символы, отличные от пробелов и символов табуляции; возьмем, к примеру, файл /etc/passwd:
[me@linuxbox ~]$ head /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh
314
Глава 20. Обработка текста
Поля в этом файле отделяются друг от друга двоеточием (:). Можно ли отсорти- ровать содержимое этого файла с использованием ключевых полей? Программа sort поддерживает параметр -t для определения символа — разделителя полей. Чтобы отсортировать содержимое файла passwd по седьмому полю (командная оболочка по умолчанию), используем такую команду:
[me@linuxbox ~]$ sort -t ‘:’ -k 7 /etc/passwd | head me:x:1001:1001:Myself,,,:/home/me:/bin/bash root:x:0:0:root:/root:/bin/bash dhcp:x:101:102::/nonexistent:/bin/false gdm:x:106:114:Gnome Display Manager:/var/lib/gdm:/bin/false hplip:x:104:7:HPLIP system user,,,:/var/run/hplip:/bin/false klog:x:103:104::/home/klog:/bin/false messagebus:x:108:119::/var/run/dbus:/bin/false polkituser:x:110:122:PolicyKit,,,:/var/run/PolicyKit:/bin/false pulse:x:107:116:PulseAudio daemon,,,:/var/run/pulse:/bin/false
Определив двоеточие как разделитель полей, мы смогли выполнить сортировку по седьмому полю.
uniq — выявление или удаление повторяющихся строк
В сравнении с sort программа uniq более легковесна. Она решает, казалось бы, тривиальную задачу. Когда ей передается сортированный файл (в том числе и стандартный ввод), она удаляет повторяющиеся строки и выводит результат в стандартный вывод. Она часто используется в сочетании с sort для удаления повторяющихся строк.
СОВЕТ
Даже притом, что uniq — инструмент, традиционный для Unix, который часто используется вместе с sort, GNU-версия sort поддерживает параметр -u, уда- ляющий повторяющиеся строки из сортированных результатов.
Давайте создадим текстовый файл для последующих экспериментов:
[me@linuxbox ~]$ cat > foo.txt a b c a b c
Не забудьте ввести CTRL+D, чтобы завершить ввод с клавиатуры. Если теперь при- менить uniq к нашему текстовому файлу, результат ничем не будет отличаться от оригинала; повторяющиеся записи никуда не исчезли:
cat
315
[me@linuxbox ~]$ uniq foo.txt a b c a b c
Чтобы uniq действительно выполнила свою работу, исходные данные нужно сна- чала отсортировать:
[me@linuxbox ~]$ sort foo.txt | uniq a b c
Это объясняется тем, что uniq удаляет повторяющиеся записи, только если они следуют друг за другом.
uniq имеет несколько параметров. Наиболее часто используемые из них перечис- лены в табл. 20.2.
Таблица 20.2. Часто используемые параметры команды uniq
Параметр
-c
-d
Длинный параметр
–count
Описание
Вывести список повторяющихся строк, предваряя их числом найденных дубликатов
–repeated
Вывести только повторяющиеся, не уникальные строки
-f n
–skip-fields=n
Пропустить n начальных полей в каждой строке. Деле- ние на поля производится по пробельным символам, как в программе sort; однако, в отличие от sort, про- грамма uniq не имеет параметра для настройки альтер- нативного разделителя полей
-i
-s n
-u
–ignore-case
Сравнивать строки без учета регистра символов
–skip-chars=n
Пропустить n начальных символов в каждой строке
–unique
Вывести только уникальные строки. Подразумевается по умолчанию
316
Глава 20. Обработка текста
В следующем примере используется параметр -c программы uniq для определе- ния числа повторяющихся строк в исходном текстовом файле:
[me@linuxbox ~]$ sort foo.txt | uniq -c 2 a 2 b 2 c
Нарезка и перетасовка текста
Далее мы обсудим три программы, которые используются для выделения колонок текста из файлов и их компоновки различными способами.
cut — удаление фрагментов из всех строк в файлах
Программа cut используется для извлечения фрагментов текста из строк и выво- да их в стандартный вывод. Она может принимать имена файлов в аргументах или данные со стандартного ввода.
Определение фрагментов строк, подлежащих извлечению, реализовано не очень удобно, и для этой цели применяются параметры, перечисленные в табл. 20.3.
Таблица 20.3. Параметры команды cut для выбора фрагментов
Параметр
Длинный параметр
Описание
-c список_сим- волов
–characters= спи- сок_символов
Извлекает фрагмент строки, определя- емый списком_символов. Список может включать один или несколько числовых диапазонов, разделенных запятыми
-f список_полей
–fields= список_полей Извлекает одно или несколько полей из строки, как определено аргументом спи- сок_символов. Список может включать одно или несколько полей или диапазо- нов полей, разделенных запятыми
-d символ_разде- литель
–delimiter= символ_разделитель
–complement
В присутствии параметра -f, в качестве разделителя полей используется сим- вол_разделитель. По умолчанию поля должны отделяться друг от друга одним символом табуляции
Извлекает строку текста целиком, кроме фрагментов, определяемых параметром -c и/или -f
Нарезка и перетасовка текста
317
Как видите, программа cut не обладает особенной гибкостью. Она лучше всего подходит для извлечения фрагментов из текста, произведенного другими про- граммами, а не человеком. Давайте вернемся к нашему файлу distros.txt и по- смотрим, достаточно ли он «хорош» для программы cut. Если воспользоваться программой cat с параметром -A, можно увидеть, отвечает ли файл требованию в отношении использования символа табуляции в качестве разделителя полей.
[me@linuxbox ~]$ cat -A distros.txt SUSE^I10.2^I12/07/2006$ Fedora^I10^I11/25/2008$ SUSE^I11.0^I06/19/2008$ Ubuntu^I8.04^I04/24/2008$ Fedora^I8^I11/08/2007$ SUSE^I10.3^I10/04/2007$ Ubuntu^I6.10^I10/26/2006$ Fedora^I7^I05/31/2007$ Ubuntu^I7.10^I10/18/2007$ Ubuntu^I7.04^I04/19/2007$ SUSE^I10.1^I05/11/2006$ Fedora^I6^I10/24/2006$ Fedora^I9^I05/13/2008$ Ubuntu^I6.06^I06/01/2006$ Ubuntu^I8.10^I10/30/2008$ Fedora^I5^I03/20/2006$
Похоже, что все в порядке: пробелы отсутствуют и поля разделены единствен- ными символами табуляции. Поскольку вместо пробелов в файле используются символы табуляции, можно воспользоваться параметром -f для извлечения поля:
[me@linuxbox ~]$ cut -f 3 distros.txt 12/07/2006 11/25/2008 06/19/2008 04/24/2008 11/08/2007 10/04/2007 10/26/2006 05/31/2007 10/18/2007 04/19/2007 05/11/2006 10/24/2006 05/13/2008 06/01/2006 10/30/2008 03/20/2006
318
Глава 20. Обработка текста
Так как поля в файле distros.txt разделены символами табуляции, их удобнее из- влекать с помощью cut именно как поля, а не как группы символов. Когда поля разделяются символами табуляции, маловероятно, что строки будут содержать одно и то же число символов, из-за чего определение позиций символов в строках становится сложной или неразрешимой задачей. В примере, приведенном выше, мы смогли извлечь поля с датами, которые, к нашей удаче, все имеют одинаковую длину, поэтому теперь мы можем показать, как выполняется извлечение групп символов, для чего попробуем извлечь год из каждой строки:
[me@linuxbox ~]$ cut -f 3 distros.txt | cut -c 7-10 2006 2008 2008 2008 2007 2007 2006 2007 2007 2007 2006 2006 2008 2006 2008 2006
Применив cut второй раз к нашему списку, мы смогли извлечь символы в пози- циях с 7-й по 10-ю, которые соответствуют году в поле с датой. Форма записи 7-10 — это пример определения диапазона. Полное описание особенностей опре- деления диапазонов находится на странице справочного руководства (man) для команды cut.
При работе с полями определим разделитель, отличающийся от символа табуля- ции. Следующий пример извлекает первое поле из файла /etc/passwd:
[me@linuxbox ~]$ cut -d ‘:’ -f 1 /etc/passwd | head root daemon bin sys sync games man lp mail news
Нарезка и перетасовка текста
319
С помощью параметра -d мы определили, что роль разделителя полей будет играть двоеточие.
ЗАМЕНА СИМВОЛОВ ТАБУЛЯЦИИ
Наш файл distros.txt идеально отформатирован для извлечения полей с использо- ванием cut. Но что, если нам понадобится обработать файл, вырезая фрагменты по символам, а не по полям? Для этого нам нужно заменить символы табуляции в файле соответствующим числом пробелов. К счастью, в GNU-пакете Coreutils имеется инструмент для этого — программа expand. Она может принимать имена файлов в аргументах или данные со стандартного ввода и выводить измененный текст в стандартный вывод.
Если обработать наш файл distros.txt программой expand, мы сможем использовать cut -c для извлечения любых диапазонов символов из файла. Например, с помо- щью следующей команды можно извлечь год выпуска из нашего файла со списком, применив cut для извлечения всех символов с 23-й позиции до конца строки:
[me@linuxbox ~]$ expand distros.txt | cut -c 23-
В состав пакета Coreutils входит также программа unexpand, замещающая пробелы символами табуляции.
paste — слияние строк из файлов
Команда paste выполняет операцию, обратную команде cut. Вместо извлечения колонок текста из файла она добавляет одну или несколько колонок текста в файл. Для этого она читает содержимое нескольких файлов, комбинирует поля, найден- ные в них, и выводит результат в стандартный вывод. Подобно программе cut, paste принимает несколько файлов в аргументах и/или данные со стандартного ввода. Для демонстрации возможностей программы paste выполним небольшую хирургическую операцию с файлом distros.txt, чтобы получить список выпусков в хронологическом порядке.
Сначала применим команду sort, чтобы получить список дистрибутивов, отсо- ртированный по дате выпуска, и сохраним результат в файле distros-by-date.txt:
[me@linuxbox ~]$ sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt > distros-by-date.txt
Затем с помощью cut извлечем два первых поля (с именами дистрибутивов и но- мерами версий) и сохраним результат в файле distro-versions.txt:
320
Глава 20. Обработка текста
[me@linuxbox ~]$ cut -f 1,2 distros-by-date.txt > distros-versions.txt [me@linuxbox ~]$ head distros-versions.txt Fedora 10 Ubuntu 8.10 SUSE 11.0 Fedora 9 Ubuntu 8.04 Fedora 8 Ubuntu 7.10 SUSE 10.3 Fedora 7 Ubuntu 7.04
Завершая этап подготовки, извлечем даты выпусков и сохраним их в файле distro- dates.txt:
[me@linuxbox ~]$ cut -f 3 distros-by-date.txt > distros-dates.txt [me@linuxbox ~]$ head distros-dates.txt 11/25/2008 10/30/2008 06/19/2008 05/13/2008 04/24/2008 11/08/2007 10/18/2007 10/04/2007 05/31/2007 04/19/2007
Теперь у нас есть все необходимое. Чтобы завершить процедуру, с помощью paste добавим колонку с датами перед названиями и номерами версий дистрибутивов, создав хронологический список. Для этого достаточно просто вызвать paste и пе- редать ей файлы в требуемом порядке.
[me@linuxbox ~]$ paste distros-dates.txt distros-versions.txt 11/25/2008 Fedora 10 10/30/2008 Ubuntu 8.10 06/19/2008 SUSE 11.0 05/13/2008 Fedora 9 04/24/2008 Ubuntu 8.04 11/08/2007 Fedora 8 10/18/2007 Ubuntu 7.10 10/04/2007 SUSE 10.3 05/31/2007 Fedora 7 04/19/2007 Ubuntu 7.04 12/07/2006 SUSE 10.2 10/26/2006 Ubuntu 6.10
Нарезка и перетасовка текста
321
10/24/2006 Fedora 6 06/01/2006 Ubuntu 6.06 05/11/2006 SUSE 10.1 03/20/2006 Fedora 5
join — объединение строк из двух файлов по общему полю
Программа join действует подобно paste, в том смысле, что добавляет колонки в файл, но делает это по-своему. Операция join у многих ассоциируется с реляци- онными базами данных, где она объединяет записи из нескольких таблиц по обще- му ключевому полю. Программа join выполняет ту же операцию. Она объединяет данные из множества файлов, опираясь на общее ключевое поле.
Чтобы понять, как действует операция join в реляционной базе данных, пред- ставьте очень маленькую базу данных с двумя таблицами, по одной записи в каж- дой. Первая таблица, с именем CUSTOMERS, имеет три поля: номер клиента (CUSTNUM), имя клиента (FNAME) и фамилия клиента (LNAME):
CUSTNUM FNAME LNAME ========= ====== ====== 4681934 John Smith
Вторая таблица, с именем ORDERS, имеет четыре поля: номер заказа (ORDERNUM), номер клиента (CUSTNUM), количество (QUAN) и пункт зака- за (ITEM):
ORDERNUM CUSTNUM QUAN ITEM ========== ========= ===== ==== 3014953305 4681934 1 Blue Widget
Обратите внимание, что обе таблицы имеют общее поле CUSTNUM. Это важно, так как оно устанавливает отношение между таблицами.
Применив операцию join, мы сможем объединить поля из двух таблиц, чтобы по- лучить желаемый результат, например, для подготовки накладной. Проверяя со- впадение значений в полях CUSTNUM обеих таблиц, операция join выдаст сле- дующий результат:
FNAME LNAME QUAN ITEM ====== ====== ===== ==== John Smith 1 Blue Widget
Для демонстрации программы join нам понадобится пара файлов с общим ключом. Возьмем в качестве отправной точки файл distros-by-date.txt и из него
322
Глава 20. Обработка текста
сконструируем два дополнительных файла. Первый будет содержать даты выпу- сков (которые в этом примере будут играть роль общего ключа) и названия дис- трибутивов:
[me@linuxbox ~]$ cut -f 1,1 distros-by-date.txt > distros-names.txt [me@linuxbox ~]$ paste distros-dates.txt distros-names.txt > distros-key-names.txt [me@linuxbox ~]$ head distros-key-names.txt 11/25/2008 Fedora 10/30/2008 Ubuntu 06/19/2008 SUSE 05/13/2008 Fedora 04/24/2008 Ubuntu 11/08/2007 Fedora 10/18/2007 Ubuntu 10/04/2007 SUSE 05/31/2007 Fedora 04/19/2007 Ubuntu
И второй — даты выпусков и номера версий:
[me@linuxbox ~]$ cut -f 2,2 distros-by-date.txt > distros-vernums.txt [me@linuxbox ~]$ paste distros-dates.txt distros-vernums.txt > distros-keyvernums.txt [me@linuxbox ~]$ head distros-key-vernums.txt 11/25/2008 10 10/30/2008 8.10 06/19/2008 11.0 05/13/2008 9 04/24/2008 8.04 11/08/2007 8 10/18/2007 7.10 10/04/2007 10.3 05/31/2007 7 04/19/2007 7.04
Теперь у нас есть два файла с общим ключом (поле «дата выпуска»). Здесь важ- но отметить, что файлы должны быть отсортированы по ключевому полю, чтобы программа join выдала правильный результат.
[me@linuxbox ~]$ join distros-key-names.txt distros-key-vernums.txt | head 11/25/2008 Fedora 10 10/30/2008 Ubuntu 8.10 06/19/2008 SUSE 11.0 05/13/2008 Fedora 9 04/24/2008 Ubuntu 8.04 11/08/2007 Fedora 8 10/18/2007 Ubuntu 7.10
Сравнение текста
323
10/04/2007 SUSE 10.3 05/31/2007 Fedora 7 04/19/2007 Ubuntu 7.04
Отметьте также, что по умолчанию в качестве разделителя полей во входных дан- ных join использует символы табуляции, а в выводе — пробел. Такое поведение можно изменить с помощью параметров. За дополнительными подробностями об- ращайтесь к странице справочного руководства (man) для join.
Сравнение текста
Довольно часто бывает необходимо сравнить версии текстовых файлов. Для си- стемных администраторов и разработчиков программного обеспечения это осо- бенно важно. Системному администратору, например, может понадобиться срав- нить имеющийся конфигурационный файл с предыдущей версией, чтобы понять суть возникшей проблемы. Аналогично, программисту часто бывает необходимо увидеть изменения, происшедшие в программе с течением времени.
comm — построчное сравнение двух сортированных файлов
Программа comm сравнивает два текстовых файла, показывая, какие строки в них уникальные, а какие — одинаковые. Для демонстрации создадим с помощью cat два почти идентичных файла:
[me@linuxbox ~]$ cat > file1.txt a b c d [me@linuxbox ~]$ cat > file2.txt b c d e
Затем сравним эти два файла с помощью comm:
[me@linuxbox ~]$ comm file1.txt file2.txt a b c d e
324
Глава 20. Обработка текста
Как видите, comm произвела вывод в три колонки. Первая колонка содержит уни- кальные строки из первого файла, вторая — уникальные строки из второго фай- ла, третья — строки, одинаковые в обоих файлах. Программа comm поддерживает параметры в формате -n, где n может быть числом 1, 2 или 3. При использовании эти параметры определяют номера колонок, вывод которых следует подавить. Например, чтобы вывести только одинаковые строки, нужно подавить вывод колонок 1 и 2:
[me@linuxbox ~]$ comm -12 file1.txt file2.txt b c d
diff — построчное сравнение файлов
Подобно программе comm, diff используется для выявления различий между файлами. Однако diff намного более сложный инструмент, поддерживаю- щий вывод во множестве форматов и способный обрабатывать сразу огромные коллекции файлов. Программа diff часто используется разработчиками про- граммного обеспечения для исследования различий между разными версиями исходного программного кода, потому что позволяет рекурсивно обходить ка- талоги, которые часто называют деревьями исходного кода (source trees). Часто программа diff применяется для создания diff-файлов, или заплат (patches), которые могут использоваться другими программами, такими как patch (о ко- торой рассказывается чуть ниже), для преобразования файлов из одной версии в другую.
Если применить diff к файлам из предыдущего примера, можно увидеть стиль вывода результатов ее работы по умолчанию: краткое описание различий между двумя файлами:
[me@linuxbox ~]$ diff file1.txt file2.txt 1d0 e
В формате по умолчанию каждой группе изменений предшествует команда изме- нения (табл. 20.4) в форме диапазон — операция — диапазон, описывающая пози- ции и типы изменений, которые нужно выполнить, чтобы преобразовать первый файл во второй.
В этом формате любой диапазон представлен списком через запятую номеров начальной и конечной строки. Хотя этот формат используется по умолчанию
Сравнение текста
325
(главным образом для совместимости со стандартом POSIX и обратной совме- стимости с традиционными версиями diff для Unix), он не так широко исполь- зуется, как другие, дополнительные форматы. Два других формата, получив- ших большую популярность, — это контекстный формат и унифицированный формат.
Таблица 20.4. Команды изменения, генерируемые программой diff
Команда
Описание
r1ar2
r1cr2
r1dr2
Добавить строки, находящиеся в диапазоне r2 во втором файле, после строк в позиции r1 в первом файле
Изменить (заменить) строки в диапазоне r1 в первом файле строками в диапазоне r2 во втором файле
Удалить строки в диапазоне r1 в первом файле, которые находились бы в диапазоне r2 во втором файле
При использовании контекстного формата (параметр -c) вывод выглядит так:
[me@linuxbox ~]$ diff -c file1.txt file2.txt *** file1.txt 2008-12-23 06:40:13.000000000 -0500 — file2.txt 2008-12-23 06:40:34.000000000 -0500
*** 1,4 ****
a b c d — 1,4 —- b
c d
- e
Вывод начинается с имен двух файлов и времени последнего их изменения. Пер- вый файл отмечается звездочками, а второй — дефисами. На протяжении всей оставшейся части листинга эти маркеры обозначают соответствующие им файлы. Далее следуют группы изменений, включая заданное по умолчанию число окру- жающих строк, определяющих контекст. Первая группа начинается со строки:
*** 1,4 ****
326
Глава 20. Обработка текста
указывающей на строки с номерами с 1-го по 4-й в первом файле. Далее следует строка:
— 1,4 —-
указывающая на строки с номерами с 1-го по 4-й во втором файле. Внутри группы изменений присутствуют строки, начинающиеся с одного из четырех индикато- ров, перечисленных в табл. 20.5.
Таблица 20.5. Индикаторы изменений, генерируемые программой diff при использовании контекстного формата
Индикатор
Значение
(нет)
Строка показана для контекста. В ней отсутствуют различия между файлами
!
Строка удалена. Эта строка присутствует в первом файле и отсутствует во втором
Строка добавлена. Эта строка присутствует во втором файле и отсут- ствует в первом
Строка изменена. Выводятся две версии строки, каждая в соответству- ющем разделе внутри группы изменений
Унифицированный формат напоминает контекстный, но более компактный. За- дается параметром -u:
[me@linuxbox ~]$ diff -u file1.txt file2.txt — file1.txt 2008-12-23 06:40:13.000000000 -0500 +++ file2.txt 2008-12-23 06:40:34.000000000 -0500 @@ -1,4 +1,4 @@ -a b c d +e
Самое большое отличие между контекстным и унифицированным форматами — отсутствие повторяющихся контекстных строк, благодаря чему обеспечивается большая компактность унифицированного формата в сравнении с контекстным. В примере, приведенном выше, видны те же времена последнего изменения фай- лов, что и в контекстном формате, за которыми следует строка @@ -1,4 +1,4 @@. Она указывает номера строк в первом и во втором файлах, описываемых группой
Сравнение текста
327
изменений. Далее следуют сами строки с тремя (по умолчанию) строками контек- ста. Каждая строка начинается с одного из трех возможных символов, значение которых описывается в табл. 20.6.
Таблица 20.6. Индикаторы изменений, генерируемые программой diff при использовании унифицированного формата
Индикатор
Значение
(нет)
Строка, одинаковая в обоих файлах
Строка отсутствует в первом файле
Строка присутствует только в первом файле
patch — применение diff-файла к оригиналу
Программа patch используется для применения изменений к текстовым файлам. Она принимает вывод программы diff и обычно используется для преобразо- вания старых версий файлов в более новые. Рассмотрим один известный при- мер. Ядро Linux разрабатывается большой, свободно организованной группой разработчиков, от которых неиссякаемым потоком идут небольшие изменения в исходном коде. Ядро Linux включает миллионы строк программного кода, но изменения, присылаемые одним разработчиком за один раз, очень невелики. Раз- работчикам нет смысла пересылать все дерево исходных текстов ядра всякий раз, когда вносится небольшое изменение. Вместо этого они присылают diff-файлы. Эти файлы описывают различия между предыдущей версией ядра и новой, вклю- чающей изменения разработчика. Другой разработчик, получивший такое изме- нение, использует программу patch, позволяющую применить предложенное из- менение к своему дереву исходных текстов. Использование пары программ diff/ patch дает два важных преимущества:
diff-файл очень невелик в сравнении с полным деревом исходных текстов;
diff-файл наглядно показывает произведенные изменения, что позволяет экс-
пертам быстро применить эти изменения и оценить их.
Разумеется, пару diff/patch можно применять к любым текстовым файлам, не только к исходному коду. Эти программы с таким же успехом можно применять к конфигурационным файлам или другому тексту.
Чтобы подготовить diff-файл для последующего его применения программой patch, документация GNU предлагает использовать diff, как показано ниже:
diff -Naur старый_файл новый_файл > diff_файл
328
Глава 20. Обработка текста
где старый_файл и новый_файл могут быть одиночными файлами или каталогами. Параметр r поддерживает рекурсивный обход вложенных подкаталогов.
Получив diff-файл, его можно применить к старому файлу, чтобы получить новый:
patch patchfile.txt [me@linuxbox ~]$ patch файл_unix
где файл_dos — это файл, подлежащий преобразованию, а файл_unix — результат. В этой форме команды используется экранированная последовательность \r, представляющая символ возврата каретки. Чтобы увидеть полный список после- довательностей и классов символов, поддерживаемых программой tr, попробуйте:
[me@linuxbox ~]$ tr –help
330
Глава 20. Обработка текста
tr также позволяет выполнять и другие трюки. При вызове с параметром -s команда tr «сжимает» (squeeze), или удаляет, повторяющиеся экземпляры сим- волов:
[me@linuxbox ~]$ echo “aaabbbccc” | tr -s ab abccc
Здесь у нас имеется строка с повторяющимися символами. Передав множество ab команде tr, мы удалили повторяющиеся экземпляры символов, входящие в мно- жество, при этом остальные символы (c), отсутствующие в множестве, остались нетронутыми. Обратите внимание, что повторяющиеся символы должны следо- вать подряд. В противном случае сжатие не даст никакого эффекта:
[me@linuxbox ~]$ echo “abcabcabc” | tr -s ab abcabcabc
ROT13: НЕ САМЫЙ НАДЕЖНЫЙ СПОСОБ ШИФРОВАНИЯ
Одно забавное применение команды tr — шифрование текста по алгоритму ROT13. ROT13 — тривиальный тип шифрования, основанный на простом подстановочном шифре. Шифрованием назвать этот алгоритм можно только с большой натяжкой, скорее это алгоритм обфускации (запутывания) текста. Он используется иногда для запутывания потенциально уязвимого содержимого. Метод заключается в простом смещении каждого символа на 13 позиций далее по алфавиту. Так как число 13 соответствует середине набора из 26 символов, повторное применение алгоритма к тексту приводит к его восстановлению в исходное состояние. Шифрование с по- мощью tr выполняется, как показано ниже:
echo “secret text” | tr a-zA-Z n-za-mN-ZA-M frperg grkg
Повторное применение той же процедуры приводит к обратному преобразованию:
echo “frperg grkg” | tr a-zA-Z n-za-mN-ZA-M secret text
Многие программы для работы с электронной почтой и чтения новостей Usenet поддерживают шифрование ROT13. В Википедии можно найти замечательную статью по этой теме: http://ru.wikipedia.org/wiki/ROT13.
Редактирование на лету
331
sed — потоковый редактор для фильтрации и преобразования текста
Имя sed — это сокращенное словосочетание stream editor (потоковый редактор). Данная команда осуществляет редактирование потока текста, получаемого из множества файлов или подаваемого на стандартный ввод команды. sed — мощная и достаточно сложная программа (ей посвящены целые книги), поэтому здесь мы не будем рассматривать ее во всех подробностях.
В общем случае sed используется следующим образом: ей передается единствен- ная команда редактирования (в командной строке) или имя файла сценария с множеством команд, и она применяет эти команды к каждой строке в потоке текста. Ниже приводится очень простой пример sed в действии:
[me@linuxbox ~]$ echo “front” | sed ’s/front/back/’ back
В этом примере с помощью echo создается поток текста с единственным словом, который по конвейеру передается программе sed. sed, в свою очередь, применяет инструкцию s/front/back/ к тексту в потоке и выводит результат. Эта команда напоминает команду подстановки (поиск с заменой) в редакторе vi.
Команды sed начинаются с единственной буквы. В примере, рассмотренном выше, буква s представляет команду подстановки (substitution). За ней следуют иско- мая строка и строка замены, разделенные слешем. В качестве разделителя мож- но использовать любые символы. По общепринятому соглашению, чаще других используется символ «слеш», но sed будет использовать в качестве разделителя любой символ, следующий сразу за командой. Ту же самую команду можно было бы записать иначе:
[me@linuxbox ~]$ echo “front” | sed ’s_front_back_’ back
Символ подчеркивания, следующий сразу за командой, становится разделителем. Возможность употребления произвольных разделителей можно использовать для улучшения читаемости команд, как будет показано далее.
Большинству команд в sed может предшествовать адрес, который определяет, какие строки во входном потоке должны редактироваться. Если адрес отсутству- ет, команда редактирования применяется ко всем строкам во входном потоке.
332
Глава 20. Обработка текста
В простейшем случае адрес — это номер строки. Мы могли бы добавить единицу в наш пример:
[me@linuxbox ~]$ echo “front” | sed ‘1s/front/back/’ back
Добавление адреса 1 в команду гарантирует применение операции подстановки только к первой строке в нашем однострочном потоке. Можно указать другое число:
[me@linuxbox ~]$ echo “front” | sed ‘2s/front/back/’ front
Теперь, как видите, редактирование не было выполнено, потому что во входном потоке отсутствует строка с номером 2.
Адреса можно выражать множеством способов. В табл. 20.7 перечислены адреса, чаще других используемые на практике.
Таблица 20.7. Форма записи адресов в команде sed
Адрес
Описание
n
$
Номер строки, где n — положительное число
Последняя строка
/регулярное_выражение/
адр1,адр2
первая~шаг
адр1,+n
адр!
Строки, соответствующие простому регулярному выраже- нию POSIX. Обратите внимание, что регулярное выражение должно ограничиваться символ «слеш» с обеих сторон. При желании можно использовать другие ограничитель- ные символы, определив регулярное выражение в форме \cрегулярное_выражениеc, где c — альтернативный символ- ограничитель
Диапазон строк с номерами от адр1 по адр2 включительно. Каждый адрес может иметь любую форму из перечисленных выше
Соответствует строке с номером первая и каждой последую-
щей с указанным шагом. Например, адрес 12 соответствует
всем строкам с нечетными номерами, а адрес 55 соответ-
ствует пятой строке и каждой пятой последующей
Соответствует строке с адресом адр1 и следующим за ней n строкам
Соответствует всем строкам, кроме строки с адресом адр, где адрес может иметь любую форму из перечисленных выше
Редактирование на лету
333
Рассмотрим разные способы адресации строк на примере файла distros.txt, создан- ного выше в этой главе. Сначала попробуем диапазоны номеров строк:
[me@linuxbox ~]$ sed -n ‘1,5p’ distros.txt SUSE 10.2 12/07/2006 Fedora 10 11/25/2008 SUSE 11.0 06/19/2008 Ubuntu 8.04 04/24/2008 Fedora 8 11/08/2007
В нашем примере мы вывели строки с 1-й по 5-ю. Для этого использовалась коман- да p, которая просто выводит строки, соответствующие адресам. Однако здесь нам пришлось добавить параметр -n (параметр подавления автоматического вывода), чтобы программа sed не выводила все строки, что она делает по умолчанию.
Далее попробуем задействовать регулярное выражение:
[me@linuxbox ~]$ sed -n ‘/SUSE/p’ distros.txt SUSE 10.2 12/07/2006 SUSE 11.0 06/19/2008 SUSE 10.3 10/04/2007 SUSE 10.1 05/11/2006
Добавив регулярное выражение /SUSE/, заключенное в слеши, мы смогли выде- лить строки подобно тому, как это делает программа grep.
Наконец, попробуем применить оператор отрицания, добавив в адрес восклица- тельный знак (!):
[me@linuxbox ~]$ sed -n ‘/SUSE/!p’ distros.txt Fedora 10 11/25/2008 Ubuntu 8.04 04/24/2008 Fedora 8 11/08/2007 Ubuntu 6.10 10/26/2006 Fedora 7 05/31/2007 Ubuntu 7.10 10/18/2007 Ubuntu 7.04 04/19/2007 Fedora 6 10/24/2006 Fedora 9 05/13/2008 Ubuntu 6.06 06/01/2006 Ubuntu 8.10 10/30/2008 Fedora 5 03/20/2006
Здесь мы видим ожидаемый результат: все строки из файла, кроме совпавших с регулярным выражением.
334
Глава 20. Обработка текста
Пока что мы познакомились лишь с двумя командами редактирования, поддер- живаемыми программой sed: s и p. В табл. 20.8 приводится более полный список основных команд редактирования.
Таблица 20.8. Основные команды редактирования sed
Команда
Описание
=
a
d
i
p
q
Q
s/регулярное_выражение/ строка_замены/
y/множество1/множество2
Выводит номер текущей строки
Добавляет текст в конец текущей строки
Удаляет текущую строку
Вставляет текст в начало текущей строки
Выводит текущую строку. По умолчанию sed выводит все строки, но редактирует только соответствующие указан- ному адресу. Поведение по умолчанию можно отменить, передав параметр -n
Завершает sed без обработки остальных строк. Если пара- метр -n не указан, выводит текущую строку
Завершает sed без обработки остальных строк
Замещает совпадение с регулярным выражением строкой замены. Строка замены может включать специальный символ &, обозначающий совпадение с регулярным вы- ражением. Кроме того, строка замены может включать последовательности, с \1 по \9, обозначающие совпаде- ния с соответствующими подвыражениями в регулярном выражении. Дополнительную информацию по этой теме можно найти в обзоре обратных ссылок ниже. За симво- лом «слеш», закрывающим строку замены, может следо- вать необязательный флаг, определяющий дополнитель- ные особенности поведения команды
Выполняет перекодирование, преобразуя символы из первого множества в символы второго множества. Имейте в виду, что в отличие от программы tr, sed требует, что- бы оба множества были одинаковой длины
Команда s, вне всяких сомнений, используется намного чаще других команд ре- дактирования. Далее мы рассмотрим только часть ее возможностей, выполняя ре- дактирование нашего файла distros.txt. Мы уже говорили, что поле даты в distros. txt хранит информацию не в самом «дружественном» для компьютеров виде. Здесь даты записаны в формате ММ/ДД/ГГГГ, однако гораздо удобнее (для со- ртировки) было бы, если бы даты были записаны в формате ГГГГ-ММ-ДД. Замена
Редактирование на лету
335
представления дат вручную — довольно утомительное занятие и чревато ошибка- ми, но с помощью sed ту же замену можно выполнить в одно действие:
[me@linuxbox ~]$ sed ’s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-
2/’ distros.txt
SUSE 10.2 2006-12-07
Fedora 10 2008-11-25
SUSE 11.0 2008-06-19
Ubuntu 8.04 2008-04-24
Fedora 8 2007-11-08
SUSE 10.3 2007-10-04
Ubuntu 6.10 2006-10-26
Fedora 7 2007-05-31
Ubuntu 7.10 2007-10-18
Ubuntu 7.04 2007-04-19
SUSE 10.1 2006-05-11
Fedora 6 2006-10-24
Fedora 9 2008-05-13
Ubuntu 6.06 2006-06-01
Ubuntu 8.10 2008-10-30
Fedora 5 2006-03-20
Прекрасный результат! Правда, команда выглядит устрашающе, но она работает. За один шаг мы изменили представление дат во всем файле. Этот пример также наглядно показывает, почему про регулярные выражения иногда в шутку говорят «только для записи». Мы можем писать их, но прочитать их порой никак не полу- чается. Прежде чем сбежать от этой устрашающей команды, давайте посмотрим, как она была сконструирована. Во-первых, как мы уже знаем, эта команда имеет следующую структуру:
sed ’s/регулярное_выражение/строка_замены/’ distros.txt
Теперь разберем регулярное выражение, отыскивающее даты. Так как даты имеют формат ММ/ДД/ГГГГ и находятся в конце строки, найти их можно с помощью следующего выражения:
[0-9]{2}/[0-9]{2}/[0-9]{4}$
которому соответствуют две цифры, слеш, две цифры, слеш, четыре цифры и ко- нец строки. Так, с регулярным выражением разобрались, а что со строкой замены? Чтобы описать ее, нам необходимо познакомиться с новой для нас особенностью регулярных выражений, которую можно использовать в некоторых приложениях, поддерживающих BRE. Эта особенность называется обратные ссылки, и действу- ет она так: если в строке замены присутствует последовательность \n, где n — чис- ло от одного до девяти, эта последовательность будет ссылаться на совпадение
336
Глава 20. Обработка текста
с соответствующим подвыражением в предшествующем регулярном выражении. Чтобы создать подвыражение, достаточно просто заключить часть регулярного выражения в круглые скобки, например:
([0-9]{2})/([0-9]{2})/([0-9]{4})$
Теперь у нас есть три подвыражения. Первому соответствует месяц, второму — число месяца и третьему — год. Соответственно строку замены можно выразить так:
\3-\1-\2
что даст нам в результате такую последовательность: год, дефис, месяц, дефис, число месяца.
Теперь наша команда приобрела следующий вид:
sed ’s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/’ distros.txt
Но остались две проблемы. Первая: дополнительные слеши в регулярном выра- жении запутают программу sed, когда она попытается интерпретировать коман- ду s. Вторая: так как по умолчанию sed принимает только простые регулярные выражения, некоторые символы в нашем регулярном выражении будут интерпре- тироваться как литералы, а не как метасимволы. Мы решим обе проблемы, при- менив обратные слеши для экранирования нужных символов:
sed ’s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/’ distros.txt
И дело в шляпе!
Другая особенность команды s — возможность использования дополнительных флагов вслед за строкой замены. Наиболее примечательным из них является флаг g, который требует от sed применить поиск с заменой к строке глобально (globally), а не только к первому найденному совпадению, как это делается по умолчанию. Например:
[me@linuxbox ~]$ echo “aaabbbccc” | sed ’s/b/B/’ aaaBbbccc
Как видите, замена была выполнена только для первого вхождения буквы b, а остальные остались нетронутыми. Добавив флаг g, можно изменить все вхож- дения:
[me@linuxbox ~]$ echo “aaabbbccc” | sed ’s/b/B/g’ aaaBBBccc
Редактирование на лету
337
До сих пор мы передавали команды программе sed только по одной и только в командной строке. Однако существует возможность создавать более сложные коман ды в файлах сценариев и передавать эти сценарии с помощью параметра -f. Для демонстрации создадим с помощью sed отчет на основе нашего файла distros. txt. Отчет будет содержать заголовок вверху, измененные даты и названия дис- трибутивов будут преобразованы в верхний регистр. Для этого нам понадобится написать сценарий, поэтому запустите текстовый редактор и введите следующие строки:
Сценарий для sed, создающий отчет о дистрибутивах Linux #
1 i
Linux Distributions Report
s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
Сохраните сценарий в файл с именем distros.sed и запустите его:
[me@linuxbox ~]$ sed -f distros.sed distros.txt
Linux Distributions Report
SUSE 10.2 2006-12-07 FEDORA 10 2008-11-25 SUSE 11.0 2008-06-19 UBUNTU 8.04 2008-04-24 FEDORA 8 2007-11-08 SUSE 10.3 2007-10-04 UBUNTU 6.10 2006-10-26 FEDORA 7 2007-05-31 UBUNTU 7.10 2007-10-18 UBUNTU 7.04 2007-04-19 SUSE 10.1 2006-05-11 FEDORA 6 2006-10-24 FEDORA 9 2008-05-13 UBUNTU 6.06 2006-06-01 UBUNTU 8.10 2008-10-30 FEDORA 5 2006-03-20
Как видите, сценарий выдал желаемый результат, но как он это сделал? Давайте вернемся еще раз к нашему сценарию. Выведем его с помощью программы cat так, чтобы она пронумеровала строки:
[me@linuxbox ~]$ cat -n distros.sed 1 # Сценарий для sed, создающий отчет о дистрибутивах Linux
338
Глава 20. Обработка текста
2
3 1 i
4
5 Linux Distributions Report
6
7 s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/
8 y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
Строка с номером 1 — это комментарий. Так же как во многих конфигурационных файлах и языках программирования, широко используемых в Linux, коммента- рии начинаются с символа #, за которым следует пояснительный текст. Коммента- рии можно помещать в сценарии в любое место (только не в сами команды), и они, безусловно, полезны для всех, кто хочет выяснить особенности работы сценария или сопровождать его.
Строка 2 — это пустая строка. Так же как комментарии, мы можем добавлять пу- стые строки для удобочитаемости.
Многие команды sed поддерживают адресацию строк. Адреса используются, что- бы определить, к каким строкам во входном потоке применяется операция. Адре- са могут выражаться в форме простых номеров строк, диапазонов номеров и спе- циального номера $, соответствующего последней входной строке.
В строках с 3-й по 6-ю содержится текст, который должен быть вставлен по адресу 1, в первую входную строку. Команда i, сопровождаемая последовательностью из обратного слеша и перевода строки, производит экранированный символ перево- да, или то, что мы называем символом продолжения строки. Эта последователь- ность используется во многих случаях, в том числе и в сценариях на языке ко- мандной оболочки, позволяя встраивать символ перевода строки в поток текста так, чтобы он не воспринимался интерпретатором (в данном случае программой sed) как конец строки. Команда i, а также команда a (добавления текста в конец) и команда c (замены текста) могут располагаться в нескольких строках текста, при условии, что каждая из них, кроме последней, будет завершаться символом продолжения строки. Шестая строка в нашем сценарии фактически завершает вставляемый текст и заканчивается уже не символом продолжения строки, а про- стым переводом строки, сообщая о завершении команды i.
ПРИМЕЧАНИЕ
Символ продолжения строки формируется из обратного слеша и следующего сразу за ним перевода строки. Никаких промежуточных пробелов между ними быть не должно.
Редактирование на лету
339
Строка 7 — наша команда поиска с заменой. Так как ей не предшествует никакой конкретный адрес, она будет выполнена для каждой строки во входном потоке.
Строка 8 выполняет перекодировку букв нижнего регистра в буквы верхнего ре- гистра. Обратите внимание, что, в отличие от программы tr, команда y в sed не поддерживает ни диапазоны символов (например, [a-z]), ни классы символов POSIX. И снова, так как команде y не предшествует никакой конкретный адрес, она будет выполнена для каждой строки во входном потоке.
ИСПОЛЬЗУЮЩИЕ SED ТАКЖЕ ЧАСТО ВЫБИРАЮТ…
Программа sed обладает очень широкими возможностями. С ее помощью можно решать весьма сложные задачи, связанные с редактированием потока текста. Но чаще она используется для выполнения простеньких операций, определе- ние которых укладывается в одну строку. Для решения объемных задач многие предпочитают использовать другие инструменты. Наиболее популярными из них являются awk и perl. Они не относятся к разряду простых инструментов, как программы, рассматриваемые здесь, а являются полноценными языками про- граммирования. perl, например, часто применяется взамен языка командной оболочки для решения многих задач системного администрирования, а также пользуется большой популярностью как средство разработки веб-приложений. awk имеет более узкую область применения. Основное его достоинство заклю- чается в возможности управления табличными данными. Он напоминает sed в том смысле, что программы на awk обычно занимаются построчной обработкой текстовых файлов, используя схему, похожую на адреса в sed со следующими за ними операциями. Даже при том, что рассмотрение awk и perl выходит за рамки этой книги, они являются отличными инструментами для пользователей командной строки в Linux.
aspell — интерактивная проверка орфографии
Последний инструмент, который мы рассмотрим в этой главе, — программа aspell, интерактивное средство проверки орфографии. Программа aspell является пре- емницей программы ispell, существовавшей прежде, и может использоваться как ее замена. Чаще всего программа aspell используется другими программами в тех случаях, когда необходима функция проверки орфографии, однако aspell может также весьма эффективно использоваться как самостоятельный инстру- мент командной строки. Она способна проверять текстовые файлы разных типов, включая документы HTML, программы на C/C++, электронные письма и другие специальные виды текста.
340
Глава 20. Обработка текста
Чтобы проверить орфографию в файле с простым текстом, можно вызвать aspell, как показано ниже:
aspell check текстовый_файл
где текстовый_файл — это имя файла для проверки. В качестве практического при- мера создадим простой текстовый файл с именем foo.txt, содержащий несколько произвольных орфографических ошибок:
[me@linuxbox ~]$ cat > foo.txt The quick brown fox jimped over the laxy dog.
Затем проверим файл с помощью aspell:
[me@linuxbox ~]$ aspell check foo.txt
Поскольку в режиме проверки (check) программа aspell действует интерактивно, вы увидите следующий экран:
The quick brown fox jimped over the laxy dog.
- jumped 6) wimped
- gimped 7) camped
- comped 8) humped
- limped 9) impede
- pimped 0) umped i) Ignore I) Ignore all r) Replace R) Replace all a) Add l) Add Lower b) Abort x) Exit
?
В верхней части экрана выводится текст с выделенным подозрительным словом. В середине — 10 вариантов исправления ошибки, пронумерованных от 0 до 9, а за- тем следует список других возможных действий. Наконец, в самом низу выводит- ся приглашение к вводу, готовое принять наш выбор.
Если ввести 1, aspell заменит подозрительное слово jimped словом jumped и перей- дет к следующему подозрительному слову, laxy. Если выбрать вариант замены lazy, aspell выполнит подстановку и завершится (так как ошибок во введенной фразе больше нет). После того как aspell завершится, заглянем в файл и увидим, что все ошибки исправлены:
[me@linuxbox ~]$ cat foo.txt The quick brown fox jumped over the lazy dog.
Редактирование на лету
341
Если вызвать программу aspell без параметра –dont-backup, она создаст резерв- ную копию файла с исходным текстом, добавив к имени файла расширение .bak.
А теперь похвастаемся умением пользоваться программой sed и вернем наши ошибки на место, чтобы продолжить эксперименты с нашим файлом:
[me@linuxbox ~]$ sed -i ’s/lazy/laxy/; s/jumped/jimped/’ foo.txt
Параметр -i сообщает программе sed, что требуется отредактировать файл «на месте», в том смысле, что изменения нужно произвести в самом файле, а не пере- слать их в стандартный вывод. Здесь также показана возможность передать более одной команды редактирования, разделив их точкой с запятой.
Далее мы посмотрим, как aspell справляется с текстовыми файлами разных ви- дов. С помощью текстового редактора, например vim (наиболее смелые могут по- пробовать использовать sed), добавим в файл немного разметки HTML:
Mispelled HTML file
<p>The quick brown fox jimped over the laxy dog.
Если теперь попытаться проверить орфографию в измененном файле, мы стол- кнемся с проблемой. Вызвав команду
[me@linuxbox ~]$ aspell check foo.txt
мы получим следующее:
Mispelled HTML file
<p>The quick brown fox jimped over the laxy dog.
- HTML 4) Hamel
- ht ml 5) Hamil
- ht-ml 6) hotel i) Ignore I) Ignore all r) Replace R) Replace all
342
Глава 20. Обработка текста
a) Add l) Add Lower b) Abort x) Exit
?
aspell посчитала, что HTML-теги записаны с ошибками. Эту проблему можно преодолеть, передав параметр -H (HTML) режима проверки:
[me@linuxbox ~]$ aspell -H check foo.txt
Теперь результат будет выглядеть так:
Mispelled HTML file
<p>The quick brown fox jimped over the laxy dog.
- Mi spelled 6) Misapplied
- Mi-spelled 7) Miscalled
- Misspelled 8) Respelled
- Dispelled 9) Misspell
- Spelled 0) Misled i) Ignore I) Ignore all r) Replace R) Replace all a) Add l) Add Lower b) Abort x) Exit
?
Теперь теги HTML игнорируются, и проверке подвергаются только фрагменты файла, не являющиеся частью разметки. В этом режиме содержимое HTML-тегов игнорируется и не проверяется, исключение составляет содержимое тегов ALT (точнее, атрибутов alt), которое будет проверяться в этом режиме проверки.
ПРИМЕЧАНИЕ
По умолчанию aspell игнорирует адреса URL и электронной почты в тексте. Эту ситуацию можно изменить с помощью параметров командной строки. Также можно указать, какие теги разметки должны проверяться, а какие пропускаться. За подробностями обращайтесь к странице справочного руководства (man) для aspell.
Дополнительное задание
343
Заключение
В этой главе мы познакомились с несколькими из множества инструментов ко- мандной строки для обработки текста. В следующей главе мы рассмотрим еще не- сколько. Нужно признать, что для многих из вас пока не очевидно, как или для чего можно было бы использовать некоторые из них в повседневной работе, хотя мы попытались привести практические примеры. В следующих главах вы увиди- те, что эти инструменты формируют базовый набор для решения большого коли- чества практических задач. Это вам пригодится, когда мы перейдем к сценариям на языке командной оболочки, где эти инструменты по-настоящему продемон- стрируют свои возможности.
Дополнительное задание
Существует несколько интересных команд обработки текста, на которые стоит об- ратить внимание. Среди них split (разбивает файлы на фрагменты), csplit (раз- бивает файлы на фрагменты, опираясь на контекст) и sdiff (выводит различия между файлами, что называется, «бок о бок»).
21 Форматирование
вывода
В этой главе мы продолжим знакомство с инструментами, имеющими отноше- ние к тексту, сосредоточившись на программах для форматирования выводимого текста, а не его изменения. Эти инструменты часто используются для подготовки текста к печати, о которой мы поговорим в следующей главе. В этой главе мы рас- смотрим следующие программы:
nl — нумерует строки;
fold — выполняет перенос строк, ограничивая их указанной длиной;
fmt — выполняет простое форматирование текста;
pr — форматирует текст для печати;
printf — форматирует и выводит данные;
groff — система форматирования документов.
Инструменты простого форматирования
Для начала рассмотрим несколько инструментов простого форматирования. В ос- новном это узкоспециализированные и довольно бесхитростные программы, но их можно использовать для решения простых задач в конвейерах и сценариях.
Инструменты простого форматирования
345
nl — нумерация строк
Программа nl предназначена для решения простой задачи: она выполняет нуме- рацию строк. В простейшем случае использования nl напоминает команду cat -n:
[me@linuxbox ~]$ nl distros.txt | head 1 SUSE 10.2 12/07/2006 2 Fedora 10 11/25/2008 3 SUSE 11.0 06/19/2008 4 Ubuntu 8.04 04/24/2008 5 Fedora 8 11/08/2007 6 SUSE 10.3 10/04/2007 7 Ubuntu 6.10 10/26/2006 8 Fedora 7 05/31/2007 9 Ubuntu 7.10 10/18/2007 10 Ubuntu 7.04 04/19/2007
Так же, как cat, программа nl может принимать несколько имен файлов в аргу- ментах командной строки или данные со стандартного ввода. Однако nl имеет ряд параметров и поддерживает простейшую форму разметки, обеспечивая более сложные способы нумерации.
nl поддерживает идею логических страниц. Это дает возможность начинать ну- мерацию на каждой странице заново. С помощью параметров можно определить номер первой строки и протяженность нумерации, а также формат номеров. Логи- ческую страницу можно разбить на заголовок, тело и нижний колонтитул. В каж- дом разделе нумерация может начинаться с начала и/или может использоваться разный формат нумерации. Если программе nl передать несколько файлов, она будет интерпретировать их как один поток текста. Разделы в потоке выделяются добавлением в поток немного странной разметки, как показано в табл. 21.1.
Таблица 21.1. Разметка nl
Разметка
Значение
:::
::
:
Начало заголовка логической страницы
Начало тела логической страницы
Начало нижнего колонтитула логической страницы
Каждый элемент разметки из перечисленных в табл. 21.1 должен находиться в отдельной строке. После обработки элемента программа nl удалит его из по- тока текста.
346
Глава 21. Форматирование вывода
В табл. 21.2 перечислены наиболее часто используемые параметры nl.
Таблица 21.2. Наиболее часто используемые параметры nl
Параметр
Значение
-b стиль
Стиль нумерации тела, где аргумент стиль может иметь следующие значе- ния:
• a — нумеровать все строки; • t — нумеровать только непустые строки. Этот стиль применяется по
умолчанию;
• n — не нумеровать; • регулярное выражение — нумеровать только строки, соответствующие
простому регулярному выражению
-f стиль
-h стиль
Стиль нумерации нижнего колонтитула. По умолчанию имеет значение n (нет нумерации)
Стиль нумерации заголовка. По умолчанию имеет значение n (нет нумера- ции)
-i число
Шаг приращения номеров на странице. По умолчанию имеет значение 1
-n формат
Формат номеров, где аргумент формат может иметь следующие значения: • ln — с выравниванием по левому краю, без ведущих нулей; • rn — с выравниванием по правому краю, без ведущих нулей. Использу-
ется по умолчанию;
• rz — с выравниванием по правому краю и с ведущими нулями
-p
Не сбрасывать нумерацию в начале каждой логической страницы
-s строка
Добавить указанную строку в конец каждого номера строки, чтобы от- делить его от текста строки. По умолчанию используется один символ табуляции
-v число
Номер первой строки на каждой логической странице. По умолчанию имеет значение 1
-w ширина
Ширина поля номера строки. По умолчанию имеет значение 6
Следует отметить, что на практике нумеровать строки приходится довольно ред- ко, но мы можем использовать nl, чтобы посмотреть, как объединить несколько инструментов для решения более сложных задач. Возьмем за основу наши на- работки, созданные в предыдущей главе для получения отчета о дистрибутивах
Инструменты простого форматирования
347
Linux. Поскольку далее мы будем использовать программу nl, включим в текст разметку, отделяющую заголовок/тело/нижний колонтитул. Для этого откройте в текстовом редакторе сценарий для sed из предыдущей главы, добавьте в него строки с разметкой, как показано ниже, и сохраните сценарий в файле с именем distros-nl.sed:
Сценарий для sed, создающий отчет о дистрибутивах Linux #
1 i
\:\:\:
Linux Distributions Report
Name Ver. Released
—- —- ——–
\:\:
s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/
$ a
\:
End Of Report
Новый сценарий вставляет разметку логических страниц для nl и добавляет ниж- ний колонтитул в конец отчета. Обратите внимание, что нам пришлось удвоить обратные слеши в разметке, потому что sed обычно интерпретирует их как экра- нирующие символы.
Теперь выведем улучшенный отчет, объединив sort, sed и nl:
[me@linuxbox ~]$ sort -k 1,1 -k 2n distros.txt | sed -f distros-nl.sed | nl
Linux Distributions Report
Name Ver. Released
---- ---- --------
1 Fedora 5 2006-03-20
2 Fedora 6 2006-10-24
3 Fedora 7 2007-05-31
4 Fedora 8 2007-11-08
5 Fedora 9 2008-05-13
6 Fedora 10 2008-11-25
7 SUSE 10.1 2006-05-11
8 SUSE 10.2 2006-12-07
9 SUSE 10.3 2007-10-04
348
Глава 21. Форматирование вывода
10 SUSE 11.0 2008-06-19
11 Ubuntu 6.06 2006-06-01
12 Ubuntu 6.10 2006-10-26
13 Ubuntu 7.04 2007-04-19
14 Ubuntu 7.10 2007-10-18
15 Ubuntu 8.04 2008-04-24
16 Ubuntu 8.10 2008-10-30
End Of Report
Наш отчет является результатом объединения в конвейер нескольких команд. Сначала мы отсортировали список по названиям дистрибутивов и номерам вер- сий (поля 1 и 2), затем обработали результат программой sed, добавив заголовок отчета (включая разметку логических страниц для nl) и нижний колонтитул. В заключение мы обработали результат с помощью программы nl, которая по умолчанию нумерует только строки в потоке текста, принадлежащие разделу с те- лом логической страницы.
Попробуйте повторить команду и поэкспериментировать с разными параметрами команды nl. Интересный результат, например, можно получить с помощью
nl -n rz
и
nl -w 3 -s ’ '
fold — перенос строк после указанной длины
Перенос строк заключается в разрыве текстовых строк по указанной ширине. По- добно другим рассматриваемым командам, fold может принимать одно или не- сколько имен файлов или данные со стандартного ввода. Если передать команде fold простой поток текста, можно увидеть, как она действует:
[me@linuxbox ~]$ echo “The quick brown fox jumped over the lazy dog.” | fold -w 12 The quick br own fox jump ed over the lazy dog.
Здесь мы видим, как действует программа fold. Текст, посланный командой echo, был разбит на сегменты указанной в параметре -w ширины. В этом при- мере мы ограничили ширину строк 12 символами. Если ширина не указана, по умолчанию она принимается равной 80 символам. Обратите внимание, что стро- ки были разбиты без учета границ слов. Добавив параметр -s, можно заставить
Инструменты простого форматирования
349
fold разбивать строки по последнему доступному пробелу перед достижением указанной ширины:
[me@linuxbox ~]$ echo “The quick brown fox jumped over the lazy dog.” | fold -w 12 -s The quick brown fox jumped over the lazy dog.
fmt — простое форматирование текста
Программа fmt также позволяет выполнить перенос строк плюс кое-что еще. Она принимает файлы или данные со стандартного ввода и формирует абзацы в по- токе текста. По сути, она заполняет и объединяет строки, сохраняя пустые строки и отступы.
Для демонстрации нам понадобится некий текст. Возьмем фрагмент из Info- страницы для fmt:
`fmt’ читает текст из файла, заданного аргументами FILE (или со стандартного ввода, если аргументы отсутствуют), и выводит результат в стандартный вывод.
По умолчанию пустые строки, пробелы между словами и отступы сохраняются в выводе; последующие строки с разными отступами не объединяются; символы табуляции на входе заменяются соответствующим числом пробелов и выводятся в таком виде.
fmt' старается разбивать строки по концам предложений и стремится не разрывать строки после первого слова или перед последним словом в предложении. "Конец предложения" определяется либо по концу абзаца, либо по слову, завершающемуся любым из символов .?!’, за
которым следуют два пробела или символ перевода строки, любые
скобки или кавычки при этом игнорируются. Подобно TЕX, fmt' читает "абзацы" целиком, прежде чем выполнить перенос строк; программа использует вариант алгоритма, предложенного Дональдом Э. Кнутом (Donald E. Knuth) и Михаэлем Ф. Плассом (Michael F. Plass) в статье "Breaking Paragraphs Into Lines", Software–Practice & Experience’
11, 11 (November 1981), 1119-1184.
Скопируйте этот текст в текстовый редактор и сохраните в файле с именем fmt- info.txt. Теперь представьте, что нам нужно переформатировать этот текст, уме- стив его в колонку шириной 50 символов. Решить эту задачу можно с помощью команды fmt и ее параметра -w:
350
Глава 21. Форматирование вывода
[me@linuxbox ~]$ fmt -w 50 fmt-info.txt | head `fmt’ читает текст из файла, заданного аргументами FILE (или со стандартного ввода, если аргументы отсутствуют), и выводит результат в стандартный вывод. По умолчанию пустые строки, пробелы между словами и отступы сохраняются в выводе; последующие строки с разными отступами не объединяются; символы
Результат не особенно впечатляет. Может быть, стоит прочитать этот текст, так как он объясняет происходящее:
По умолчанию пустые строки, пробелы между словами и отступы сохраня- ются в выводе; последующие строки с разными отступами не объединяются; символы табуляции на входе заменяются соответствующим числом пробелов и выводятся в таком виде.
Итак, fmt сохраняет отступ в первой строке. К счастью, fmt имеет параметр, ис- правляющий это:
[me@linuxbox ~]$ fmt -cw 50 fmt-info.txt `fmt’ читает текст из файла, заданного аргументами FILE (или со стандартного ввода, если аргументы отсутствуют), и выводит результат в стандартный вывод.
По умолчанию пустые строки, пробелы между словами и отступы сохраняются в выводе; последующие строки с разными отступами не объединяются; символы табуляции на входе заменяются соответствующим числом пробелов и выводятся в таком виде.
fmt' старается разбивать строки по концам предложений и стремится не разрывать строки после первого слова или перед последним словом в предложении. "Конец предложения" определяется либо по концу абзаца, либо по слову, завершающемуся любым из символов .?!’,
за которым следует два пробела или символ
перевода строки, любые скобки или кавычки при
этом игнорируются. Подобно TeX, `fmt’ читает
“абзацы” целиком, прежде чем выполнить перенос
строк; программа использует вариант алгоритма,
предложенного Дональдом Э. Кнутом (Donald
Инструменты простого форматирования
351
E. Knuth) и Михаэлем Ф. Плассом (Michael F. Plass) в статье “Breaking Paragraphs Into Lines”, `Software–Practice & Experience’ 11, 11 (November 1981), 1119-1184.
Намного лучше. Добавив параметр -c, мы получили желаемый результат.
Программа fmt содержит несколько интересных параметров, которые перечисле- ны в табл. 21.3.
Таблица 21.3. Параметры fmt
Параметр
Значение
-c
-p строка
-s
-u
Включить режим обработки края (crown margin). В этом режиме сохраня- ется отступ первых строк абзаца. Последующие строки выравниваются по второй строке
Форматировать только строки, начинающиеся со строки, указанной в аргументе. После форматирования содержимое аргумента добавляется в начало каждой переформатированной строки. Этот параметр можно использовать для форматирования текста комментариев в исходном коде. Например, любой сценарий или конфигурационный файл, где комментарии начинаются с символа #, можно обработать командой fmt с параметром -p ‘# ‘, чтобы отформатировать только комментарии. Пример приводится ниже
Режим простой разбивки. В этом режиме выполняется только разбивка строк по указанной ширине. Короткие строки не объединяются. Этот режим можно использовать для форматирования исходного кода, когда объединение коротких строк нежелательно
Нормировать пробелы. Этот параметр применяется для форматирования в стандартном «машинописном стиле», то есть когда слова отделяются од- ним пробелом, а предложения — двумя. Этот режим удобно использовать для удаления выравнивающих пробелов, то есть пробелов, добавленных с целью выравнивания текста по обоим краям
-w ширина
Форматировать текст по указанной ширине. По умолчанию используется ширина 75 символов. Обратите внимание: в действительности fmt форма- тирует строки немного короче, чем указано в аргументе, чтобы обеспечить сбалансированность ширины текста
Особый интерес представляет параметр -p. С его помощью можно форматировать выбранные фрагменты файла, гарантировав, что все отформатированные строки будут начинаться с одной и той же последовательности символов. Многие языки программирования поддерживают комментарии, начинающиеся с символа решет- ки (#), и такие комментарии можно форматировать с помощью этого параметра.
352
Глава 21. Форматирование вывода
Давайте создадим файл, имитирующий исходный текст программы с коммента- риями:
[me@linuxbox ~]$ cat > fmt-code.txt
Этот файл содержит код с комментариями. #
Эта строка - комментарий. #
За ней следует другая строка с комментарием. #
И еще одна. #
Это не комментарий, а строка с кодом. Еще одна строка с кодом. И еще.
Файл примера содержит комментарии, начинающиеся со строки # (символ # и пробел), и строки «кода». Теперь воспользуемся командой fmt, чтобы отформа- тировать комментарии и при этом не затронуть код:
[me@linuxbox ~]$ fmt -w 50 -p ‘# ’ fmt-code.txt
Этот файл содержит код с комментариями. #
Эта строка - комментарий. За ней следует другая #
строка с комментарием. И еще одна. #
Это не комментарий, а строка с кодом. Еще одна строка с кодом. И еще.
Обратите внимание, что смежные строки комментариев были объединены, а пу- стые строки и строки, не начинающиеся с указанного префикса, остались нетро- нутыми.
pr — форматирование текста для печати
Программа pr используется для разбивки текста на страницы. Перед печатью текст зачастую желательно разбить на страницы с несколькими пустыми строка- ми в начале и в конце, чтобы получить пустые поля вверху и внизу каждой страни- цы. В дальнейшем эти поля можно использовать для вставки верхнего и нижнего колонтитулов.
Продемонстрируем работу pr, форматируя наш файл distros.txt в последователь- ность очень коротких страниц (ниже показаны только первые две страницы):
Инструменты простого форматирования
353
[me@linuxbox ~]$ pr -l 15 -w 65 distros.txt
2016-12-11 18:27 distros.txt Page 1
SUSE 10.2 12/07/2006 Fedora 10 11/25/2008 SUSE 11.0 06/19/2008 Ubuntu 8.04 04/24/2008 Fedora 8 11/08/2007
2012-16-11 18:27 distros.txt Page 2
SUSE 10.3 10/04/2007 Ubuntu 6.10 10/26/2006 Fedora 7 05/31/2007 Ubuntu 7.10 10/18/2007 Ubuntu 7.04 04/19/2007
В этом примере использовались параметры -l (длина (length) страницы) и -w (ширина (width) страницы), определяющие размеры «страницы» — 65 символов в ширину и 15 строк в длину. pr разбила содержимое файла distros.txt на отдельные страницы, добавив несколько пустых строк сверху и снизу, и создала заголовок по умолчанию, содержащий время последнего изменения файла, имя файла и номер страницы. Программа pr поддерживает множество параметров для управления форматированием страницы, но подробнее о них мы поговорим в главе 22.
printf — форматирование и вывод данных
В отличие от других команд в этой главе, команда printf не используется в кон- вейерной обработке (она не принимает данные со стандартного ввода) и редко применяется непосредственно в командной строке (чаще она используется в сце- нариях). Почему это так важно? Потому что она используется очень широко.
Команда printf (ее название происходит от print formatted (форматированный вы- вод)) первоначально была создана как функция для языка программирования C
354
Глава 21. Форматирование вывода
и впоследствии была реализована во многих других языках, включая язык команд- ной оболочки. Фактически в bash команда printf реализована как встроенная коман да. Она имеет следующий синтаксис:
printf “формат” аргументы
Команда принимает строку с описанием формата, которая затем применяется к списку аргументов. Отформатированный результат передается в стандартный вывод. Ниже приводится простой пример:
[me@linuxbox ~]$ printf “I formatted the string: %s\n” foo I formatted the string: foo
Строка формата может содержать литеральный текст (такой, как I formatted the string:), экранированные последовательности (такие, как \n, символ перевода строки) и последовательности, начинающиеся с символа %, которые называют спецификаторами преобразований (conversion specifications). В примере выше спецификатор преобразования %s используется для форматирования строки foo и включения ее в вывод команды. Еще один пример:
[me@linuxbox ~]$ printf “I formatted ‘%s’ as a string.\n” foo I formatted ‘foo’ as a string.
Как видите, в выводе команды спецификатор преобразования %s замещается стро- кой foo. Преобразование s используется для форматирования строковых данных. Существуют также другие спецификаторы для других видов данных. В табл. 21.4 перечислены наиболее часто используемые типы данных.
Таблица 21.4. Спецификаторы типов данных, наиболее часто используемых в команде printf
Спецификатор
Описание
d
f
o
s
x
X
%
Форматирует число как десятичное целое со знаком
Форматирует и выводит вещественное число
Форматирует целочисленное значение как восьмеричное число
Форматирует строку
Форматирует целочисленное значение как шестнадцатеричное чис- ло, с использованием букв a–f нижнего регистра, где это необходимо
То же, что и x, но использует буквы верхнего регистра
Выводит литеральный символ % (то есть сам спецификатор имеет вид: %%)
Инструменты простого форматирования
355
Продемонстрируем действие каждого спецификатора преобразования на примере строки 380:
[me@linuxbox ~]$ printf “%d, %f, %o, %s, %x, %X\n” 380 380 380 380 380 380 380, 380.000000, 574, 380, 17c, 17C
Так как в строке формата указано шесть спецификаторов формата, нам потребова- лось передать команде printf шесть аргументов. Шесть результатов показывают результат действия каждого спецификатора.
Для настройки вывода спецификатору формата можно передать несколько до- полнительных компонентов. Полный синтаксис спецификатора преобразования выглядит так:
%[флаги][ширина][.точность]спецификатор_преобразования
Для правильной интерпретации дополнительные компоненты, если их не- сколько, должны передаваться в указанном порядке. Все компоненты описаны в табл. 21.5.
Таблица 21.5. Компоненты спецификаторов преобразований в команде printf
Спецификатор
Описание
флаги
Существует пять разных флагов:
• # — использовать альтернативный формат вывода. Действует по-
разному, в зависимости от типа данных. Преобразование o (восьме- ричное число) добавляет в вывод префикс 0 (ноль). Преобразова- ния x и X (шестнадцатеричное число) добавляют в вывод префикс 0x или 0X соответственно;
• 0 (ноль) — добавляет нули в начало вывода. Это означает, что поле будет дополнено ведущими нулями, например: 000380; • - (дефис) — выравнивание по левому краю. По умолчанию
printf выполняет выравнивание по правому краю;
• ’ ’ (пробел) — добавляет ведущий пробел перед положительны-
ми числами;
• + (знак «плюс») — выводит знак перед положительными числа-
ми. По умолчанию printf выводит знаки только перед отрицатель- ными числами
ширина
.точность
Число, определяющее минимальную ширину поля вывода в символах
Определяет число знаков после десятичной запятой при выводе веще- ственных чисел. Для строковых значений точность определяет число выводимых символов
356
Глава 21. Форматирование вывода
В табл. 21.6 перечислены некоторые примеры применения разных форматов.
Таблица 21.6. Примеры применения спецификаторов преобразований команды printf
Аргумент
Формат
Результат
Примечание
380
380
“%d”
“%#x”
380
0x17c
380
“%05d”
00380
380
“%05.5f”
380.00000
380
380
380
“%010.5f”
0380.00000
“%+d”
+380
“%-d”
380
abcdefghijk
“%5s”
abcdefghijk
abcdefghijk
“%.5s”
abcde
Простое форматирование целых чисел
Форматирование целочисленных значений в шестнадцатеричное пред- ставление с использованием альтерна- тивного форматирования
Форматирование целочисленных значений с ведущими нулями и мини- мальным размером поля, равным пяти символам
Форматирование вещественных зна- чений с ведущими нулями и 5 знаками после запятой. Поскольку указанная минимальная ширина поля (5) меньше фактической ширины отформатиро- ванного числа, ведущие нули не были добавлены
Ширина поля вывода увеличена до 10, вследствие чего появился ведущий нуль
Флаг + требует выводить знак у по- ложительных чисел
Флаг - обеспечивает форматирование с выравниванием по левому краю
Форматирование строки в поле с ука- занной минимальной шириной
Применение компонента точности к строке привело к ее усечению
И еще раз: команда printf в основном используется в сценариях, где применяется для форматирования табличных данных, а не как самостоятельный инструмент командной строки. Тем не менее мы можем использовать ее для решения различ- ных задач форматирования. Во-первых, попробуем вывести несколько полей, раз- делив их символами табуляции:
[me@linuxbox ~]$ printf “%s\t%s\t%s\n” str1 str2 str3 str1 str2 str3
Системы форматирования документов
357
Добавив \t (экранированную последовательность, соответствующую символу та- буляции), мы достигли желаемого эффекта. Затем попробуем вывести несколько чисел в форматированном виде:
[me@linuxbox ~]$ printf “Line: %05d %15.3f Result: %+15d\n” 1071 3.14156295 32589 Line: 01071 3.142 Result: +32589
Здесь демонстрируется действие компонента, определяющего минимальную ширину поля. А можно ли подобным образом отформатировать небольшую веб- страницу?
[me@linuxbox ~]$ printf “\n\t\n\t\t%s\n\t \n\t\n\t\t
%s\n\t\n\n” “Page Title” “Page Content”
Page Title
<p>Page Content
Системы форматирования документов
До сих пор мы исследовали простые инструменты форматирования текста. Они хорошо подходят для решения небольших и простых задач, но как быть с более сложными заданиями? Одна из причин большой популярности операционной системы Unix среди технических специалистов и научных работников (кроме мощной поддержки многозадачности и многопользовательского окружения для выполнения любых работ, связанных с разработкой программного обеспечения) состоит в наличии инструментов, которые можно использовать для создания са- мых разных документов, таких как научные и академические публикации. Фак- тически, как описывается в документации GNU, средства подготовки документов положительно сказались на разработке Unix:
Первая версия Unix была создана на машине PDP-7, простаивавшей в Bell Labs. В 1971-м разработчики захотели заполучить PDP-11 для дальнейшей работы над операционной системой. Чтобы оправдать затраты на эту систему, они внесли предложение о реализации системы форматирования документов для патентного бюро в AT&T. Эта первая программа форматирования являлась переделкой про- граммы roff Макиллроя (McIllroy), которую написал Д. Ф. Оссанна (J. F. Ossanna).
В области систем форматирования документов доминируют два основных се- мейства программ: уходящие корнями в оригинальную программу roff, включая
358
Глава 21. Форматирование вывода
nroff и troff, и основанные на системе верстки Дональда Кнута TEX (произно- сится как «тек»). И да, буква «E» в середине имени действительно смещена вниз.
Имя roff произошло от словосочетания «run off» (напечатать), как во фразе: «Я напечатал копию для вас». Программа nroff используется для форматиро- вания документов перед выводом на устройства, использующие моноширинные шрифты, такие как алфавитно-цифровые терминалы и принтеры, действующие подобно пишущим машинкам. На момент появления программы такие устрой- ства составляли подавляющее большинство устройств вывода, подключаемых к компьютерам. Позднее появилась программа troff, форматирующая доку- менты для вывода на наборные устройства, используемые для производства «готовых к тиражированию» макетов. Большинство современных принтеров способны имитировать вывод таких наборных устройств. Семейство roff так- же включает ряд других программ для подготовки фрагментов документов. К их числу относятся eqn (для форматирования математических формул) и tbl (для форматирования таблиц).
Система TEX (в версии, готовой к эксплуатации) впервые появилась в 1989 году и, до определенной степени, заменила troff как инструмент для получения доку- ментов типографского качества. Мы не будем рассматривать систему TEX здесь, во-первых, из-за ее сложности (существуют целые книги, посвященные ей) и, во- вторых, из-за того, что в большинстве современных систем Linux она не устанав- ливается по умолчанию.
СОВЕТ
Если у вас появится желание установить TEX, обратите внимание на пакет texlive, присутствующий в репозиториях большинства дистрибутивов, а также на редактор графического содержимого LyX.
groff
groff — это пакет программ с GNU-реализацией troff. Он также включает сцена- рий, имитирующий работу nroff, и остальные программы семейства roff.
Семейство roff и его потомки использовались для создания форматированных документов способом, довольно чуждым современным пользователям. Большин- ство документов в наше время создается в текстовых процессорах, способных осу- ществлять составление и оформление документов в один шаг. До появления гра- фических текстовых процессоров создание документов обычно происходило в два этапа. Сначала в текстовом редакторе выполнялось составление документа, а за- тем с помощью процессора, такого как troff, осуществлялось его форматирование.
Системы форматирования документов
359
Инструкции для программы форматирования встраивались в текст документа с применением языка разметки. Современным аналогом этого процесса может служить подготовка веб-страниц, которые записываются в текстовом редакторе и затем отображаются веб-браузером, интерпретирующим код HTML как ин- струкции языка разметки, описывающие окончательный вид страницы.
Мы не будем рассматривать семейство программ groff во всех подробностях, так как многие тонкости его языка разметки имеют прямое отношение к туманным для нас деталям типографского дела. Вместо этого мы сосредоточимся на одном из его макропакетов, широко использующемся до сих пор. Эти макропакеты упа- ковывают множество низкоуровневых команд в небольшое множество высоко- уровневых команд, существенно упрощающих работу с groff.
Давайте ненадолго приостановимся и рассмотрим простую страницу справочно- го руководства (man). Она хранится в каталоге /usr/share/man в виде текстового файла, сжатого с помощью gzip. Если заглянуть на распакованное содержимое, можно увидеть следующее (здесь показана страница справочного руководства из раздела 1 для команды ls):
[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | head
." DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.3. .TH LS “1” “January 2018” “GNU coreutils 8.28” “User Commands” .SH NAME ls - list directory contents .SH SYNOPSIS .B ls [\fI,OPTION/\fR]… [\fI,FILE/\fR]… .SH DESCRIPTION ." Add any additional description here .PP
Если сравнить содержимое страницы с ее нормальным представлением на экране, можно заметить определенную корреляцию между языком разметки и результа- том его интерпретации:
[me@linuxbox ~]$ man ls | head LS(1) User Commands LS(1)
NAME ls - list directory contents
SYNOPSIS ls [OPTION]… [FILE]…
360
Глава 21. Форматирование вывода
Этот пример интересен тем, что страницы справочного руководства отобража- ются программой groff с использованием макропакета mandoc. В действитель- ности работу команды man можно сымитировать с помощью следующего кон- вейера.
[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc -T ascii | head LS(1) User Commands LS(1)
NAME ls - list directory contents
SYNOPSIS ls [OPTION]… [FILE]…
Здесь использована программа groff с множеством параметров, определяющих макропакет mandoc и драйвер вывода для ASCII. groff может выводить информа- цию в нескольких форматах. Если формат не задан, по умолчанию вывод произ- водится в формате PostScript:
[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc | head %!PS-Adobe-3.0 %%Creator: groff version 1.18.1 %%CreationDate: Thu Feb 2 13:44:37 2009 %%DocumentNeededResources: font Times-Roman %%+ font Times-Bold %%+ font Times-Italic %%DocumentSuppliedResources: procset grops 1.18 1 %%Pages: 4 %%PageOrder: Ascend %%Orientation: Portrait
Мы упоминали PostScript вскользь в предыдущей главе и снова вернемся к нему в следующей главе. PostScript — это язык описания страниц, используемый для вывода страниц на устройства печати с типографским качеством. Вывод коман- ды можно сохранить в файл (здесь предполагается, что вы работаете в графиче- ском окружении рабочего стола и в вашем домашнем каталоге имеется каталог Desktop):
[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | groff -mandoc > ~/Desktop/foo.ps
После выполнения этой команды на рабочем столе появится значок файла. После двойного щелчка мышью на этом значке должен запуститься инструмент просмо- тра страниц и отобразить содержимое файла (рис. 21.1).
Системы форматирования документов
361
Рис. 21.1. Отображение содержимого PostScript-файла в окне обозревателя страниц в GNOME
Здесь мы видим прекрасно отформатированную страницу справочного руковод- ства для команды ls! В действительности PostScript-файл можно преобразовать в формат PDF (Portable Document Format — формат переносимых документов), как показано ниже:
[me@linuxbox ~]$ ps2pdf ~/Desktop/foo.ps ~/Desktop/ls.pdf
Программа ps2pdf входит в состав пакета ghostscript, который устанавливается в большинстве систем Linux для поддержки печати.
СОВЕТ
Системы Linux включают множество программ командной строки для преоб- разования файлов из одного формата в другой. Они традиционно получают на- звания в соответствии с соглашением: формат2формат. Попробуйте выполнить команду ls /usr/bin/[[:alpha:]]2[[:alpha:]], чтобы найти все такие про- граммы. Также попробуйте поискать программы с именами форматtoформат.
362
Глава 21. Форматирование вывода
В последнем упражнении с программой groff мы вновь вернемся к нашему старо- му доброму другу — файлу distros.txt. На этот раз мы воспользуемся программой tbl, которая применяется для форматирования таблиц, чтобы сформировать наш список дистрибутивов Linux. В этом упражнении мы задействуем сценарий для sed, созданный ранее, и с его помощью добавим разметку в поток текста, который затем передадим программе groff.
Сначала изменим сценарий для sed, добавив в него вызовы, необходимые про- грамме tbl. Откройте сценарий distros.sed в текстовом редакторе и измените его, как показано ниже:
Сценарий для sed, создающий отчет о дистрибутивах Linux #
1 i
.TS
center box;
cb s s
cb cb cb
l n c.
Linux Distributions Report
=
Name Version Released
_
s/([0-9]{2})/([0-9]{2})/([0-9]{4})$/\3-\1-\2/
$ a
.TE
Обратите внимание, что для корректной работы сценария слова Name Version Released должны разделяться символами табуляции, а не пробелами. Сохраним получившийся файл с именем distros-tbl.sed. Программа tbl использует запросы .TS и .TE как метки начала и конца таблицы. Строки, следующие за запросом .TS, определяют глобальные свойства таблицы, в нашем случае выравнивание по цен- тру страницы и окружение внешней рамкой. Остальные строки описывают фор- матирование строк таблицы. Если теперь снова запустить наш конвейер составле- ния отчета с новым сценарием для sed, мы получим следующее:
[me@linuxbox ~]$ sort -k 1,1 -k 2n distros.txt | sed -f distros-tbl.sed | groff -t -T ascii +——————————+ | Linux Distributions Report | +——————————+ | Name Version Released | +——————————+ |Fedora 5 2006-03-20 | |Fedora 6 2006-10-24 |
Системы форматирования документов
363
|Fedora 7 2007-05-31 |
|Fedora 8 2007-11-08 |
|Fedora 9 2008-05-13 |
|Fedora 10 2008-11-25 |
|SUSE 10.1 2006-05-11 |
|SUSE 10.2 2006-12-07 |
|SUSE 10.3 2007-10-04 |
|SUSE 11.0 2008-06-19 |
|Ubuntu 6.06 2006-06-01 |
|Ubuntu 6.10 2006-10-26 |
|Ubuntu 7.04 2007-04-19 |
|Ubuntu 7.10 2007-10-18 |
|Ubuntu 8.04 2008-04-24 |
|Ubuntu 8.10 2008-10-30 |
+------------------------------+
Параметр -t требует от groff обработать поток текста с помощью tbl. Аналогич- но, параметр -T требует вывести данные в формате ASCII вместо используемого по умолчанию PostScript.
Это лучший формат вывода для тех, кто ограничен возможностями терминала или алфавитно-цифрового печатающего устройства. Если определить формат вы- вода PostScript и открыть результат в графическом обозревателе, мы получим на- много более удовлетворительную картинку (рис. 21.2).
[me@linuxbox ~]$ sort -k 1,1 -k 2n distros.txt | sed -f distros-tbl.sed | groff -t
~/Desktop/foo.ps
Рис. 21.2. Изображение законченной таблицы
364
Глава 21. Форматирование вывода
Заключение
Учитывая, что текст занимает главенствующее положение в Unix-подобных опе- рационных системах, было бы оправданно ожидать наличия большого числа ин- струментов для работы с текстом и его форматирования. И, как мы увидели, эти ожидания небеспочвенны! Простые инструменты форматирования, такие как fmt и pr, находят широкое применение в сценариях, производящих короткие доку- менты, тогда как groff (с сопутствующими программами) можно использовать для создания книг. Возможно, вам никогда не придется использовать инструмен- ты командной строки для написания технических статей (хотя многие делают это!), но знать о такой возможности вам не помешает.
22 Печать
После знакомства с приемами манипулирования текстом в двух предыдущих гла- вах пришло время вывести его на бумагу. В этой главе мы рассмотрим инстру- менты командной строки, используемые для печати файлов и управления работой принтера. Мы не будем касаться вопросов настройки принтера, так как в разных дистрибутивах она осуществляется по-разному и обычно происходит автомати- чески в ходе установки. Для выполнения упражнений из этой главы вам понадо- бится настроенный и действующий принтер.
Далее мы обсудим следующие команды:
pr — преобразует текстовые файлы для печати;
lpr — печатает файлы;
a2ps — форматирует файлы для печати на принтере с поддержкой PostScript;
lpstat — выводит информацию о состоянии принтера;
lpq — выводит информацию о состоянии очереди печати;
lprm — отменяет задания печати.
Краткая история поддержки печати
Для полного понимания особенностей печати в Unix-подобных операционных си- стемах сначала познакомимся с ее историей. Поддержка печати в Unix-подобных системах восходит к временам зарождения самой операционной системы. В те времена принтеры и способы их использования существенно отличались от со- временных.
366
Глава 22. Печать
Печать в ночное время
Подобно компьютерам, принтеры в эпоху, предшествующую появлению персо- нальных компьютеров, были большими, дорогими и централизованными. Ти- пичный пользователь 1980-х работал за терминалом на порядочном удалении от ЭВМ. Принтер же размещался рядом с ЭВМ и находился под неусыпным наблю- дением операторов.
Когда принтеры были дорогими и централизованными, что было обычно в ранний период развития Unix, нормальной практикой считалось совместное использова- ние принтера несколькими пользователями. Чтобы идентифицировать принад- лежность задания печати определенному пользователю, в начале каждого задания печаталась титульная страница с именем пользователя. Персонал, обслуживаю- щий ЭВМ, извлекал корзину с напечатанными за день документами и доставлял их конкретным пользователям.
Символьные принтеры
Технология печати в 80-х существенно отличалась от современной двумя аспекта- ми. Во-первых, практически все принтеры того периода были ударного действия. В этих принтерах использовался механический узел, ударявший по красящей ленте, находящейся между узлом и бумагой и таким способом формировавший отпечатки символов на бумаге. В то время были наиболее распространены два вида печатающих механизмов: лепестковый литероноситель («ромашка») и мат- ричная печатающая головка.
Во-вторых, что особенно важно, ранние принтеры использовали фиксирован- ный набор символов, встроенный непосредственно в устройство. Например, принтер с «ромашкой» мог печатать только символы, присутствующие на ле- пестках «ромашки». В результате такие принтеры действовали как высокоско- ростные печатающие машинки. Как большинство печатающих машинок, они печатали моноширинными шрифтами (шрифтами с фиксированной шириной символов). Это означает, что все символы имели одинаковую ширину. Печать символов выполнялась в фиксированных позициях на бумаге, и область печа- ти имела фиксированное число знакомест. Большинство принтеров выводило 10 символов на дюйм (Characters Per Inch, CPI) по горизонтали и 6 строк на дюйм (Lines Per Inch, LPI) по вертикали. В соответствии с этой схемой на стра- нице формата US-letter помещалось 85 символов в ширину и 66 строк в высоту. С учетом отступов слева и справа считалось, что максимальная ширина печати составляет 80 символов в строке. Это объясняет, почему дисплеи терминалов (и наших эмуляторов терминалов) обычно имеют ширину 80 символов. Таким способом обеспечивался режим «что вижу, то и получаю» (What You See Is What
Краткая история поддержки печати
367
You Get, WYSIWYG) отображения текста перед печатью с применением моно- ширинного шрифта.
Данные, посылаемые на принтер с ударным механизмом, — это простой поток байтов, соответствующих печатаемым символам. Например, чтобы напечатать a, посылался код символа 97 в наборе ASCII. Кроме того, в начале набора ASCII были выделены коды для управления кареткой принтера и бумагой, например, для возврата каретки в исходное положение, перевода строки, перевода формата (страницы) и др. С помощью управляющих кодов можно было имитировать неко- торые эффекты, такие как жирная печать, когда принтеру посылался печатаемый символ, символ забоя (backspace) и снова тот же печатаемый символ, благодаря чему на бумаге получалось более темное изображение. В этом легко убедиться, если с помощью nroff отобразить страницу справочного руководства и исследо- вать вывод с помощью команды cat -A:
[me@linuxbox ~]$ zcat /usr/share/man/man1/ls.1.gz | nroff -man | cat -A | head LS(1) User Commands LS(1) $ $ $ N^HNA^HAM^HME^HE$ ls - list directory contents$ $ S^HSY^HYN^HNO^HOP^HPS^HSI^HIS^HS$ l^Hls^Hs [^HO^HP_^HT_^HI_^HO_^HN]… [^HF^HI_^HL_^HE]…$
Символы ^H (CTRL-H) — это символы забоя (backspace), используемые для созда- ния эффекта жирной печати. Аналогично для получения эффекта подчеркивания можно использовать последовательность из символов забоя/подчеркивания.
Графические принтеры
Создание графического интерфейса пользователя (GUI) привело к существен- ным изменениям в технологии печати. Так же как компьютеры все больше смеща- лись в сторону использования графических дисплеев, печать все дальше уходила от символьных технологий вывода к графическим. Эта задача упростилась с появ- лением недорогих лазерных принтеров, которые вместо фиксированного набора символов могли осуществлять печать маленькими точками в любом месте обла- сти печати на странице. Это позволило использовать для печати пропорциональ- ные шрифты (подобные тем, что применяются в книгопечатании) и даже печатать фотографии и высококачественные диаграммы.
Однако переход от символьной системы печати к графической вызвал появле- ние огромных технических проблем. И вот почему. Число байтов, которое нужно
368
Глава 22. Печать
послать символьному принтеру для заполнения страницы, можно было подсчи- тать с помощью простой формулы (если предположить, что на странице умещает- ся 60 строк, по 80 символов в каждой):
60 × 80 = 4800 байт.
Для сравнения: лазерному принтеру с качеством печати 300 точек на дюйм (Dot Per Inch, DPI) для покрытия страницы размером 8 × 10 дюймов (203 × 254 мм) нужно послать
(8 × 300) × (10 × 300) × 8 = 900 000 байт.
Многие медленные сети персональных компьютеров просто не могли достаточно быстро пропустить почти 1 мегабайт данных, чтобы напечатать на лазерном прин- тере полную страницу, поэтому требовалось какое-то новое решение.
Таким решением стало изобретение языка описания страниц. Язык описания стра- ниц (Page Description Language, PDL) — это язык программирования, описываю- щий содержимое страницы. Программы на этом языке как бы говорили: «перей- ти в эту позицию, нарисовать символ a шрифтом Helvetica с кеглем 10 пунктов, перейти в эту позицию…», пока вся страница не была описана. Первым основным языком PDL стал PostScript, разработанный в Adobe Systems, он все еще широко используется в наше время. Язык PostScript — это полноценный язык программи- рования, ориентированный на книгопечатание и создание разного вида графиче- ских изображений. Он включает поддержку 35 стандартных высококачественных шрифтов плюс может принимать определения дополнительных шрифтов во вре- мя выполнения. На первом этапе поддержка PostScript встраивалась непосред- ственно в принтеры. Это решало проблему передачи данных. Даже притом, что типичная программа на PostScript по объему превышала простой поток байтов для символьных принтеров, ее размер был намного меньше числа байтов, необхо- димых для представления целой страницы.
Принтер с поддержкой PostScript принимал на входе программу на PostScript. Принтер имел собственный процессор и память (нередко принтеры имели боль- шую вычислительную мощность, чем компьютеры, к которым они подключались) и выполнял специальную программу, называвшуюся интерпретатором PostScript, которая читала входящую программу на PostScript и отображала результат во внутреннюю память принтера, таким образом формируя шаблон из битов (точек) для вывода на бумагу. Такой процесс отображения чего-то в большой битовый ша- блон (его называют bitmap — растр) в общем случае называют процессором рас- тровых изображений (Raster Image Processor, RIP).
Спустя годы компьютеры и сети стали намного быстрее. Это позволило переме- стить RIP с принтера в компьютер, что, в свою очередь, позволило удешевить вы- сококачественные принтеры.
Подготовка файлов к печати
369
Многие современные принтеры все еще способны принимать потоки символов, но большинство уже не поддерживают эту возможность. Они полагаются на RIP компьютера и ожидают получить поток битов для печати их в виде точек. Суще- ствуют также современные PostScript-принтеры.
Печать в Linux
Современные системы Linux используют два комплекта программного обеспече- ния для печати. Первый, CUPS (Common Unix Printing System — общая система печати для Unix), включает драйверы печати и средства управления заданиями; второй, Ghostscript, интерпретатор PostScript, действует как RIP.
Пакет CUPS осуществляет управление принтерами, создавая очереди печа- ти и управляя ими. Как говорилось выше, в краткой исторической справке, поддержка печати в Unix первоначально была ориентирована на управление централизованным принтером, совместно используемым несколькими поль- зователями. Поскольку принтеры — довольно медленные устройства по своей природе в сравнении с компьютерами, которые поставляют им данные, систе- мы печати должны обладать возможностью планирования множества заданий печати и их организации. Система CUPS также способна распознавать данные разных типов (в разумных пределах) и преобразовывать файлы в вид, пригод- ный для печати.
Подготовка файлов к печати
Так как мы — пользователи командной строки, наибольший интерес для нас пред- ставляет печать текста, хотя при этом сохраняется возможность печатать данные других форматов.
pr — преобразование текстовых файлов для печати
Мы уже касались программы pr в предыдущей главе. А теперь исследуем все бо- гатство ее параметров, используемых при печати. В краткой исторической справке развития технологий печати рассказывалось, что символьные принтеры исполь- зовали мноноширинные шрифты, что обеспечивало фиксированное число симво- лов в строке и строк на странице. Программа pr используется для выравнивания текста в соответствии с заданным размером страницы, с учетом дополнительных заголовков и полей на странице. Наиболее часто используемые параметры пере- числены в табл. 22.1.
370
Глава 22. Печать
Таблица 22.1. Наиболее часто используемые параметры команды pr
Параметр
Описание
+первая[:последняя]
Вывести диапазон страниц, начиная со страницы с номером первая и заканчивая страницей с номером последняя (если указано)
-колонок
Вывести содержимое страницы в указанное число колонок
-a
-d
-D формат
-f
-h заголовок
-l длина
-n
-o отступ
-w ширина
По умолчанию, когда задан вывод в несколько колонок, ко- лонки организованы по вертикали. Параметр -a (across — по- перек) позволяет организовать колонки по горизонтали
Вывести с двойным интервалом
Формат вывода даты в заголовке страницы. Описание строки формата можно найти в странице справочного руководства (man) для команды date
Использовать символ перевода формата вместо возврата ка- ретки для отделения страниц друг от друга
Текст для вывода в центре заголовка страницы вместо имени файла
Длина страницы. По умолчанию длина устанавливается равной 66 строкам (соответствует формату US-letter с плотностью печати 6 строк на дюйм)
Нумеровать строки
Создать левое поле, выполнив отступ указанного размера (в символах)
Ширина страницы в символах. По умолчанию ширина устанав- ливается равной 72 символам
Программа pr часто используется в конвейерах в роли фильтра. Следующий при- мер создает список содержимого каталога /usr/bin и с помощью pr выводит его в три колонки с разбивкой на страницы:
[me@linuxbox ~]$ ls /usr/bin | pr -3 -w 65 | head
2012-02-18 14:00 Page 1 [ apturl bsd-write 411toppm ar bsh a2p arecord btcflash
Отправка задания печати на принтер
371
a2ps arecordmidi bug-buddy a2ps-lpr-wrapper ark buildhash
Отправка задания печати на принтер
Пакет программ печати CUPS поддерживает два метода печати, исторически ис- пользуемых в Unix-подобных системах. Первый метод, с названием Berkeley, или LPD (используется в BSD-версиях Unix), основан на использовании программы lpr; второй метод, с названием SysV (используется в версиях Unix System V), ос- нован на использовании программы lp. Обе программы работают примерно оди- наково. Выбор той или иной зависит от личных предпочтений.
lpr — печать файлов (в стиле Berkeley)
Программа lpr применяется для отправки файлов на принтер. Она также может использоваться в конвейерах, так как способна принимать исходные данные со стандартного ввода. Например, напечатать предыдущий результат форматирова- ния содержимого каталога в несколько колонок можно было бы так:
[me@linuxbox ~]$ ls /usr/bin | pr -3 | lpr
В этом случае отчет будет выведен на принтер, используемый системой по умол- чанию. Для вывода файла на другой принтер используйте параметр -P:
lpr -P имя_принтера
где аргумент имя_принтера — имя требуемого принтера. Получить список принте- ров, известных системе, поможет команда
[me@linuxbox ~]$ lpstat -a
СОВЕТ
Многие дистрибутивы Linux позволяют определять фиктивные «принтеры», которые выводят результат в файл в формате PDF, а не на физический прин- тер. Это очень удобно для экспериментов с командами печати. Запустите свою программу настройки принтеров и посмотрите, поддерживает ли она такую воз- можность. Чтобы включить ее, в некоторых дистрибутивах может понадобиться установить дополнительные пакеты (такие, как cups-pdf).
В табл. 22.2 перечислены некоторые часто используемые параметры команды lpr.
372
Глава 22. Печать
Таблица 22.2. Наиболее часто используемые параметры команды lpr
Параметр
Описание
-# число
Число копий
-p
Вывести на каждой странице заголовок с датой, временем, именем за- дания и номером страницы. Этот так называемый параметр структурной печати («pretty print») можно использовать для печати текстовых файлов
-P принтер
Имя принтера для вывода. Если принтер не указан, используется систем- ный принтер по умолчанию
-r
Удалить файлы после печати. Может пригодиться при использовании про- грамм, создающих временные файлы для печати
lp — печать файлов (в стиле System V)
Подобно lpr, программа lp принимает файлы или данные со стандартного ввода. Отличается от lpr поддержкой иного (немного более сложного) набора параме- тров. Наиболее часто используемые параметры перечислены в табл. 22.3.
Таблица 22.3. Наиболее часто используемые параметры команды lp
Параметр
-d принтер
Описание
Имя принтера для вывода. Если принтер не указан, использу- ется системный принтер по умолчанию
-n число
Число копий
-o landscape
Вывести в альбомной ориентации
-o fitplot
-o scaling=число
-o cpi=число
-o lpi=число
Масштабировать файл, чтобы уместить на странице. Этот параметр может пригодиться при печати изображений, на- пример, файлов в формате JPEG
Масштабировать файл с указанным коэффициентом. Зна- чение 100 соответствует полному заполнению страницы. Значения меньше 100 уменьшают размеры области печати, тогда как значения больше 100 — увеличивают, вследствие чего файл печатается на нескольких страницах
Установить плотность печати символов на дюйм (CPI) как указанное число символов на дюйм. По умолчанию это значе- ние устанавливается равным 10
Установить плотность печати строк на дюйм (LPI) как ука- занное число строк на дюйм. По умолчанию это значение устанавливается равным 6
Отправка задания печати на принтер
373
Параметр
Описание
-o page-bottom=пунктов -o page-left=пунктов -o page-right=пунктов -o page-top=пунктов
-P страницы
Размеры полей. Значения выражаются в пунктах, единице измерения, используемой в типографском деле. Один дюйм соответствует 72 пунктам
Список страниц для печати. Список может иметь вид пере- числения номеров страниц через запятую и/или диапазонов, например: 1,3,5,7-10
Вернемся к нашему списку содержимого каталога, но на этот раз выведем его с плотностью печати 12 CPI и 8 LPI и с левым полем размером полдюйма. Обра- тите внимание, что нам пришлось откорректировать параметры pr, чтобы учесть новые размеры страницы:
[me@linuxbox ~]$ ls /usr/bin | pr -4 -w 90 -l 88 | lp -o page-left=36 -o cpi=12 -o lpi=8
Этот конвейер выводит список в четыре колонки с меньшим размером шрифта, чем принято по умолчанию. Увеличение плотности символов на дюйм позволило уместить больше колонок на странице.
Еще одна возможность: a2ps
Программа a2ps довольно интересна. Как можно догадаться по ее имени, это про- грамма преобразования одного формата в другой, но не только. Первоначально ее имя означало ASCII to PostScript (из ASCII в PostScript) и она использовалась для подготовки текстовых файлов к печати на принтерах с поддержкой PostScript. С го- дами, однако, возможности программы росли, и теперь ее имя означает Anything to PostScript (все что угодно — в PostScript). Несмотря на то что имя программы гово- рит, что это — программа преобразования одного формата в другой, в действительно- сти она является программой печати. Она выводит результаты своей работы в свой вывод по умолчанию — в системный принтер, а не в стандартный вывод. По умол- чанию программа действует как программа «структурной печати», улучшая формат вывода. Мы можем с ее помощью создать PostScript-файл на своем рабочем столе:
[me@linuxbox ~]$ ls /usr/bin | pr -3 -t | a2ps -o ~/Desktop/ls.ps -L 66 [stdin (plain): 11 pages on 6 sheets] [Total: 11 pages on 6 sheets] saved into the file `/home/me/Desktop/ls.ps’
Здесь мы обработали поток с помощью программы pr, передав ей параметр -t (что- бы опустить верхние и нижние колонтитулы), и передали результат программе
374
Глава 22. Печать
a2ps, указав ей файл для вывода (параметр -o) и плотность печати 66 строк на странице (параметр -L), чтобы разбить вывод pr на страницы. Если открыть полу- чившийся файл с помощью соответствующего средства просмотра, можно уви- деть, что он выглядит, как показано на рис. 22.1.
Рис. 22.1. Результат работы a2ps
Как видите, по умолчанию используется формат вывода «две страницы рядом». В этом формате содержимое двух страниц будет напечатано на одном листе бума- ги. a2ps добавляет также свои хорошо отформатированные колонтитулы.
a2ps имеет множество параметров, они перечислены в табл. 22.4.
Таблица 22.4. Параметры команды a2ps
Параметр
Описание
–center-title текст
Текст для заголовка страницы в центре
–columns число
Число колонок для вывода страниц. По умолчанию 2
–footer текст
Текст для нижнего колонтитула
–guess
Вывести типы файлов, переданных программе в аргумен- тах. Поскольку программа a2ps пытается преобразовы- вать и форматировать данные любых типов, этот пара- метр может пригодиться, чтобы понять, что a2ps будет делать с данным конкретным файлом
Отправка задания печати на принтер
375
Параметр
Описание
–left-footer текст
Текст для нижнего колонтитула слева
–left-title текст
Текст для заголовка страницы слева
–line-numbers=интервал
Выводить номера строк через заданный интервал
–list=defaults
Вывести настройки по умолчанию
–pages диапазон
Печатать страницы из указанного диапазона
–right-footer текст
Текст для нижнего колонтитула справа
–right-title текст
Текст для заголовка страницы справа
–rows число
Разместить страницы в указанное число рядов. По умол- чанию 1
-B
-b текст
-f размер
-l число
-L число
-M имя
-n число
-o файл
-P принтер
-R
-r
-T число
-u текст
Не выводить заголовки страниц
Текст заголовка страницы
Использовать шрифт указанного размера
Число символов в строке. Этот параметр и параметр -L (ниже) можно использовать, чтобы правильно разместить на странице файлы, разбитые на страницы с помощью других программ, таких как pr
Число строк на странице
Имя формата бумаги, например A4
Вывести указанное число копий каждой страницы
Вывести результат в указанный файл. Если в качестве имени файла указан - (дефис), используется стандартный вывод
Имя принтера для вывода. Если принтер не указан, ис- пользуется системный принтер по умолчанию
Вывести в книжной ориентации
Вывести в альбомной ориентации
Установить табулостопы через каждое указанное число символов
Текст для нижнего слоя на странице («водяной знак»)
Это далеко не полный список. В действительности программа a2ps имеет намного больше параметров.
376
Глава 22. Печать
ПРИМЕЧАНИЕ
Кроме того, существует еще одна программа форматирования, которую можно использовать для преобразования текста в формат PostScript. Она называется enscript и способна выполнять почти те же виды форматирования и печати, что и a2ps, но, в отличие от последней, принимает только текстовые данные.
Наблюдение за заданиями печати и управление ими
Поскольку система печати в Unix изначально проектировалась для обработки заданий печати от нескольких пользователей, соответственно и система CUPS проектировалась исходя из той же предпосылки. Для каждого принтера созда- ется своя очередь печати, в которой задания хранятся, пока не будут переданы принтеру. В составе CUPS имеется несколько программ командной строки для управления состоянием принтеров и очередей печати. Подобно программам lpr и lp, эти управляющие программы создавались после появления соответствую- щих программ из систем Berkeley и System V.
lpstat — вывод информации о состоянии принтера
Программу lpstat удобно использовать для определения имен и доступности принтеров в системе. Например, если к системе подключены два принтера — фи- зический (с именем printer) и виртуальный, для вывода в файлы PDF (с именем PDF), — их состояние можно проверить так:
[me@linuxbox ~]$ lpstat -a PDF accepting requests since Mon 05 Dec 2011 03:05:59 PM EST printer accepting requests since Tue 21 Feb 2012 08:43:22 AM EST
Кроме того, с ее помощью можно получить более подробное описание конфигура- ции системы печати:
[me@linuxbox ~]$ lpstat -s system default destination: printer device for PDF: cups-pdf:/ device for printer: ipp://print-server:631/printers/printer
В этом примере видно, что имя printer соответствует системному принтеру по умол- чанию и что это сетевой принтер, для взаимодействий с которым используется
Наблюдение за заданиями печати и управление ими
377
протокол печати через Интернет (Internet Printing Protocol, ipp://), физически подключенный к системе с именем print-server.
В табл. 22.5 перечислены некоторые часто используемые параметры команды lpstat.
Таблица 22.5. Наиболее часто используемые параметры команды lpstat
Параметр
Описание
-a [принтер…]
Вывести состояние очереди печати для указанного принтера. Обра- тите внимание, что результат отражает состояние очереди печати для данного принтера и ее способность принимать задания, а не со- стояние физического принтера. Если принтер не указан, выводится информация обо всех очередях печати
-d
Вывести имя системного принтера по умолчанию
-p [принтер…]
Вывести состояние указанного принтера. Если принтер не указан, выводится информация обо всех принтерах
-r
-s
-t
Вывести состояние сервера печати
Вывести сводную информацию о состоянии
Вывести полный отчет о состоянии
lpq — вывод информации о состоянии очереди печати
Программа lpq используется для получения информации о состоянии очереди печати. С ее помощью можно увидеть состояние очереди и список заданий в ней. Ниже приводится пример вывода информации о состоянии пустой очереди для системного принтера по умолчанию с именем printer:
[me@linuxbox ~]$ lpq printer is ready no entries
Если принтер не указан (с помощью параметра -P), выводится информация об очереди для системного принтера по умолчанию. Если сформировать задание для печати и затем вывести информацию о состоянии очереди, это задание появится в списке:
[me@linuxbox ~]$ ls *.txt | pr -3 | lp request id is printer-603 (1 file(s)) [me@linuxbox ~]$ lpq
378
Глава 22. Печать
printer is ready and printing Rank Owner Job File(s) Total Size active me 603 (stdin) 1024 bytes
lprm и cancel — отмена заданий печати
В составе CUPS имеется две программы для завершения заданий печати и удале- ния их из очереди. Одна программа — в стиле Berkeley (lprm), а другая — в стиле System V (cancel). Они несколько отличаются поддерживаемыми параметрами, но, по сути, выполняют одну и ту же операцию. Если использовать пример с за- данием печати, рассматриваемый выше, мы могли бы остановить выполнение за- дания и удалить его:
[me@linuxbox ~]$ cancel 603 [me@linuxbox ~]$ lpq printer is ready no entries
Обе команды имеют параметры, позволяющие удалить все задания, принадлежа- щие определенному пользователю задания, предназначенные для печати на опре- деленном принтере, а также задания, содержащиеся в указанном списке номеров заданий. Все необходимые подробности вы найдете на страницах справочного ру- ководства (man) для этих команд.
Заключение
В этой главе мы увидели, как в прошлом принтеры влияли на дизайн систем пе- чати в Unix-подобных системах. Мы также выяснили, сколько имеется программ командной строки, помогающих не только планировать и выполнять задания пе- чати, но также управлять различными параметрами вывода.
23 Компиляция программ
В этой главе мы посмотрим, как собирать программы, компилируя их исходный код. Доступность исходного кода — основное преимущество Linux, оно обеспечи- вает само существование этой системы. Вся экосистема разработки в Linux опира- ется на свободный обмен информацией между разработчиками. Для многих рядо- вых пользователей компиляция — утраченное искусство. Когда-то эта процедура была вполне обыденным делом, но в настоящее время создатели дистрибутивов поддерживают огромные репозитории с предварительно скомпилированными файлами, готовыми для загрузки и использования. На момент написания этих строк в репозитории дистрибутива Debian (одном из крупнейших) насчитывалось почти 68 000 пакетов.
Но зачем может понадобиться компилировать исходный код? Могу назвать две основные причины:
Доступность. Несмотря на большое число предварительно скомпилированных пакетов в репозиториях дистрибутивов некоторые дистрибутивы могут вклю- чать не все необходимые приложения. В этом случае остается только один спо- соб установить требуемую программу: скомпилировать ее из исходных кодов.
Своевременность. Даже притом, что некоторые дистрибутивы специализиру- ются на ультрасовременных версиях программ, многие все же немного отстают от прогресса. Это означает, что для получения самой последней версии про- граммы придется ее скомпилировать.
Компиляция программ из исходных кодов может оказаться весьма специфиче- ским и технически сложным делом, непосильным для некоторых пользователей. Однако многие программы компилируются относительно легко и просто, всего в несколько шагов. Все зависит от пакета. Далее мы рассмотрим очень простой случай, чтобы получить общее понимание процесса и начальные знания, оттал- киваясь от которых желающие смогут продолжить исследования самостоятельно.
380
Глава 23. Компиляция программ
В этой главе будет представлена одна новая команда:
make — утилита сопровождения программ.
Что такое компиляция?
Выражаясь простым языком, компиляция — это процесс трансляции исходного кода (текста программы, описывающего ее действия и написанного программи- стом) на низкоуровневый язык, понятный процессору компьютера.
Процессор компьютера (Computer Processor Unit, CPU) работает на очень низ- ком уровне, выполняя программы на языке, который называют машинным. Это числовой код, описывающий элементарные операции, такие как «сложить эти два байта», «сослаться на эту ячейку в памяти» или «скопировать этот байт». Каждая из этих инструкций выражается в двоичной форме (нулями и единицами). Самые первые программы писались на числовом коде, поэтому программисты, писавшие такой код, как поговаривают, много курили, пили кофе литрами и носили очки с толстенными линзами.
Эта проблема была решена с появлением языка ассемблера, который заменил чис- ловые коды (слегка) более простыми символическими мнемониками, такими как CPY (для обозначения операции копирования) и MOV (для обозначения опера- ции перемещения). Исходный код на языке ассемблера преобразовывался в ма- шинный код программой, называющейся ассемблером. Язык ассемблера исполь- зуется и в наши дни для решения специальных задач программирования, таких как разработка драйверов устройств или встраиваемых систем.
Затем появились высокоуровневые языки программирования. Они называются так потому, что позволяют программисту меньше думать об особенностях работы про- цессора и больше — о решении задачи, стоящей перед ним. К числу этих первых языков (разработанных в течение 1950-х) относятся: FORTRAN (создавался для решения научных и технических задач) и СOBOL (для решения экономических задач). Оба продолжают ограниченно использоваться и по сию пору.
Несмотря на большое число популярных языков программирования, господству- ющие позиции занимают только два из них. Многие современные системы и про- граммы написаны на C или на C++. В примерах ниже мы будем компилировать программы на языке C.
Программы на языках высокого уровня преобразуются в инструкции на машин- ном языке с помощью другой программы — компилятора. Некоторые компиля- торы транслируют высокоуровневые инструкции на язык ассемблера, а затем
Что такое компиляция?
381
с помощью ассемблера производят окончательную трансляцию на машинный язык.
Компиляции, как правило, сопутствует процесс компоновки. Программы часто вы- полняют множество типовых операций. Возьмем для примера операцию откры- тия файла. Многие программы выполняют ее, но было бы слишком расточительно в каждой программе реализовывать свою процедуру открытия файлов. Предпо- чтительнее иметь единый программный код, знающий, как открывать файлы, и дать всем программам возможность использовать его. Поддержка решения ти- повых задач осуществляется с помощью библиотек. Они содержат множество под- программ, которые решают типовые задачи и могут использоваться множеством программ. Если заглянуть в каталоги /lib и /usr/lib, мы обнаружим подобные библиотеки. Для формирования связей между результатом работы компилятора и библиотеками, необходимыми компилируемой программе, используется про- грамма-компоновщик (linker, ее также называют редактором связей). Окончатель- ным результатом этого процесса является выполняемый файл программы, готовый к использованию.
Все ли программы компилируются?
Нет. Как мы уже видели, некоторые программы, такие как сценарии на языке командной оболочки, не требуют компиляции и выполняются непосредственно. Они написаны на языках, которые называют языками сценариев, или интерпре- тируемыми языками. К числу этих языков, популярность которых только растет в последние годы, относятся Perl, Python, PHP, Ruby и многие другие.
Программы на языках сценариев выполняются специальной программой, интер- претатором. Интерпретатор получает файл программы, читает его и выполняет каждую инструкцию, содержащуюся в нем. Вообще, интерпретируемые програм- мы выполняются намного медленнее, чем компилируемые. Это объясняется не- обходимостью транслировать исходный код каждой инструкции в интерпретиру- емой программе всякий раз, когда она встречается, тогда как в скомпилированной программе исходный код инструкций был уже однажды преобразован в машин- ный код и сохранен в окончательном выполняемом файле.
Но почему тогда интерпретирующие языки так популярны? Для многих рутинных задач они оказываются «достаточно быстрыми», но самое большое их достоин- ство в простоте и скорости разработки интерпретируемых программ в сравнении с компилируемыми. Разработка программ — это обычно циклический процесс, включающий три этапа: создание исходного кода, компиляцию и тестирование. С увеличением программы в размерах этап компиляции в упомянутом цикле
382
Глава 23. Компиляция программ
может оказаться весьма продолжительным. Интерпретирующие языки избавля- ют от необходимости компиляции и тем самым ускоряют их разработку.
Компиляция программ на C
Давайте что-нибудь скомпилируем. Для этого нам понадобятся некоторые ин- струменты, такие как компилятор, компоновщик и утилита make. Практически во всех системах Linux используется один и тот же компилятор языка C с именем gcc (GNU C Compiler), первоначально написанный Ричардом Столлманом. Многие дистрибутивы не устанавливают gcc по умолчанию. Проверить его присутствие в системе можно так:
[me@linuxbox ~]$ which gcc /usr/bin/gcc
Результат свидетельствует о присутствии компилятора.
СОВЕТ
Ваш дистрибутив может иметь метапакет (коллекцию пакетов) для разработки программ. В этом случае рекомендуется установить его, если вы собираетесь компилировать программы в своей системе. Если такой метапакет отсутствует, попробуйте установить пакеты gcc и make. Во многих дистрибутивах их вполне достаточно для выполнения упражнений, предлагаемых далее.
Получение исходного кода
В нашем упражнении мы скомпилируем программу с названием diction из про- екта GNU. Эта маленькая удобная программка проверяет качество и стиль содер- жимого текстовых файлов. А поскольку она невелика, она легко компилируется.
Следуя соглашениям, мы сначала создадим каталог src для исходного кода и за- тем загрузим в него исходный код с помощью команды ftp:
[me@linuxbox ~]$ mkdir src [me@linuxbox ~]$ cd src [me@linuxbox src]$ ftp ftp.gnu.org Connected to ftp.gnu.org. 220 GNU FTP server ready. Name (ftp.gnu.org:me): anonymous 230 Login successful.
Компиляция программ на C
383
Remote system type is UNIX. Using binary mode to transfer files. ftp> cd gnu/diction 250 Directory successfully changed. ftp> ls 200 PORT command successful. Consider using PASV. 150 Here comes the directory listing. -rw-r–r– 1 1003 65534 68940 Aug 28 1998 diction-0.7.tar.gz -rw-r–r– 1 1003 65534 90957 Mar 04 2002 diction-1.02.tar.gz -rw-r–r– 1 1003 65534 141062 Sep 17 2007 diction-1.11.tar.gz 226 Directory send OK. ftp> get diction-1.11.tar.gz local: diction-1.11.tar.gz remote: diction-1.11.tar.gz 200 PORT command successful. Consider using PASV. 150 Opening BINARY mode data connection for diction-1.11.tar.gz (141062 bytes). 226 File send OK. 141062 bytes received in 0.16 secs (847.4 kB/s) ftp> bye 221 Goodbye. [me@linuxbox src]$ ls diction-1.11.tar.gz
В предыдущем примере мы использовали традиционный метод с применением ftp, но есть и другие способы загрузки исходного кода. Например, проект GNU поддерживает также загрузку по протоколу HTTPS. То есть мы могли бы загру- зить исходный код diction с помощью программы wget:
[me@linuxbox src]$ wget https://ftp.gnu.org/gnu/diction/diction-1.11.tar.gz –2018-07-25 09:42:20– https://ftp.gnu.org/gnu/diction/diction-1.11.tar.gz Resolving ftp.gnu.org (ftp.gnu.org)… 208.118.235.20, 2001:4830:134:3::b Connecting to ftp.gnu.org (ftp.gnu.org)|208.118.235.20|:443… connected. HTTP request sent, awaiting response… 200 OK Length: 141062 (138K) [application/x-gzip] Saving to: ‘diction-1.11.tar.gz’ diction-1.11.tar.gz 100%[===================>] 137.76K –.-KB/s in 0.09s
2018-07-25 09:42:20 (1.43 MB/s) - ‘diction-1.11.tar.gz.1’ saved [141062/141062]
ПРИМЕЧАНИЕ
Поскольку мы сами управляем процессом компиляции исходного кода, поместим его в каталог ~/src. Исходный код, устанавливаемый дистрибутивом, помеща- ется в каталог /usr/src, а исходный код, предназначенный для использования множеством пользователей, обычно устанавливается в /usr/local/src.
384
Глава 23. Компиляция программ
Исходный код обычно распространяется в виде сжатого tar-файла. Иногда назы- ваемые тарболлами (tarball), эти файлы содержат дерево исходных текстов, или иерархию каталогов и файлов, составляющих исходный код. Подключившись к FTP-сайту, мы получили список доступных tar-файлов и выбрали для загруз- ки самую свежую версию. При помощи команды get программы ftp скопировали файл с сервера FTP на локальную машину.
После загрузки tar-файла его нужно распаковать. Делается это с помощью про- граммы tar:
[me@linuxbox src]$ tar xzf diction-1.11.tar.gz [me@linuxbox src]$ ls diction-1.11 diction-1.11.tar.gz
СОВЕТ
Программа diction, подобно всем программам из проекта GNU, следует опреде- ленным стандартам упаковки исходного кода. Большая часть других исходных кодов, доступных в экосистеме Linux, также следует этому стандарту. Одним из элементов стандарта является создание каталога с деревом исходных текстов и именем project-x.xx после распаковывания tar-файла, то есть с именем, со- держащим имя проекта и номер версии. Такая схема упрощает установку не- скольких версий одной и той же программы. Однако перед распаковыванием хорошо бы исследовать организацию дерева. При распаковывании некоторых проектов каталог не создается, а файлы помещаются непосредственно в текущий каталог, что может вызвать неразбериху и путаницу в хорошо организованном каталоге src. Чтобы избежать этого, пользуйтесь следующей командой для ис- следования содержимого tar-файла:
tar tzvf tarfile | head
Исследование дерева исходных текстов
В результате распаковывания tar-файла был создан новый каталог diction-1.11. Этот каталог содержит дерево исходных текстов. Давайте заглянем внутрь:
[me@linuxbox src]$ cd diction-1.11 [me@linuxbox diction-1.11]$ ls config.guess diction.c getopt.c nl config.h.in diction.pot getopt.h nl.po config.sub diction.spec getopt_int.h README configure diction.spec.in INSTALL sentence.c configure.in diction.texi.in install-sh sentence.h
Компиляция программ на C
385
COPYING en Makefile.in style.1.in de en_GB misc.c style.c de.po en_GB.po misc.h test diction.1.in getopt1.c NEWS
Здесь мы видим множество файлов. Программы, принадлежащие проекту GNU, а также многие другие поставляются вместе с файлами документации README, INSTALL, NEWS и COPYING. В них содержится описание программы, информа- ция о порядке сборки и установки и условия лицензионного соглашения. Я реко- мендую всегда внимательно прочитывать файлы README и INSTALL перед сбор- кой программы.
Другими интересными файлами в этом каталоге являются файлы с расширения- ми .c и .h:
[me@linuxbox diction-1.11]$ ls *.c diction.c getopt1.c getopt.c misc.c sentence.c style.c [me@linuxbox diction-1.11]$ ls *.h getopt.h getopt_int.h misc.h sentence.h
Файлы с расширением .c содержат две программы на C, входящие в состав пакета (style и diction), разбитые на несколько модулей. Большие программы часто раз- бивают на более мелкие, более простые в сопровождении фрагменты. Файлы с ис- ходным кодом содержат простой текст, и их можно исследовать с помощью less:
[me@linuxbox diction-1.11]$ less diction.c
Файлы с расширением .h известны как заголовочные файлы. Они тоже содержат простой текст — описание подпрограмм, подключаемых из файла с исходным ко- дом или библиотеки. Чтобы компилятор смог связать модули, он должен иметь описание всех модулей, составляющих единую программу. Ближе к началу в фай- ле diction.c имеется следующая строка:
#include “getopt.h”
Она требует от компилятора прочитать фал getopt.h, прежде чем продолжать чтение исходного кода в diction.c, чтобы «узнать», что имеется в файле getopt.c. Файл getopt.c содержит код подпрограмм, используемых обеими программами, style и diction.
Инструкции подключения файла getopt.h предшествует еще несколько инструк- ций include:
#include #include #include #include #include
386
Глава 23. Компиляция программ
Они также ссылаются на заголовочные файлы, но эти файлы хранятся за предела- ми дерева исходных текстов. Они должны поставляться системой для поддержки компиляции программ. Эти файлы можно найти в каталоге /usr/include:
[me@linuxbox diction-1.11]$ ls /usr/include
Заголовочные файлы помещаются в этот каталог в процессе установки компиля- тора.
Сборка программ
В большинстве случаев сборка программы заключается в выполнении последова- тельности из двух команд:
./configure make
Программа configure — это сценарий командной оболочки, поставляемый вместе с деревом исходных текстов. Его задача — проанализировать окружение сборки. Большинство исходного кода поддерживает переносимость. То есть такой исходный код спроектирован так, что допускает сборку в разных Unix-подобных системах. Но для этого во время сборки в исходный код может потребоваться внести небольшие изменения, учитывающие различия между системами. Программа configure также проверяет наличие необходимых внешних инструментов и компонентов.
Давайте запустим configure. Так как эта программа находится не там, где команд- ная оболочка обычно ищет выполняемые файлы, нужно явно сообщить ей место- положение программы, добавив в команду префикс ./. Он указывает, что про- грамма находится в текущем рабочем каталоге:
[me@linuxbox diction-1.11]$ ./configure
В процессе проверки и настройки сборки configure выведет множество сообще- ний. Последние строки ее вывода должны выглядеть примерно так:
checking libintl.h presence… yes checking for libintl.h… yes checking for library containing gettext… none required configure: creating ./config.status config.status: creating Makefile config.status: creating diction.1 config.status: creating diction.texi config.status: creating diction.spec config.status: creating style.1
Компиляция программ на C
387
config.status: creating test/rundiction config.status: creating config.h [me@linuxbox diction-1.11]$
Самое важное здесь — отсутствие сообщений об ошибках. Их появление означало бы неудачу настройки и невозможность сборки программы до их устранения.
Как мы видим, configure создала в каталоге с исходным кодом несколько файлов. Самым важным является файл сборки Makefile. Файл сборки — это конфигура- ционный файл с инструкциями для программы make, описывающими, как собрать программу. Без такого файла утилита make работать не будет. Это обычный тексто- вый файл, то есть мы можем заглянуть в него:
[me@linuxbox diction-1.11]$ less Makefile
Программа make принимает файл сборки (обычно с именем Makefile), в котором описываются отношения и зависимости между компонентами, составляющими окончательную программу.
Первый раздел в файле сборки определяет переменные для подстановки в после- дующих его разделах. Например, здесь можно увидеть строку
CC= gcc
определяющую, что роль компилятора C будет играть gcc. Далее в файле можно посмотреть, как используется это определение:
diction: diction.o sentence.o misc.o getopt.o getopt1.o
$(CC) -o $@ $(LDFLAGS) diction.o sentence.o misc.o
getopt.o getopt1.o $(LIBS)
Здесь выполняется подстановка: во время выполнения конструкция $(CC) заме- щается командой gcc.
Большую часть файла сборки занимают строки, определяющие целевой файл (target) — в данном случае выполняемый файл diction — и файлы, от которых он зависит. Остальные строки описывают команды, которые необходимо выпол- нить для создания целевого файла из его компонентов. Мы видим, что выполняе- мый файл diction (одна из конечных программ) зависит от присутствия файлов diction.o, sentence.o, misc.o, getopt.o и getopt1.o. Далее в файле сборки присутствуют определения, в которых каждый из этих файлов играет роль целевого.
diction.o: diction.c config.h getopt.h misc.h sentence.h getopt.o: getopt.c getopt.h getopt_int.h getopt1.o: getopt1.c getopt.h getopt_int.h
388
Глава 23. Компиляция программ
misc.o: misc.c config.h misc.h sentence.o: sentence.c config.h misc.h sentence.h style.o: style.c config.h getopt.h misc.h sentence.h
Однако в этих определениях не видно ни одной команды. Обработка этих строк осуществляется определением общей цели, что находится выше в файле, где опи- сывается команда компиляции всех файлов с расширением .c в файлы с расшире- нием .o:
.c.o: $(CC) -c $(CPPFLAGS) $(CFLAGS) $
Заголовок страницы
Тело страницы.
Если ввести этот текст в текстовом редакторе и сохранить в файле с именем foo. html, мы сможем открыть его, введя следующий адрес URL в Firefox: file:///home/ username/foo.html.
400
Глава 25. Начало проекта
На первом этапе создадим программу, которая будет выводить эту разметку HTML в стандартный вывод. Написать такую программу очень просто. Откройте текстовый редактор и создайте файл с именем ~/bin/sys_info_page:
[me@linuxbox ~]$ vim ~/bin/sys_info_page
А затем введите следующую программу:
#!/bin/bash
Программа вывода страницы с информацией о системе #
echo "" echo " " echo " Page Title" echo " " echo " " echo " Page body." echo " " echo ""
Наша первая версия содержит строку-шебанг (shebang), комментарий (можно только приветствовать) и последовательность команд echo, по одной для вывода каждой строки. После сохранения файла сделайте его выполняемым и попробуй- те запустить:
[me@linuxbox ~]$ chmod 755 ~/bin/sys_info_page [me@linuxbox ~]$ sys_info_page
После запуска на экране должен появиться текст HTML-документа, потому что команды echo в сценарии посылают свои строки в стандартный вывод. Запусти- те программу снова и перенаправьте вывод программы в файл sys_info_page.html, чтобы затем посмотреть результат в веб-браузере:
[me@linuxbox ~]$ sys_info_page > sys_info_page.html [me@linuxbox ~]$ firefox sys_info_page.html
Пока все идет неплохо.
Разрабатывая программы, всегда следует помнить о простоте и ясности. Сопрово- ждение дается проще, когда программа легко читается и доступна для понимания, не говоря уже о том, что программу легче писать, когда есть возможность умень- шить объем ручного ввода. Текущая версия программы работает замечательно, но ее можно упростить. Если объединить все команды echo в одну, это определенно упростит в будущем добавление новых строк в вывод программы. Поэтому изме- ним программу, как показано ниже:
Этап второй: добавление некоторых данных
401
#!/bin/bash
Программа вывода страницы с информацией о системе #
echo "
Page Title
Page body.
"
Строки в кавычках могут включать символы перевода строки и, соответственно, содержать несколько строк текста. Командная оболочка будет продолжать читать текст, пока не встретит закрывающую кавычку. Это правило действует также в ко- мандной строке:
[me@linuxbox ~]$ echo "
Page Title Page body."
Символ > в начале каждой строки — это приглашение к вводу командной обо- лочки, определяемое ее переменной PS2. Оно появляется всякий раз, когда про- исходит ввод многострочной инструкции. Эта особенность пока малопонятна, но потом, когда мы познакомимся с многострочными программными инструкциями, ее преимущества станут очевидными.
Этап второй: добавление некоторых данных
Теперь, когда программа способна сгенерировать минимальный документ, доба- вим в отчет немного данных. Для этого внесите следующие изменения:
#!/bin/bash
Программа вывода страницы с информацией о системе #
echo "
402
Глава 25. Начало проекта
System Information Report
<h1>System Information Report
"
Здесь добавлено название страницы и заголовок в теле отчета.
Переменные и константы
В нашем сценарии возникла проблема. Обратили внимание, что строка System Information Report повторяется дважды? Вообще, для такого крохотного сце- нария это не такая большая проблема, но представьте по-настоящему длинный сценарий, в котором эта строка повторяется много раз. Если в таком сценарии понадобится изменить название, придется внести изменения во множестве мест, а это масса ручной работы. Можно ли изменить сценарий так, чтобы строка опре- делялась в нем только один раз? Это существенно упростило бы сопровождение сценария в будущем. Да, это возможно, например, так:
#!/bin/bash
Программа вывода страницы с информацией о системе #
title=“System Information Report”
echo "
$title
<h1>$title
"
Создав переменную с именем title и присвоив ей значение System Information Report, мы воспользовались преимуществами подстановки параметров и поме- стили строку во множество мест.
Но как создать переменную? Просто — достаточно использовать ее. Когда ко- мандная оболочка встречает переменную, она автоматически создает ее. Этим она отличается от многих языков программирования, в которых переменные должны явно объявляться или определяться до ее использования. Командная оболочка
Переменные и константы
403
слишком либеральна в этом отношении, что в итоге приводит к некоторым про- блемам. Например, рассмотрим следующий сценарий, выполненный в команд- ной строке:
[me@linuxbox ~]$ foo=“yes” [me@linuxbox ~]$ echo $foo yes [me@linuxbox ~]$ echo $fool
[me@linuxbox ~]$
Мы сначала присвоили значение yes переменной foo и затем вывели ее значе- ние командой echo. Далее, мы попробовали вновь вывести значение перемен- ной, но допустили опечатку, указав имя fool, и получили пустую строку. Та- кой результат объясняется тем, что командная оболочка благополучно создала переменную fool, встретив ее, и присвоила ей пустое значение по умолчанию. Из этого примера следует, что нужно внимательно следить за правописанием! Также важно понять, что в действительности произошло в этом примере. Из предыдущего знакомства с особенностями работы механизма подстановки мы знаем, что команда
[me@linuxbox ~]$ echo $foo
подвергается действию механизма подстановки параметров, в результате чего приобретает вид
[me@linuxbox ~]$ echo yes
С другой стороны, команда
[me@linuxbox ~]$ echo $fool
превращается в
[me@linuxbox ~]$ echo
На место пустой переменной ничего не подставляется! Это может вызвать ошиб- ку в командах, требующих наличия аргументов. Например:
[me@linuxbox ~]$ foo=foo.txt [me@linuxbox ~]$ foo1=foo1.txt [me@linuxbox ~]$ cp $foo $fool cp: после ‘foo.txt’ пропущен операнд, задающий целевой файл По команде “cp –help” можно получить дополнительную информацию.
404
Глава 25. Начало проекта
Мы присвоили значения двум переменным, foo и foo1. А затем попытались вы- полнить команду cp, но допустили опечатку в имени второго аргумента. После обработки механизмом подстановки команда cp получила только один аргумент, хотя требует двух.
Ниже приводятся несколько правил именования переменных:
Имена переменных могут состоять из алфавитно-цифровых символов (букв
и цифр) и символов подчеркивания.
Первый символ в имени переменной может быть только буквой или символом
подчеркивания.
Присутствие пробелов и знаков препинания в именах переменных не допуска-
ется.
Название переменная подразумевает значение, которое может изменяться, и во многих приложениях переменные именно так и используются. Однако пере- менная title в нашем приложении используется как константа. Константа, так же как переменная, имеет имя и содержит значение. Отличие лишь в том, что значение константы не изменяется. В приложении, осуществляющем геоме- трические расчеты, можно определить константу PI со значением 3.1415, вме- сто того, чтобы использовать это число по всей программе. Командная оболочка не различает константы и переменные; эти термины используются в основном для удобства программиста. Типичное соглашение — использовать буквы верх- него регистра для обозначения констант и буквы нижнего регистра для истин- ных переменных. Давайте изменим сценарий, приведя его в соответствие с этим соглашением:
#!/bin/bash
Программа вывода страницы с информацией о системе #
TITLE=“System Information Report For $HOSTNAME”
echo "
$TITLE
<h1>$TITLE
"
Попутно мы дополнили название, добавив в конец значение переменной команд- ной оболочки HOSTNAME. Это — сетевое имя машины.
Переменные и константы
405
ПРИМЕЧАНИЕ
В действительности командная оболочка имеет механизм, гарантирующий не- изменяемость констант, в виде встроенной команды declare с параметром -r (read-only — только для чтения). Если переменной TITLE присвоить значение, как показано ниже:
declare -r TITLE=“Page Title”
командная оболочка не допустит повторного присваивания значения перемен- ной TITLE. Этот механизм редко используется на практике, но он имеется и его можно применять в особенно строгих сценариях.
Присваивание значений переменным и константам
Мы подошли к моменту, когда наше знание особенностей работы механизма под- становки начинает приносить свои плоды. Как мы видели, присваивание значе- ний переменным производится так:
переменная=значение
где переменная — это имя переменной, а значение — строка. В отличие от неко- торых других языков программирования, командная оболочка не заботится о ти- пах значений, присваиваемых переменным; она все значения интерпретирует как строки. Существует возможность заставить командную оболочку ограничить круг присваиваемых значений целыми числами, задействовав команду declare с пара- метром -i, но, как и объявление переменных, доступных только для чтения, эта возможность редко используется на практике.
Обратите внимание на отсутствие пробелов в операторе присваивания между именем переменной, знаком «равно» и значением. А из чего может состоять значе- ние? Из всего что угодно, что можно развернуть в строку.
a=z # Присвоит переменной a строку “z”. b=“a string” # Внутренние пробелы должны находиться в кавычках. c=“a string and $b” # При присваивании допускается выполнять подстановку, # например, значений других переменных. d=$(ls -l foo.txt) # Результат выполнения команды. e=$((5 * 7)) # Подстановка результата арифметического выражения. f="\t\ta string\n" # Экранированные последовательности, такие как # символы табуляции и перевода строки.
В одной строке можно выполнить присваивание сразу нескольким переменным:
a=5 b=“a string”
406
Глава 25. Начало проекта
При использовании подстановки имена переменных можно заключать в необяза- тельные фигурные скобки {}. Это пригодится в том случае, когда имя переменной становится неоднозначным в окружающем контексте. В следующем примере вы- полняется попытка переименовать файл myfile в myfile1 с использованием пере- менной:
[me@linuxbox ~]$ filename=“myfile” [me@linuxbox ~]$ touch $filename [me@linuxbox ~]$ mv $filename $filename1 mv: после ‘myfile’ пропущен операнд, задающий целевой файл По команде “mv –help” можно получить дополнительную информацию.
Эта попытка не увенчалась успехом, потому что командная оболочка интерпрети- ровала второй аргумент команды mv как имя новой (и пустой) переменной. Ниже показано, как решается подобная проблема:
[me@linuxbox ~]$ mv $filename ${filename}1
Добавив фигурные скобки, мы гарантировали, что командная оболочка не будет интерпретировать последний символ 1 как часть имени переменной.
ПРИМЕЧАНИЕ
Выполняя подстановку, имена переменных и команды рекомендуется заключать в двойные кавычки, чтобы исключить разбиение строк на слова оболочкой. Особенно важно использовать кавычки, когда переменная может содержать имя файла.
Воспользуемся этой возможностью, чтобы добавить в отчет дополнительные дан- ные, а именно дату и время составления отчета, а также имя пользователя, соста- вившего отчет:
#!/bin/bash
Программа вывода страницы с информацией о системе #
TITLE=“System Information Report For $HOSTNAME” CURRENT_TIME=$(date +"%x %r %Z") TIME_STAMP=“Generated $CURRENT_TIME, by $USER”
echo "
$TITLE
Встроенные документы
407
<h1>$TITLE
<p>$TIME_STAMP
"
Встроенные документы
Мы рассмотрели два разных метода вывода текста, и оба используют команду echo. Однако существует еще один, третий метод, который называется встроенным до- кументом (here document), или встроенным сценарием (here script). Встроенный документ — это дополнительная форма перенаправления ввода/вывода, которая передает текст, встроенный в сценарий, на стандартный ввод команды. Действует это перенаправление так:
команда
$TITLE
<h1>$TITLE
<p>$TIME_STAMP
EOF
Теперь вместо команды echo в сценарии используются команда cat и встро- енный документ. На роль индикатора была выбрана строка EOF (означает
408
Глава 25. Начало проекта
end-of-file — конец файла, распространенное соглашение), и она отмечает конец встроенного текста. Обратите внимание, что строка-индикатор должна нахо- диться в отдельной строке, одна, и за ней не должно следовать никаких про- белов.
Но какие преимущества дало использование встроенного документа здесь? Прак- тически никаких, кроме того, что кавычки внутри встроенных документов теряют свое специальное значение для командной оболочки. Ниже приводится пример использования встроенного документа в командной строке:
[me@linuxbox ~]$ foo=“some text” [me@linuxbox ~]$ cat $foo
“$foo” ‘$foo’ $foo EOF some text “some text” ‘some text’ $foo
Как видите, командная оболочка не обращает никакого внимания на кавычки. Она интерпретирует их как обычные символы. Благодаря этому мы свободно вставляем кавычки во встроенные документы. Этим обстоятельством можно вос- пользоваться при разработке программ составления отчетов.
Встроенные документы можно использовать с любыми командами, принимающи- ми данные со стандартного ввода. В следующем примере встроенный документ используется для передачи последовательности команд программе ftp, чтобы за- грузить файл с удаленного FTP-сервера:
#!/bin/bash
Сценарий загрузки файла через FTP #
FTP_SERVER=ftp.nl.debian.org FTP_PATH=/debian/dists/stretch/main/installer-amd64/current/images/cdrom REMOTE_FILE=debian-cd_info.tar.gz
ftp -n
$TITLE
<h1>$TITLE
<p>$TIME_STAMP
$(report_uptime)
$(report_disk_space)
$(report_home_space)
EOF
Создать такие команды можно двумя способами: написать три отдельных сцена- рия и поместить их в каталог, входящий в список PATH, или встроить эти сцена- рии в программу в виде функций командной оболочки. Как уже отмечалось ранее, функции — это «мини-сценарии», находящиеся внутри другого сценария, кото- рые работают как автономные программы. Функции имеют две синтаксические формы. Первая выглядит так:
function имя { команды return }
Вторая проще (и часто предпочтительнее):
имя () { команды return }
Функции командной оболочки
413
где имя — это имя функции, а команды — последовательность команд внутри функ- ции. Обе формы эквивалентны и могут использоваться одна вместо другой. Ниже приводится сценарий, демонстрирующий использование функций командной оболочки:
1 #!/bin/bash 2 3 # Демонстрация функций командной оболочки 4 5 function step2 { 6 echo “Step 2” 7 return 8 } 9 10 # Здесь начинается основная программа 11 12 echo “Step 1” 13 step2 14 echo “Step 3”
Когда командная оболочка читает сценарий, она пропускает строки с 1-й по 11-ю, так как они содержат комментарии и определение функции. Выполнение начина- ется со строки 12 с командой echo. Строка 13 вызывает функцию step2, и команд- ная оболочка выполняет функцию как любую другую команду. Управление пере- дается в строку 6, и выполняется вторая команда echo. Следующей выполняется строка 7. Команда return в этой строке завершает выполнение функции и возвра- щает управление в строку, следующую за вызовом функции (строка 14). После этого выполняется заключительная команда echo. Обратите внимание: чтобы вы- зовы функций интерпретировались не как имена внешних программ, а действи- тельно как вызовы функций, эти функции должны быть определены в сценарии до их вызова.
Добавим в наш сценарий минимальные определения функций:
#!/bin/bash
Программа вывода страницы с информацией о системе #
TITLE=“System Information Report For $HOSTNAME” CURRENT_TIME=$(date +"%x %r %Z") TIME_STAMP=“Generated $CURRENT_TIME, by $USER”
report_uptime () { return }
414
Глава 26. Проектирование сверху вниз
report_disk_space () { return } report_home_space () { return }
cat
$TITLE
<h1>$TITLE
<p>$TIME_STAMP
$(report_uptime)
$(report_disk_space)
$(report_home_space)
EOF
Имена функций подчиняются тем же правилам, что и имена переменных. Функ- ция должна содержать хотя бы одну команду. Команда return (которая является необязательной) помогает удовлетворить это требование.
Локальные переменные
В сценариях, что нам доводилось писать до сих пор, все переменные (включая константы) были глобальными. Глобальные переменные существуют и доступны в любой точке программы. В некоторых случаях это безусловно полезное свой- ство осложняет использование функций. Внутри функций иногда желательно ис- пользовать локальные переменные. Локальные переменные доступны только вну- три функции, в которой они определены, и прекращают свое существование по завершении выполнения функции.
Поддержка локальных переменных позволяет программисту использовать пере- менные с именами, которые уже определены в сценарии, глобально или в других функциях, не беспокоясь о возможных конфликтах имен.
Следующий пример сценария демонстрирует, как определяются и используются локальные переменные:
Локальные переменные
415
#!/bin/bash
local-vars: сценарий, демонстрирующий локальные переменные #
foo=0 # глобальная переменная foo
funct_1 () {
local foo # переменная foo, локальная для funct_1
foo=1
echo "funct_1: foo = $foo"
}
funct_2 () {
local foo # переменная foo, локальная для funct_2
foo=2
echo "funct_2: foo = $foo"
}
echo “global: foo = $foo” funct_1 echo “global: foo = $foo” funct_2 echo “global: foo = $foo”
Как видите, локальные переменные объявляются добавлением слова local перед именем переменной. В результате создается переменная, локальная по отноше- нию к функции, в которой она определена. Когда выполнение выйдет за пределы функции, переменная перестанет существовать. Если запустить этот сценарий, он выведет следующее:
[me@linuxbox ~]$ local-vars global: foo = 0 funct_1: foo = 1 global: foo = 0 funct_2: foo = 2 global: foo = 0
Этот пример показывает, что присваивание значений локальной переменной foo внутри обеих функций не оказывает влияния на значение переменной foo, объ- явленной за пределами функций.
416
Глава 26. Проектирование сверху вниз
Эта особенность позволяет писать функции, сохраняя их независимость друг от друга и от сценария, в котором они определяются. Это очень ценное качество, оно предотвращает взаимовлияние разных частей программы друг на друга, а кроме того, помогает писать переносимые функции, то есть функции, которые можно скопировать из одного сценария в другой.
Постоянное опробование сценария
В процессе разработки программ необходимо постоянно проверять их работоспо- собность. Запуская и тестируя программы как можно чаще, мы сможем выявить ошибки на самых ранних этапах разработки. Это существенно упрощает задачу отладки. Например, если после внесения небольших изменений и очередного запуска программы обнаружится ошибка, источник проблемы почти наверняка будет находиться в последних изменениях. Добавив пустые функции, которые на языке программистов называются заглушками, мы смогли проверить работо- способность программы на ранней стадии. Создавая заглушку, неплохо было бы включить в нее что-то, что давало бы обратную связь, позволяющую программи- сту оценить ход выполнения. Если сейчас взглянуть на вывод нашего сценария, можно заметить несколько пустых строк, следующих за строкой с текущим време- нем, но мы пока не уверены в причинах их появления.
[me@linuxbox ~]$ sys_info_page
System Information Report For twin2
<h1>System Information Report For linuxbox
Generated 03/19/2012 04:02:10 PM EDT, by me
Изменим функции, добавив в них сообщения для обратной связи:
report_uptime () { echo “Function report_uptime executed.” return }
report_disk_space () {
Постоянное опробование сценария
417
echo "Function report_disk_space executed."
return
}
report_home_space () { echo “Function report_home_space executed.” return }
И запустим сценарий еще раз:
[me@linuxbox ~]$ sys_info_page
System Information Report For linuxbox
<h1>System Information Report For linuxbox
<p>Generated 03/20/2012 05:17:26 AM EDT, by me
Function report_uptime executed.
Function report_disk_space executed.
Function report_home_space executed.
Теперь можно с уверенностью сказать, что наши три функции выполняются как надо.
Теперь, когда каркас функций готов и работает, самое время добавить в них некий код. Сначала займемся функцией report_uptime:
report_uptime () { cat System Uptime
$(uptime) EOF return }Она выглядит очень просто. Мы использовали встроенный документ для вывода заголовка раздела и результатов выполнения команды uptime, заключив их в теги
, чтобы сохранить формат вывода команды. Функция report_disk_space вы- глядит аналогично: report_disk_space () { cat Disk Space Utilization$(df -h) _EOF_ return } Она получает информацию о дисковом пространстве с помощью команды df -h. Наконец, определим функцию report_home_space: report_home_space () { cat Home Space Utilization$(du -sh /home/*) _EOF_ return } Для решения поставленной задачи мы использовали команду du с параметрами -sh. Однако это не полное решение задачи. Даже притом, что его можно исполь- зовать в некоторых системах (например, в Ubuntu), кое-где оно работать не бу- дет. Причина в том, что во многих системах для домашних каталогов выбираются разрешения, не позволяющие читать их содержимое другим пользователям, что является вполне разумной мерой предосторожности. В этих системах функция report_home_space в том виде, в каком она написана здесь, будет работать, толь- ко если запустить сценарий с правами суперпользователя. Лучшее, что можно сделать в такой ситуации, — корректировать поведение сценария в соответствии с привилегиями пользователя, запустившего его. Мы займемся этим в главе 27. ФУНКЦИИ КОМАНДНОЙ ОБОЛОЧКИ В ФАЙЛЕ .BASHRC Функции командной оболочки могут служить прекрасной заменой псевдонимам и в действительности считаются предпочтительным способом определения не- больших команд для личного использования. Возможности псевдонимов весьма ограниченны в отношении использования некоторых видов команд и особенностей командной оболочки, тогда как функции позволяют все, что можно выразить в виде сценария. Например, если вам понравилась функция report_disk_space, создан- ная нами для нашего сценария, вы можете создать похожую функцию с именем ds в своем файле .bashrc: ds () { echo "Disk Space Utilization For $HOSTNAME" df -h } Заключение 419 Заключение В этой главе мы познакомились с широко применяемым методом проектирова- ния программ сверху вниз и увидели, как можно поэтапно развивать функции командной оболочки. Мы также научились при помощи локальных перемен- ных делать функции независимыми от других функций и программ, в которых они находятся. Функции можно делать переносимыми и пригодными для по- вторного использования во множестве программ, что поможет сэкономить массу времени. 27 Управление потоком выполнения: ветвление при помощи if В предыдущей главе мы столкнулись с проблемой. Как помочь сценарию адапти- ровать свое поведение в зависимости от привилегий пользователя, запустившего его? Для решения проблемы нам необходим некий способ «изменить направле- ние» выполнения сценария, опираясь на результаты проверки. Выражаясь язы- ком программистов, нам нужен способ, обеспечивающий ветвление программы. Рассмотрим простой пример логики, выраженный в псевдокоде, имитирующем язык компьютеров, но понятном человеку: x = 5 Если x = 5, тогда: Сказать "x равно 5". Иначе: Сказать "x не равно 5". Это — пример ветвления. Если условие «x = 5?» верно, выполняется строка: «Ска- зать ‘x равно 5’». Иначе выполняется строка: «Сказать ‘x не равно 5’». Инструкция if В сценариях на языке командной оболочки описанную выше логику можно реа- лизовать так: x=5 if [ $x = 5 ]; then echo "x equals 5." Код завершения 421 else echo "x does not equal 5." fi То же самое можно выполнить непосредственно в командной строке, получается немного короче: [me@linuxbox ~]$ x=5 [me@linuxbox ~]$ if [ $x = 5 ]; then echo "equals 5"; else echo "does not equal 5"; fi equals 5 [me@linuxbox ~]$ x=0 [me@linuxbox ~]$ if [ $x = 5 ]; then echo "equals 5"; else echo "does not equal 5"; fi does not equal 5 В этом примере мы выполнили команду дважды. Первый раз со значением 5 в пе- ременной x, что привело к выводу строки equals 5, и второй раз со значением 0 в переменной x, что привело к выводу строки not equal 5. Инструкция if имеет следующий синтаксис: if команды; then команды [elif команды; then команды...] [else команды] fi где команды — это список команд. На первый взгляд такой синтаксис выглядит запутанным. Но прежде чем прояснить его, посмотрим, как командная оболочка определяет, успешно или нет выполнена команда. Код завершения Команды (включая сценарии и функции, написанные нашими собственными ру- ками) по завершении работы возвращают системе значение, которое называют кодом завершения (exit status). Это значение — целое число в диапазоне от 0 до 255 — сообщает об успешном или неуспешном завершении команды. По соглаше- ниям значение 0 служит признаком успешного завершения, а любое другое — неу- спешного. Командная оболочка поддерживает переменную, посредством которой можно определить код завершения. Например: 422 Глава 27. Управление потоком выполнения: ветвление при помощи if [me@linuxbox ~]$ ls -d /usr/bin /usr/bin [me@linuxbox ~]$ echo $? 0 [me@linuxbox ~]$ ls -d /bin/usr ls: cannot access /bin/usr: No such file or directory [me@linuxbox ~]$ echo $? 2 В этом примере мы дважды выполнили команду ls. В первый раз команда выпол- нилась благополучно. Если вывести значение переменной $?, можно увидеть, что оно равно 0. Во второй раз команда ls сообщила об ошибке, а переменная $? содер- жала значение 2, указывающее, что команда столкнулась с ошибкой. Одни коман- ды используют разные коды завершения, чтобы сообщить о характере ошибки, тогда как другие, столкнувшись с любой ошибкой, просто возвращают значение 1. Страницы справочного руководства часто включают раздел с заголовком «Exit Status» («Коды завершения»), описывающий возвращаемые коды. Однако 0 всег- да служит признаком успешного выполнения. Командной оболочкой поддерживаются две чрезвычайно простые встроенные коман ды, которые просто завершаются с кодом 0 или 1. Команда true всегда за- вершается с признаком успеха, а команда false — всегда с признаком ошибки: [me@linuxbox ~]$ true [me@linuxbox ~]$ echo $? 0 [me@linuxbox ~]$ false [me@linuxbox ~]$ echo $? 1 Эти команды можно использовать для исследования особенностей работы ин- струкции if. Инструкция if в действительности просто оценивает код заверше- ния команды: [me@linuxbox ~]$ if true; then echo "It's true."; fi It's true. [me@linuxbox ~]$ if false; then echo "It's true."; fi [me@linuxbox ~]$ Команда echo "It's true." выполняется, только если команда, следующая за if, завершается успешно, и не выполняется, если команда, следующая за if, заверша- ется с признаком ошибки. Если за if следует список команд, успешность выпол- нения всего списка определяется по последней команде: [me@linuxbox ~]$ if false; true; then echo "It's true."; fi It's true. Команда test 423 [me@linuxbox ~]$ if true; false; then echo "It's true."; fi [me@linuxbox ~]$ Команда test Вне всяких сомнений, чаще всего с инструкцией if используется команда test. Команда test может выполнять различные проверки и сравнения. Она имеет две эквивалентные формы: test выражение и более популярную [ выражение ] где выражение возвращает истинное (true) или ложное (false) значение. Команда test возвращает код завершения 0, если выражение истинно, и код завершения 1, если выражение ложно. Выражения для проверки файлов В табл. 27.1 перечислены выражения, используемые для проверки файлов. Таблица 27.1. Выражения для проверки файлов Выражение Истинно, если файл1 -ef файл2 файл1 и файл2 имеют одно и то же число индексного узла (inode; то есть два имени принадлежат жестким ссылкам, ссы- лающимся на один и тот же файл) файл1 -nt файл2 файл1 новее файла файл2 файл1 -ot файл2 файл1 старше файла файл2 -b файл -с файл -d файл -e файл файл существует и является специальным файлом блочного устройства файл существует и является специальным файлом символьного устройства файл существует и является каталогом файл существует 424 Глава 27. Управление потоком выполнения: ветвление при помощи if Выражение Истинно, если Таблица 27.1 (окончание) -f файл -g файл -G файл -k файл -L файл -O файл -p файл -r файл -s файл -S файл файл существует и является обычным файлом файл существует и имеет атрибут set-group-ID (бит setgid) файл существует и принадлежит действующей группе файл существует и имеет атрибут «sticky bit» файл существует и является символической ссылкой файл существует и принадлежит действующему пользователю файл существует и является именованным каналом файл существует и доступен для чтения (имеет разрешение на чтение для действующего пользователя) файл существует и имеет размер больше нуля файл существует и является сетевым сокетом -t дескриптор_файла дескриптор_файла представляет файл, подключенный к тер- миналу. Это выражение можно использовать для проверки стандартных потоков ввода/вывода/ошибок -u файл -w файл -x файл файл существует и имеет атрибут setuid файл существует и доступен для записи (имеет разрешение на запись для действующего пользователя) файл существует и доступен для выполнения (имеет разреше- ние на выполнение для действующего пользователя) Следующий сценарий демонстрирует применение некоторых выражений с фай- лами: #!/bin/bash # test-file: проверка файла FILE=~/.bashrc if [ -e "$FILE" ]; then if [ -f "$FILE" ]; then echo "$FILE is a regular file." Команда test 425 fi if [ -d "$FILE" ]; then echo "$FILE is a directory." fi if [ -r "$FILE" ]; then echo "$FILE is readable." fi if [ -w "$FILE" ]; then echo "$FILE is writable." fi if [ -x "$FILE" ]; then echo "$FILE is executable/searchable." fi else echo "$FILE does not exist" exit 1 fi exit Сценарий проверяет файл, имя которого присвоено константе FILE, и выводит результат. Этот сценарий имеет две интересные особенности, на которые следует обратить внимание. Во-первых, отметьте, что параметр $FILE внутри выражений заключен в кавычки. Это не является обязательным требованием, но защищает от случаев, когда параметр пуст. Если механизм подстановки заменит $FILE пустым значением, это приведет к ошибке (операторы в этом случае будут интерпретиро- ваться как непустые строки, а не как операторы). Использование кавычек гаран- тирует, что за оператором всегда будет следовать строка, даже если она пустая. Во-вторых, обратите внимание на команду exit (в конце сценария). Команда exit принимает единственный необязательный аргумент, определяющий код возврата сценария. В отсутствие аргумента exit вернет значение по умолчанию 0. Такое использование exit позволит сценарию сообщить об ошибке, если в $FILE содер- жится имя несуществующего файла. Команда exit в самом конце сценария до- бавлена исключительно для формальности. Когда командная оболочка достигает конца сценария (то есть конца файла), она в любом случае завершает выполнение сценария с кодом завершения 0. Функции командной оболочки тоже могут возвращать свой код завершения, пере- давая целочисленный аргумент команде return. Чтобы преобразовать сценарий, приведенный выше, в функцию для использования в больших программах, нужно заменить команды exit инструкциями return: test_file () { # test-file: проверка файла 426 Глава 27. Управление потоком выполнения: ветвление при помощи if FILE=~/.bashrc if [ -e "$FILE" ]; then if [ -f "$FILE" ]; then echo "$FILE is a regular file." fi if [ -d "$FILE" ]; then echo "$FILE is a directory." fi if [ -r "$FILE" ]; then echo "$FILE is readable." fi if [ -w "$FILE" ]; then echo "$FILE is writable." fi if [ -x "$FILE" ]; then echo "$FILE is executable/searchable." fi else echo "$FILE does not exist" return 1 fi } Выражения для проверки строк В табл. 27.2 перечислены выражения, используемые для проверки строк. Таблица 27.2. Выражения для проверки строк Выражение Истинно, если строка -n строка -z строка строка не пустая Длина строки больше нуля Длина строки равна нулю строка1 = строка2 строка1 == строка2 строка1 и строка2 равны. Допускается использовать один или два знака «равно», но предпочтительнее два строка1 != строка2 строка1 и строка2 не равны строка1 > строка2 строка1 и &2 exit 1 fi if [ "$ANSWER" == "yes" ]; then echo "The answer is YES." elif [ "$ANSWER" == "no" ]; then echo "The answer is NO." elif [ "$ANSWER" == "maybe" ]; then echo "The answer is MAYBE." else echo "The answer is UNKNOWN." fi В этом сценарии определяется константа ANSWER. Сначала сценарий проверяет, не является ли строка пустой. Если строка пустая, сценарий завершается с кодом 1. Обратите внимание на оператор перенаправления в команде echo. Он перенаправ- ляет сообщение об ошибке «There is no answer» («Нет ответа») в стандартный вы- вод ошибок как «наиболее подходящий» для сообщений об ошибках. Если строка не пустая, сценарий сравнивает ее значение со строками «yes», «no» или «maybe». Проверки выполняются с использованием инструкции elif, которая является 428 Глава 27. Управление потоком выполнения: ветвление при помощи if краткой формой записи для else if. Инструкция elif позволяет конструировать более сложные логические проверки. Выражения для проверки целых чисел В табл. 27.3 перечислены выражения, используемые для проверки целых чисел. Таблица 27.3. Выражения для проверки целых чисел Выражение Истинно, если число1 -eq число2 число1 и число2 равны число1 -ne число2 число1 и число2 не равны число1 -le число2 число1 меньше или равно числу2 число1 -lt число2 число1 меньше, чем число2 число1 -ge число2 число1 больше или равно числу2 число1 -gt число2 число1 больше, чем число2 Следующий сценарий демонстрирует их применение: #!/bin/bash # test-integer: проверка целочисленного значения. INT=-5 if [ -z "$INT" ]; then echo "INT is empty." >&2 exit 1 fi if [ $INT -eq 0 ]; then echo "INT is zero." else if [ $INT -lt 0 ]; then echo "INT is negative." else echo "INT is positive." fi if [ $((INT % 2)) -eq 0 ]; then echo "INT is even." Более современная версия команды test 429 else echo "INT is odd." fi fi Обратите внимание на то, как сценарий определяет четность (even) или нечет- ность (odd) целого числа. Он возвращает остаток от деления числа на 2, по значе- нию которого можно судить о четности или нечетности числа. Более современная версия команды test Последние версии bash реализуют составную команду, которая действует как улучшенная замена для команды test. Она имеет следующий синтаксис: [[ выражение ]] где выражение возвращает истинное (true) или ложное (false) значение. Команда [[ ]] очень похожа на команду test (она поддерживает те же выражения), но до- бавляет новое выражение для проверки строк: строка1 =~ регулярное_выражение возвращающее истинное значение, если строка1 соответствует расширенному ре- гулярному выражению. Это открывает широкие перспективы для решения таких задач, как проверка корректности данных. Предыдущий сценарий, демонстриру- ющий применение выражений проверки целых чисел, может завершиться с ошиб- кой, если константе INT присвоить любое значение, не являющееся целым числом. Для надежности сценарию необходима возможность убедиться, что константа действительно содержит целое число. Используя [[ ]] с оператором проверки строки =~, мы усовершенствуем его, как показано ниже: #!/bin/bash # test-integer2: проверка целочисленного значения. INT=-5 if [[ "$INT" =~ ^-?[0-9]+$ ]]; then if [ $INT -eq 0 ]; then echo "INT is zero." else if [ $INT -lt 0 ]; then echo "INT is negative." 430 Глава 27. Управление потоком выполнения: ветвление при помощи if else echo "INT is positive." fi if [ $((INT % 2)) -eq 0 ]; then echo "INT is even." else echo "INT is odd." fi fi else echo "INT is not an integer." >&2 exit 1 fi Применив регулярное выражение, мы смогли ограничить круг проверяемых зна- чений в константе INT только строками, начинающимися с необязательного зна- ка «минус», за которым следует одна или несколько цифр. Это выражение также устраняет вероятность появления пустых значений. Еще одна дополнительная особенность [[ ]]: оператор == поддерживает сопостав- ление с шаблоном по аналогии с механизмом подстановки путей. Например: [me@linuxbox ~]$ FILE=foo.bar [me@linuxbox ~]$ if [[ $FILE == foo.* ]]; then > echo "$FILE matches pattern 'foo.*'" > fi foo.bar matches pattern 'foo.*' Она превращает [[ ]] в удобный инструмент проверки имен файлов и путей. (( )) — для проверки целых чисел В дополнение к составной команде [[ ]] bash поддерживает также составную коман ду (( )), которую удобно использовать для работы с целыми числами. Она поддерживает полное множество арифметических операторов, о которых подроб- но рассказывается в главе 34. Команда (( )) применяется для проверки истинности арифметических выраже- ний. Арифметическое выражение считается истинным, если его результат отли- чается от нуля. [me@linuxbox ~]$ if ((1)); then echo "It is true."; fi It is true. [me@linuxbox ~]$ if ((0)); then echo "It is true."; fi [me@linuxbox ~]$ Объединение выражений 431 Применив (( )), можно немного упростить сценарий test-integer2, как показано ниже: #!/bin/bash # test-integer2a: проверка целочисленного значения. INT=-5 if [[ "$INT" =~ ^-?[0-9]+$ ]]; then if ((INT == 0)); then echo "INT is zero." else if ((INT &2 exit 1 fi Обратите внимание, что здесь мы использовали знак «меньше», а равенство про- веряется с помощью оператора ==. Такой синтаксис выглядит более естественным при работе с целыми числами. Отметьте также, что составная команда (( )) явля- ется частью синтаксиса командной оболочки, а не обычной командой, может при- меняться только к целым числам, распознает переменные по именам и не требует выполнять подстановку. Подробнее команду (( )) и связанную с ней подстановку значения арифметического выражения мы обсудим в главе 34. Объединение выражений Для более сложных вычислений существует возможность объединения выра- жений. Объединяются выражения с помощью логических операторов. Мы уже встречались с ними в главе 17, когда изучали команду find. Всего команды test и [[ ]] поддерживают три логические операции. Это И (AND), ИЛИ (OR) и НЕ 432 Глава 27. Управление потоком выполнения: ветвление при помощи if (NOT). Для представления этих операций test и [[ ]] используют разные опера- торы, как показано в табл. 27.4. Таблица 27.4. Логические операторы Операция test [[ ]] и (( )) И ИЛИ НЕ -a -o ! && || ! Ниже приводится пример использования операции И (AND). Следующий сцена- рий определяет вхождение целочисленного значения в определенный диапазон: #!/bin/bash # test-integer3: проверка вхождения целочисленного значения # в определенный диапазон. MIN_VAL=1 MAX_VAL=100 INT=50 if [[ "$INT" =~ ^-?[0-9]+$ ]]; then if [[ "$INT" -ge "$MIN_VAL" && "$INT" -le "$MAX_VAL" ]]; then echo "$INT is within $MIN_VAL to $MAX_VAL." else echo "$INT is out of range." fi else echo "$INT is not an integer." >&2 exit 1 fi Этот сценарий определяет, попадает ли целочисленное значение INT в диапазон между MIN_VAL и MAX_VAL. Эта операция выполняется единственной командой [[ ]], включающей два выражения, разделенных оператором &&. Ту же проверку можно выполнить с помощью test: if [ "$INT" -ge "$MIN_VAL" -a "$INT" -le "$MAX_VAL" ]; then echo "$INT is within $MIN_VAL to $MAX_VAL." else echo "$INT is out of range." fi Объединение выражений 433 Оператор отрицания ! обращает результат выражения. Он возвращает истинное значение, если выражение ложно, и ложное значение, если выражение истинно. В следующем сценарии мы изменили логику вычислений, чтобы определить, на- ходится ли значение INT за пределами указанного диапазона: #!/bin/bash # test-integer4: проверка выхода целочисленного значения # за границы определенного диапазона. MIN_VAL=1 MAX_VAL=100 INT=50 if [[ "$INT" =~ ^-?[0-9]+$ ]]; then if [[ ! ("$INT" -ge "$MIN_VAL" && "$INT" -le "$MAX_VAL") ]]; then echo "$INT is outside $MIN_VAL to $MAX_VAL." else echo "$INT is in range." fi else echo "$INT is not an integer." >&2 exit 1 fi Здесь выражение заключено в круглые скобки для группировки. Если этого не сделать, оператор отрицания будет применяться к результату первого выражения, а не к объединению двух выражений. Ту же проверку можно реализовать с помо- щью test: if [ ! \( "$INT" -ge "$MIN_VAL" -a "$INT" -le "$MAX_VAL" \) ]; then echo "$INT is outside $MIN_VAL to $MAX_VAL." else echo "$INT is in range." fi Поскольку все выражения и операторы в команде test интерпретируются ко- мандной оболочкой как аргументы (в отличие от [[ ]] и (( ))), символы, имею- щие специальное значение для bash, такие как , ( и ), необходимо заключать в кавычки или экранировать. Учитывая, что команды test и [[ ]] до определенной степени равноценны, возни- кает вопрос: какой из них отдать предпочтение? Команда test является традици- онной (и частью стандарта POSIX), тогда как команда [[ ]] характерна для bash. Уметь пользоваться командой test крайне важно, потому что она применяется 434 Глава 27. Управление потоком выполнения: ветвление при помощи if очень широко, но команда [[ ]] проще и удобнее, поэтому она часто используется в современных сценариях. ПЕРЕНОСИМОСТЬ — БЕСПОЧВЕННЫЕ СТРАХИ ОТ НЕПОНИМАНИЯ Если вам доведется побеседовать с «истинными» пользователями Unix, вы быстро обнаружите, что многие из них Linux терпеть не могут. Они оценивают его как нечто нечистое и греховное. Один из принципов таких ревнителей Unix — все должно быть «переносимым». То есть любой сценарий, написанный вами, должен работать без изменений в любой Unix-подобной системе. Пользователи Unix имеют веские основания рассчитывать на это. Наблюдая по- следствия для мира Unix, вызванные внедрением проприетарных расширений команд и командных оболочек до появления POSIX, они естественно опасаются влияния Linux на их любимую ОС. Но переносимость имеет серьезный недостаток. Она тормозит прогресс и требует приведения всего и вся к «наименьшему общему знаменателю». Для сценариев на языке командной оболочки это означает, что они должны быть совместимы с sh, оригинальной командной оболочкой Bourne. Этот недостаток служит отговоркой, которой пользуются производители пропри- етарных расширений для их оправдания, только они называют их «новшествами». Но в действительности они замыкают пользователей на себя. Инструменты GNU, такие как bash, не имеют подобных ограничений. Они способству- ют переносимости благодаря поддержке стандартов и всеобщей доступности. bash и другие инструменты GNU можно установить практически в любую систему, даже в Windows, совершенно бесплатно. Поэтому не бойтесь использовать все возмож- ности, имеющиеся в командной оболочке bash. Она действительно переносима. Операторы управления: еще один способ ветвления bash поддерживает два оператора управления, которые используются для ветвле- ния. Операторы && (И) и || (ИЛИ) действуют подобно логическим операторам в составной команде [[ ]]. Они имеют следующий синтаксис: команда1 && команда2 и команда1 || команда2 Заключение 435 Важно понимать, как они действуют. В последовательности с оператором && пер- вая команда выполняется всегда, а вторая — только если первая завершилась успе- хом. В последовательности с оператором || первая команда выполняется всегда, а вторая — только если первая завершилась неудачей. В практическом смысле это означает, что можно выполнить следующую последо- вательность команд: [me@linuxbox ~]$ mkdir temp && cd temp Она создаст каталог с именем temp и, если эта операция завершится успехом, ката- лог temp будет назначен текущим рабочим каталогом. Попытка выполнить вторую команду будет произведена, только если команда mkdir завершится успехом. Ана- логично, следующая команда [me@linuxbox ~]$ [ -d temp ] || mkdir temp проверит существование каталога temp, и только если проверка не увенчается успехом, будет выполнена команда его создания. Такие конструкции очень удобно использовать для обработки ошибок в сценариях, о чем подробнее рассказывается в следующих главах. Например, в сценарии можно предусмотреть такую последо- вательность: [ -d temp ] || exit 1 Если сценарий требует наличия каталога temp, а он не существует, тогда сценарий завершится с кодом 1. Заключение Мы начали эту главу с вопроса, оставшегося без ответа в предыдущей главе: как сценарию sys_info_page определить, имеет ли текущий пользователь права на чтение всех домашних каталогов? После знакомства с инструкцией if эту пробле- му можно решить, добавив следующий код в функцию report_home_space: report_home_space () { if [[ $(id -u) -eq 0 ]]; then cat Home Space Utilization (All Users)$(du -sh /home/*) _EOF_ else cat Home Space Utilization ($USER) 436 Глава 27. Управление потоком выполнения: ветвление при помощи if$(du -sh $HOME) _EOF_ fi return } Здесь проверяется вывод команды id. Если вызвать команду id с параметром -u, она выведет числовой идентификатор действующего пользователя. Суперполь- зователю всегда присваивается числовой идентификатор 0. Зная это, мы скон- струировали два разных вложенных документа: один пользуется преимуществом привилегий суперпользователя, а другой ограничивается домашним каталогом текущего пользователя. Теперь мы немного отдохнем от программы sys_info_page, но не волнуйтесь. Мы еще вернемся к ней. А пока затронем те темы, знание которых потребуется, когда мы возобновим разработку. 28 Чтение ввода с клавиатуры В сценариях, написанных нами до сих пор, отсутствует одно свойство, характер- ное для многих компьютерных программ, — интерактивность, то есть возмож- ность взаимодействия с пользователем. Несмотря на то что многие программы не нуждаются в интерактивности, некоторые только выиграли бы, если бы име- ли возможность принимать ввод непосредственно от пользователя. Возьмем для примера сценарий из предыдущей главы: #!/bin/bash # test-integer2: проверка целочисленного значения. INT=-5 if [[ "$INT" =~ ^-?[0-9]+$ ]]; then if [ "$INT" -eq 0 ]; then echo "INT is zero." else if ["$INT" -lt 0 ]; then echo "INT is negative." else echo "INT is positive." fi if [ $((INT % 2)) -eq 0 ]; then echo "INT is even." else echo "INT is odd." fi fi else echo "INT is not an integer." >&2 exit 1 fi 438 Глава 28. Чтение ввода с клавиатуры Каждый раз, когда потребуется изменить значение INT, вы должны будете изме- нить сценарий. Пользоваться сценарием было бы удобнее, если бы он предлагал пользователю ввести значение. В этой главе мы посмотрим, как придать интерак- тивность нашим программам. read — чтение значений со стандартного ввода Встроенная команда read используется для чтения единственной строки со стан- дартного ввода. Эту команду можно использовать для чтения ввода с клавиатуры или, в случае перенаправления, строки данных из файла. Команда имеет следую- щий синтаксис: read [-параметры] [переменная...] где параметры — это один или несколько параметров из перечисленных в табл. 28.1 далее, а переменная — имя одной или нескольких переменных для сохранения вве- денного значения. Если имя переменной не указано, строка с данными сохраняет- ся в переменной REPLY. В простейшем случае read сохраняет значения полей, прочитанные со стандарт- ного ввода, в указанные переменные. Ниже показано, как можно изменить наш сценарий проверки целочисленных значений, задействовав в нем команду read: #!/bin/bash # read-integer: проверка целочисленного значения. echo -n "Please enter an integer -> " read int if [[ "$int" =~ ^-?[0-9]+$ ]]; then if [ "$int" -eq 0 ]; then echo "$int is zero." else if [ "$int" -lt 0 ]; then echo "$int is negative." else echo "$int is positive." fi if [ $((int % 2)) -eq 0 ]; then echo "$int is even." else echo "$int is odd." fi read — чтение значений со стандартного ввода 439 fi else echo "Input value is not an integer." >&2 exit 1 fi Сначала мы использовали команду echo с параметром -n (подавляющим вы- вод символа перевода строки в конце) для вывода приглашения к вводу, а затем коман ду read для ввода значения в переменную int. Запуск этого сценария при- водит к следующим результатам: [me@linuxbox ~]$ read-integer Please enter an integer -> 5 5 is positive. 5 is odd. Команда read может сохранять ввод в множестве переменных, это показано в сле- дующем сценарии: #!/bin/bash # read-multiple: чтение нескольких значений с клавиатуры echo -n "Enter one or more values > " read var1 var2 var3 var4 var5 echo "var1 = '$var1'" echo "var2 = '$var2'" echo "var3 = '$var3'" echo "var4 = '$var4'" echo "var5 = '$var5'" Этот сценарий вводит, присваивает переменным и выводит до пяти значений. Обратите внимание, как действует команда read, когда получает разное число значений: [me@linuxbox ~]$ read-multiple Enter one or more values > a b c d e var1 = 'a' var2 = 'b' var3 = 'c' var4 = 'd' var5 = 'e' [me@linuxbox ~]$ read-multiple Enter one or more values > a var1 = 'a' 440 Глава 28. Чтение ввода с клавиатуры var2 = '' var3 = '' var4 = '' var5 = '' [me@linuxbox ~]$ read-multiple Enter one or more values > a b c d e f g var1 = 'a' var2 = 'b' var3 = 'c' var4 = 'd' var5 = 'e f g' Если read получит меньше значений, чем ожидается, переменные, для которых не хватило значений, останутся пустыми, а при избыточном количестве значений на входе последняя переменная получит весь остаток введенной строки. Если не передать переменные команде read, весь ввод будет сохранен в перемен- ной командной оболочки REPLY: #!/bin/bash # read-single: чтение множества значений в переменную по умолчанию echo -n "Enter one or more values > " read echo "REPLY = '$REPLY'" Запуск этого сценария приводит к следующим результатам: [me@linuxbox ~]$ read-single Enter one or more values > a b c d REPLY = 'a b c d' Параметры read поддерживает параметры, перечисленные в табл. 28.1. Таблица 28.1. Параметры команды read Параметр Описание -a массив Сохранить ввод в указанный массив, начиная с элемента с индек- сом 0. Подробнее о массивах рассказывается в главе 35 -d разделитель Использовать в качестве признака конца ввода первый символ из строки разделитель, а не символ перевода строки read — чтение значений со стандартного ввода 441 Параметр Описание -e Использовать Readline для обработки ввода. Это позволяет редак- тировать ввод, как в командной строке -n число Прочитать указанное число символов, а не всю строку -p приглашение Показывать указанное приглашение к вводу -r -s Режим без промежуточной обработки. Не интерпретировать об- ратные слеши как экранирующие символы Безмолвный режим. Не производить эхо-вывод символов на экран в процессе ввода. Этот режим может пригодиться для организации ввода паролей и другой конфиденциальной информации -t секунды Предельное время ожидания. Завершить ввод по истечении ука- занного числа секунд. По истечении указанного интервала read вернет ненулевое значение -u дескриптор Произвести ввод из файла с указанным дескриптором вместо стандартного ввода Множество поддерживаемых параметров открывает доступ к довольно интерес- ным способам использования read. Например, параметр -p позволяет определить строку приглашения к вводу: #!/bin/bash # read-single: чтение множества значений в переменную по умолчанию read -p "Enter one or more values > " echo "REPLY = '$REPLY'" Параметры -t и -s позволяют писать сценарии, реализующие ввод «секретных» данных и прерывающие ввод по истечении заданного времени: #!/bin/bash # read-secret: ввод секретного пароля if read -t 10 -sp "Enter secret passphrase > " secret_pass; then echo -e "\nSecret passphrase = '$secret_pass'" else echo -e "\nInput timed out" >&2 exit 1 fi 442 Глава 28. Чтение ввода с клавиатуры Сценарий предлагает пользователю ввести секретный пароль и ждет 10 секунд. Если в течение этого времени ввод не был завершен, сценарий завершается с ко- дом ошибки. Поскольку в команду включен параметр -s, символы пароля не вы- водятся на экран в процессе ввода. С помощью параметров -e и -i можно предложить пользователю значение по умолчанию для ввода. #!/bin/bash # read-default: предложение значения по умолчанию, для ввода # которого достаточно нажать клавишу Enter. read -e -p "What is your user name? " -i $USER echo "You answered: '$REPLY'" Этот сценарий просит ввести имя учетной записи и использует переменную окру- жения USER, чтобы предложить значение по умолчанию. После запуска сценарий выведет строку со значением по умолчанию, и если пользователь просто нажмет ENTER, команда read присвоит это значение переменной REPLY. [me@linuxbox ~]$ read-default What is your user name? me You answered: 'me' Выделение полей в строке ввода с помощью IFS Обычно командная оболочка разбивает введенную строку на слова перед пере- дачей команде read. Как мы уже знаем, в результате этого слова в строке, разде- ленные одним или несколькими пробелами, превращаются в отдельные значения и присваиваются командой read разным переменным. Такое поведение командной оболочки регулируется переменной с именем IFS (от Internal Field Separator — внутренний разделитель полей). По умолчанию переменная IFS хранит символы пробела, табуляции и перевода строки, каждый из которых может служить раз- делителем полей. Изменяя значение переменной IFS, можно управлять делением ввода на поля пе- ред передачей команде read. Например, файл /etc/passwd хранит строки данных, в которых поля отделяются друг от друга двоеточием. Присвоив переменной IFS значение, состоящее из единственного двоеточия, можно с помощью read прочи- тать содержимое /etc/passwd и благополучно разделить строки на поля для при- сваивания разным переменным. Ниже приводится сценарий, который именно так и действует: read — чтение значений со стандартного ввода 443 #!/bin/bash # read-ifs: чтение полей из файла FILE=/etc/passwd read -p "Enter a username > " user_name file_info=$(grep "^$user_name:" $FILE) if [ -n "$file_info" ]; then IFS=":" read user pw uid gid name home shell &2 exit 1 fi Этот сценарий предлагает пользователю ввести имя учетной записи в системе и затем выводит разные поля, найденные в соответствующей записи в файле /etc/ passwd. В сценарии есть две интересные строки. Первая: file_info=$(grep "^$user_name:" $FILE) присваивает результат команды grep переменной file_info. Регулярное выраже- ние гарантирует извлечение из файла /etc/passwd единственной строки, соответ- ствующей введенному имени пользователя. Вторая интересная строка: IFS=":" read user pw uid gid name home shell &2 exit 1 } read -p "Enter a single item > " # пустой ввод (недопустимо) [[ -z "$REPLY" ]] && invalid_input 446 Глава 28. Чтение ввода с клавиатуры # ввод множества элементов (недопустимо) (( "$(echo "$REPLY" | wc -w)" > 1 )) && invalid_input # введено допустимое имя файла? if [[ "$REPLY" =~ ^[-[:alnum:]\._]+$ ]]; then echo "'$REPLY' is a valid filename." if [[ -e "$REPLY" ]]; then echo "And file '$REPLY' exists." else echo "However, file '$REPLY' does not exist." fi # введено вещественное число? if [[ "$REPLY" =~ ^-?[[:digit:]]*\.[[:digit:]]+$ ]]; then echo "'$REPLY' is a floating point number." else echo "'$REPLY' is not a floating point number." fi # введено целое число? if [[ "$REPLY" =~ ^-?[[:digit:]]+$ ]]; then echo "'$REPLY' is an integer." else echo "'$REPLY' is not an integer." fi else echo "The string '$REPLY' is not a valid filename." fi Этот сценарий предлагает пользователю ввести элемент данных и затем последо- вательно анализирует его содержимое. Как видите, в сценарии использовано мно- жество идей, с которыми мы уже познакомились, включая функции, [[ ]], (( )), операторы управления && и if, а также разумную дозу регулярных выражений. Меню Часто для организации интерактивной работы используются меню. Программы, управляемые системой меню, выводят список возможных вариантов и предлага- ют пользователю выбрать один из них. Например, представьте программу, которая выводит следующее: Выберите команду: 1. Вывести информацию о системе 2. Вывести информацию о дисковом пространстве 3. Вывести информацию об объеме домашнего каталога Меню 447 0. Выйти Введите номер выбранной команды [0-3] > Используя все, что мы узнали в ходе создания программы sys_info_page, мож- но сконструировать программу, реализующую решение задач, перечисленных в меню, приведенном выше: #!/bin/bash # read-menu: программа вывода системной информации, # управляемая с помощью меню clear echo " Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit " read -p "Enter selection [0-3] > " if [[ "$REPLY" =~ ^[0-3]$ ]]; then if [[ "$REPLY" == 0 ]]; then echo "Program terminated." exit fi if [[ "$REPLY" == 1 ]]; then echo "Hostname: $HOSTNAME" uptime exit fi if [[ "$REPLY" == 2 ]]; then df -h exit fi if [[ "$REPLY" == 3 ]]; then if [[ "$(id -u)" -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh "$HOME" fi 448 Глава 28. Чтение ввода с клавиатуры exit fi else echo "Invalid entry." >&2 exit 1 fi Этот сценарий делится на две логические части. Первая часть выводит меню и вводит выбор пользователя. Вторая часть идентифицирует выбор и выполняет соответствующие действия. Обратите внимание, как используется команда exit в этом сценарии. Она препятствует выполнению ненужного кода после заверше- ния затребованного действия. Наличие нескольких точек выхода из программы вообще считается дурным тоном (логику работы такой программы труднее по- нять), но в данном сценарии нас это устраивает. Заключение В этой главе мы сделали первый шаг к интерактивности, позволив пользователю вводить данные в наши программы с клавиатуры. Используя описанные приемы, можно написать множество полезных программ, например, программы, выпол- няющие специализированные вычисления или упрощающие доступ к таинствен- ным инструментам командной строки. В следующей главе мы усовершенствуем идею программ, управляемых при помощи меню, чтобы добиться большего. Дополнительные сведения Постарайтесь внимательно изучить программы из этой главы и достичь полного понимания их логической структуры, потому что программы, которые последуют далее, будут еще сложнее. В качестве упражнения перепишите программы этой главы, используя команду test вместо составной команды [[ ]]. Подсказка: ис- пользуйте grep для сопоставления с регулярными выражениями, а затем прове- ряйте код завершения. Это станет для вас хорошей практикой. 29 Управление потоком выполнения: циклы while и until В предыдущей главе мы написали программу, управляемую с помощью меню, для получения разного рода системной информации. Программа работает, но неудоб- на в использовании. Она выполняет только один выбранный вариант и заверша- ется. Хуже того, в случае ошибочного выбора программа завершается с выводом сообщения об ошибке, не давая возможности повторить попытку. Пользоваться программой было бы намного удобнее, если бы она снова и снова выводила меню и предлагала сделать выбор, пока пользователь не выберет пункт, соответствую- щий выходу из программы. В этой главе мы познакомимся с приемами организации циклов, с помощью кото- рых можно реализовать многократное выполнение участков программ. Команд- ная оболочка поддерживает три составные команды для организации циклов. Здесь мы познакомимся с двумя из них, а с третьей — в главе 33. Циклы Повседневная жизнь наполнена повторяющимися действиями. Каждодневная поездка на работу, прогулка с собакой и нарезание моркови — все эти действия состоят из повторяющейся последовательности действий. Рассмотрим в качестве примера резку моркови. Этот вид деятельности можно выразить на псевдокоде примерно так: 1. Взять разделочную доску. 2. Взять нож. 3. Положить морковь на доску. 4. Поднять нож. 5. Сдвинуть морковь. 450 Глава 29. Управление потоком выполнения: циклы while и until 6. Отрезать кусок. 7. Если вся морковь порезана, завершить операцию, иначе перейти к шагу 4. Шаги с 4-го по 7-й образуют цикл. Действия внутри цикла повторяются, пока не будет выполнено условие «вся морковь порезана». while В bash имеются средства, позволяющие выражать похожие идеи. Представьте, что нам нужно вывести пять чисел по порядку, от 1 до 5. В сценарии на языке bash это можно реализовать, как показано ниже: #!/bin/bash # while-count: вывод последовательности чисел count=1 while [[ $count -le 5 ]]; do echo $count count=$((count + 1)) done echo "Finished." Если запустить этот сценарий, он выведет: [me@linuxbox ~]$ while-count 1 2 3 4 5 Finished. Команда while имеет следующий синтаксис: while команды; do команды; done Подобно if, команда while проверяет код завершения списка команд. Пока код за- вершения равен 0, она выполняет команды внутри цикла. В сценарии, приведен- ном выше, создается переменная count, и ей присваивается начальное значение 1. Команда while проверяет код завершения составной команды [[ ]]. Пока [[ ]] возвращает код 0, команды внутри цикла продолжают выполняться. В конце каж- дого цикла повторно выполняется команда [[ ]]. После пяти итераций цикла зна- чение переменной count увеличится до 6, команда [[ ]] вернет код завершения, Циклы 451 отличный от 0, и цикл завершится, а программа продолжит выполнение с ин- струкции, следующей непосредственно за циклом. Цикл while можно использовать для усовершенствования программы read-menu из предыдущей главы: #!/bin/bash # while-menu: программа вывода системной информации, # управляемая с помощью меню DELAY=3 # Время отображения результатов на экране (в секундах) while [[ "$REPLY" != 0 ]]; do clear cat " if [[ "$REPLY" =~ ^[0-3]$ ]]; then if [[ "$REPLY" == 1 ]]; then echo "Hostname: $HOSTNAME" uptime sleep "$DELAY" fi if [[ "$REPLY" == 2 ]]; then df -h sleep "$DELAY" fi if [[ "$REPLY" == 3 ]]; then if [[ "$(id -u)" -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh "$HOME" fi sleep "$DELAY" fi else 452 Глава 29. Управление потоком выполнения: циклы while и until echo "Invalid entry." sleep "$DELAY" fi done echo "Program terminated." Заключив меню в цикл while, мы смогли заставить программу повторять вывод меню после каждой операции выбора. Цикл продолжает выполняться и выводить меню, пока переменная REPLY не получит значение 0, предоставляя пользователю возможность сделать другой выбор. После выполнения выбранной операции вы- полняется команда sleep, она приостанавливает программу на несколько секунд и дает возможность увидеть результаты до того, как экран будет очищен и на нем вновь появится меню. Когда переменная REPLY получит значение 0, соответству- ющее варианту «Quit» (выйти), цикл завершится и выполнение продолжится со строки, следующей за done. Прерывание цикла В bash имеются две встроенные команды для управления потоком выполнения внутри циклов. Команда break немедленно завершает цикл, после чего выпол- нение программы продолжается с первой инструкции, следующей за циклом. Команда пропускает оставшуюся часть цикла, и программа переходит к началу следующей итерации цикла. Ниже приводится версия программы while-menu, ис- пользующая обе команды — break и continue: #!/bin/bash # while-menu2: программа вывода системной информации, # управляемая с помощью меню DELAY=3 # Время отображения результатов на экране (в секундах) while true; do clear cat " Прерывание цикла 453 if [[ "$REPLY" =~ ^[0-3]$ ]]; then if [[ "$REPLY" == 1 ]]; then echo "Hostname: $HOSTNAME" uptime sleep "$DELAY" continue fi if [[ "$REPLY" == 2 ]]; then df -h sleep "$DELAY" continue fi if [[ "$REPLY" == 3 ]]; then if [[ "$(id -u)" -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh "$HOME" fi sleep "$DELAY" continue fi if [[ "$REPLY" == 0 ]]; then break fi else echo "Invalid entry." sleep $DELAY fi done echo "Program terminated." В этой версии сценария используется бесконечный цикл (цикл, который никогда не завершится сам по себе), в котором команда while проверяет код завершения команды true. Так как true всегда возвращает код 0, цикл никогда не завершит- ся. Этот прием на удивление широко используется в сценариях. Поскольку цикл никогда не завершится сам по себе, программист должен предусмотреть его при- нудительное прерывание в нужный момент времени. В этом сценарии выход из цикла осуществляется с помощью команды break, когда пользователь выберет пункт 0. В конец других операций добавлена команда continue, чтобы увеличить эффективность работы сценария. Встретив команду continue, сценарий пере- прыгнет через остальной код в цикле, который не требуется выполнять для дан- ного выбора. Например, если пользователь выбрал пункт 1, нет никаких причин проверять выбор остальных вариантов. 454 Глава 29. Управление потоком выполнения: циклы while и until until Команда until очень похожа на while, но завершает цикл не когда обнаружит не- нулевой код завершения, а наоборот. Цикл until продолжается, пока не получит код завершения 0. В сценарии while-count цикл продолжает выполняться, пока значение переменной count меньше или равно 5. Тот же результат можно полу- чить, переписав сценарий с командой until: #!/bin/bash # until-count: вывод последовательности чисел count=1 until [[ "$count" -gt 5 ]]; do echo "$count" count=$((count + 1)) done echo "Finished." С условным выражением $count -gt 5 команда until завершит цикл в нужный момент времени. Выбор между циклами while и until обычно зависит от того, в каком случае условное выражение будет более читабельным. Чтение файлов в циклах Команды while и until могут принимать данные со стандартного ввода. Это дает возможность обрабатывать файлы с их помощью. В следующем примере мы вы- ведем содержимое файла distros.txt, созданного в одной из предыдущих глав: #!/bin/bash # while-read: чтение строк из файла while read distro version release; do printf "Distro: %s\tVersion: %s\tReleased: %s\n" \ "$distro" \ "$version" \ "$release" done &2 exit 1 fi if ! cd "$dir_name"; then echo "Cannot cd to '$dir_name'" >&2 exit 1 fi if ! rm *; then echo "File deletion failed. Check results" >&2 exit 1 fi 462 Глава 30. Поиск и устранение ошибок ПЕРЕНОСИМЫЕ ИМЕНА ФАЙЛОВ Чтобы гарантировать переносимость имен файлов между разными платфор- мами (например, разными типами компьютеров и операционными системами), ограничьте набор символов, которые можно использовать в именах файлов. Существует стандарт POSIX Portable Filename Character Set (POSIX-совместимый набор символов для переносимых имен файлов), который можно использовать, чтобы увеличить вероятность совместимости имени файла с разными систе- мами. Стандарт очень прост. Он допускает использовать в именах файлов только заглавные A–Z и строчные a–z, буквы, цифры 0–9, точку (.), дефис (-) и подчеркивание (_). Также стандарт определяет, что имена файлов не могут начинаться с дефиса. Здесь проверяются существование каталога с указанным именем и успешное за- вершение команды cd. Если какая-то из проверок завершается неудачей, в стан- дартный вывод ошибок отправляется содержательное описание и сценарий завер- шается с кодом 1, чтобы показать, что он завершился с ошибкой. Будьте внимательны к именам файлов У этого сценария есть еще одна проблема, неочевидная, но очень опасная. Unix (и Unix-подобные операционные системы) по общему признанию имеет суще- ственный недостаток, касающийся имен файлов, — чрезмерно либеральное отно- шение к ним. Фактически имена файлов не могут включать только два символа: слеш (/), поскольку он используется для разделения элементов путей в файловой системе, и «пустой» символ (с нулевым кодом), который внутренне используется для обозначения концов строк. Все остальные символы считаются допустимыми, включая пробелы, табуляции, переводы строк, ведущие дефисы, возвраты каретки и т. д. Особую сложность вызывают ведущие дефисы. Например, ничто не помешает создать файл с именем -rf ~. А теперь представьте, что случится, если передать имя этого файла команде rm. Чтобы защититься от этой проблемы, нужно заменить команду удаления файлов в сценарии: rm * следующей командой: rm ./* Логические ошибки 463 Это предотвратит интерпретацию имен файлов, начинающихся с дефиса, как па- раметров команды. Возьмите за правило всегда предварять групповые символы (такие, как * и ?) комбинацией ./, чтобы предотвратить неверную интерпретацию команд. Примером таких подстановок с групповыми символами могут служить *.pdf или ???.mp3. Проверка ввода Главное правило надежного программирования: если программа принимает ввод, она должна уметь обработать все, что ей передали. Обычно это означает тщатель- ную отбраковку ввода с целью гарантировать, что дальнейшей обработке будут подвергнуты только допустимые данные. Пример такой проверки мы видели в предыдущей главе, когда обсуждали команду read. Там один из сценариев со- держал следующую проверку выбранного пункта меню: [[ "$REPLY" =~ ^[0-3]$ ]] Это очень специализированная проверка. Она возвращает код завершения 0, толь- ко если строка, введенная пользователем, содержит число в диапазоне от 0 до 3. Никакой другой ввод не принимается. Иногда писать такие проверки очень уто- мительно, но они совершенно необходимы, если вы хотите в результате получить надежно работающий сценарий. УДАЧНЫЙ ДИЗАЙН ЕСТЬ ФУНКЦИЯ ОТ ВРЕМЕНИ Когда я в студенчестве изучал промышленное проектирование, мудрый профессор учил нас, что степень проработки проекта определяется объемом времени, вы- деленного проектировщику. Если вам дано 5 минут на проектирование устройства для уничтожения воздушных целей, вы спроектируете мухобойку. А если срок — 5 месяцев, вы сможете спроектировать систему противовоздушной обороны с ла- зерным прицеливанием. Тот же принцип действует и в программировании. В некоторых случаях допусти- мо писать сценарии на скорую руку, но только если они будут использоваться один раз и только программистом. Потребность в таких сценариях возникает довольно часто, и они должны разрабатываться быстро, без затраты лишних усилий. Подобные сценарии не требуют подробных комментариев и защитных проверок. С другой стороны, если сценарий предназначен для постоянного ис- пользования, то есть он будет использоваться снова и снова для решения важ- ных задач или множеством пользователей, к его разработке следует подходить с большим тщанием. 464 Глава 30. Поиск и устранение ошибок Тестирование Тестирование — важный этап в разработке любого программного обеспечения, включая сценарии. В мире открытого программного обеспечения в ходу вы- сказывание «выпускай раньше, выпускай чаще», отражающее этот факт. Про- граммное обеспечение, выпускаемое раньше и чаще, получает больше времени на использование и тестирование. Опыт показывает, что ошибки тем легче най- ти и тем дешевле исправить, чем раньше в цикле разработки они будут обнару- жены. В главе 26 мы продемонстрировали использование заглушек для проверки потока выполнения программы. Это ценный прием проверки прогресса в работе, начиная с самых ранних стадий разработки сценария. Вернемся к рассматривавшейся выше задаче удаления файлов и посмотрим, как можно было бы легко протестировать ее решение. Тестировать оригинальный фрагмент довольно опасно, потому что его задача — удаление файлов, но его мож- но изменить, чтобы сделать тестирование безопасным: if [[ -d $dir_name ]]; then if cd $dir_name; then echo rm * # ТЕСТИРОВАНИЕ else echo "cannot cd to '$dir_name'" >&2 exit 1 fi else echo "no such directory: '$dir_name'" >&2 exit 1 fi exit # ТЕСТИРОВАНИЕ Так как проверка ошибочных условий уже выводит содержательные сообщения, нам не требуется добавлять ничего нового. Самое важное изменение заключается в добавлении команды echo перед командой rm, которая выведет ее и список ее аргументов, но не разрешит ей выполниться. Это изменение позволит безопасно выполнить код. В конец фрагмента мы добавили команду exit, чтобы завершить тест и предотвратить выполнение любых других частей сценария. Необходимость этого шага зависит от предназначения сценария. Мы также включили несколько комментариев, которые служат «маркерами» из- менений, имеющих отношение к тестированию. С их помощью легко можно найти и удалить эти изменения по завершении тестирования. Отладка 465 Комплекты тестов Чтобы извлечь пользу из тестирования, важно создавать и применять качествен- ные комплекты тестов. Для этого следует тщательно подобрать данные для вво- да или условия работы, отражающие крайние и пограничные ситуации. В нашем фрагменте кода (который очень прост) мы хотим проверить, как действует код в трех случаях: dir_name содержит имя существующего каталога; dir_name содержит имя несуществующего каталога; dir_name содержит пустое значение. Проверив каждое из этих условий, мы получим приличный охват тестиро- ванием. Так же как в случае с проектированием, тестирование есть функция от времени. Не каждую особенность сценария нужно тщательно тестировать. В действитель- ности выбор фрагментов для тестирования зависит от того, что считается важным. Поскольку наш фрагмент может нести разрушительные последствия, он заслужи- вает и тщательного проектирования, и тщательного тестирования. Отладка Если тестирование выявляет проблему в сценарии, то наступает черед следую- щего шага — отладки. Под «проблемой» обычно понимается несоответствие ре- зультатов работы сценария ожиданиям программиста. В этом случае нужно точно отследить, что сценарий делает и почему. Поиск ошибок иногда очень напоминает детективное расследование. Тщательное проектирование сценария может помочь в этом. Согласно принципу защитного программирования, сценарий должен обнаруживать ненормальные ус- ловия и выводить содержательные сообщения. Иногда, однако, возникают стран- ные и неожиданные проблемы, требующие применения более сложных приемов защиты. Поиск проблемной области В некоторых сценариях, особенно длинных, иногда полезным оказывается исполь- зование приема изолирования области сценария, связанной с проблемой. Пробле- ма не всегда является ошибкой, но изоляция часто помогает понять суть происхо- дящего. Один из приемов изоляции заключается в том, чтобы «закомментировать» 466 Глава 30. Поиск и устранение ошибок фрагмент сценария. Например, попробуем изменить наш фрагмент, удаляющий содержимое каталога, чтобы определить, имеет ли он отношение к ошибке: if [[ -d $dir_name ]]; then if cd $dir_name; then rm * else echo "cannot cd to '$dir_name'" >&2 exit 1 fi # else # echo "no such directory: '$dir_name'" >&2 # exit 1 fi Поместив символы комментария в начало каждой строки внутри логического раз- дела сценария, мы предотвратили возможность выполнения этого раздела. После- дующее повторное тестирование покажет, связан ли исключенный код с ошибоч- ным поведением. Трассировка Ошибки часто становятся причиной неожиданного направления выполнения сце- нария. То есть фрагменты сценария могут никогда не выполняться или выпол- няться в неправильном порядке или в неправильные моменты. Чтобы увидеть, как в действительности протекает выполнение программы, воспользуемся при- емом трассировки. Один из способов трассировки заключается в размещении информативных со- общений в разных точках сценария, сообщающих, где протекает выполнение. На- пример, добавим в наш фрагмент следующие сообщения: echo "preparing to delete files" >&2 if [[ -d $dir_name ]]; then if cd $dir_name; then echo "deleting files" >&2 rm * else echo "cannot cd to '$dir_name'" >&2 exit 1 fi else echo "no such directory: '$dir_name'" >&2 exit 1 fi echo "file deletion complete" >&2 Отладка 467 Здесь сообщения посылаются в стандартный вывод ошибок, чтобы отделить их от обычного вывода. Кроме того, отсутствуют отступы перед строками с сообщения- ми — это упростит их поиск, когда придет время убрать эти строки. Теперь, запустив сценарий, убедимся, что удаление файлов действительно было выполнено: [me@linuxbox ~]$ deletion-script preparing to delete files deleting files file deletion complete [me@linuxbox ~]$ Кроме того, bash поддерживает встроенный метод трассировки, реализованный в виде параметра -x и команды set с параметром -x. Возьмем для примера сцена- рий trouble, написанный ранее, и активируем встроенный механизм трассировки для всего сценария, добавив параметр -x в первую строку: #!/bin/bash -x # trouble: сценарий для демонстрации распространенных видов ошибок number=1 if [ $number = 1 ]; then echo "Number is equal to 1." else echo "Number is not equal to 1." fi После запуска мы получим следующие результаты: [me@linuxbox ~]$ trouble + number=1 + '[' 1 = 1 ']' + echo 'Number is equal to 1.' Number is equal to 1. Включенный механизм трассировки позволяет увидеть, какой вид приобре- тают команды после применения подстановки. Начальные знаки «плюс» по- могают отличить трассировочную информацию от обычного вывода. Знак «плюс» — это символ по умолчанию, используемый для вывода трассировки. Он хранится в переменной командной оболочки PS4 (prompt string 4 — строка приглашения 4). Изменим значение этой переменной, чтобы сделать трассиро- вочный вывод более полезным. Ниже мы изменили эту переменную, включив в трассировочный вывод текущий номер выполняемой строки в сценарии. Об- ратите внимание на необходимость использования одиночных кавычек — это 468 Глава 30. Поиск и устранение ошибок предотвращает подстановку до момента, когда строка приглашения не будет ис- пользоваться фактически: [me@linuxbox ~]$ export PS4='$LINENO + ' [me@linuxbox ~]$ trouble 5 + number=1 7 + '[' 1 = 1 ']' 8 + echo 'Number is equal to 1.' Number is equal to 1. Выполнить трассировку только выбранного фрагмента сценария можно с помо- щью команды set с параметром -x: #!/bin/bash # trouble: сценарий для демонстрации распространенных видов ошибок number=1 set -x # Включить трассировку if [ $number = 1 ]; then echo "Number is equal to 1." else echo "Number is not equal to 1." fi set +x # Выключить трассировку Здесь мы использовали команду set с параметром -x, чтобы включить трассиров- ку, и с параметром +x, чтобы выключить ее. Этот прием используется для исследо- вания сразу нескольких проблемных фрагментов в сценарии. Исследование значений в процессе выполнения Часто вместе с трассировкой полезно выводить содержимое переменных, чтобы иметь более полное представление о действиях сценария. Обычно для этого ис- пользуются дополнительные инструкции echo: #!/bin/bash # trouble: сценарий для демонстрации распространенных видов ошибок number=1 echo "number=$number" # ОТЛАДКА set -x # Включить трассировку Заключение 469 if [ $number = 1 ]; then echo "Number is equal to 1." else echo "Number is not equal to 1." fi set +x # Выключить трассировку В этом тривиальном примере мы просто вывели значение переменной number и от- метили дополнительную строку комментарием, чтобы в будущем упростить ее по- иск и удаление. Подобный прием особенно полезен при исследовании поведения циклов и арифметических операций в сценариях. Заключение В этой главе мы рассмотрели несколько проблем, с которыми можно столкнуться в процессе разработки сценариев. Конечно, таких проблем несоизмеримо больше. Приемы, описанные здесь, помогут вам в поиске наиболее распространенных ви- дов ошибок. Отладка — это искусство предотвращения ошибок (за счет постоян- ного тестирования в ходе разработки) и их поиска (путем эффективного исполь- зования приемов трассировки). 31 Управление потоком выполнения: ветвление с помощью case В этой главе мы продолжим знакомство с инструментами управления потоком выполнения. В главе 28 мы сконструировали простое меню и реализовали логи- ку обработки выбора его пунктов пользователем. Для этого использовалась серия команд if, выясняющих, какой из возможных вариантов выбран. Такие конструк- ции часто можно увидеть в программах, причем так часто, что в некоторых языках программирования (включая командную оболочку) был реализован механизм управления потоком выполнения для случаев с множеством альтернативных ва- риантов. Команда case Командная оболочка bash поддерживает составную команду выбора из несколь- ких вариантов, которая называется case. Она имеет следующий синтаксис: case слово in [шаблон [| шаблон]...) команды ;;]... esac Взгляните еще раз, как программа read-menu из главы 28 обрабатывает выбор пользователя: #!/bin/bash # read-menu: программа вывода системной информации, # управляемая с помощью меню Команда case 471 clear echo " Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit " read -p "Enter selection [0-3] > " if [[ "$REPLY" =~ ^[0-3]$ ]]; then if [[ "$REPLY" == 0 ]]; then echo "Program terminated." exit fi if [[ "$REPLY" == 1 ]]; then echo "Hostname: $HOSTNAME" uptime exit fi if [[ "$REPLY" == 2 ]]; then df -h exit fi if [[ "$REPLY" == 3 ]]; then if [[ "$(id -u)" -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh "$HOME" fi exit fi else echo "Invalid entry." >&2 exit 1 fi С помощью case можно сделать логику выбора немного проще: #!/bin/bash # case-menu: программа вывода системной информации, 472 Глава 31. Управление потоком выполнения: ветвление с помощью case # управляемая с помощью меню clear echo " Please Select: 1. Display System Information 2. Display Disk Space 3. Display Home Space Utilization 0. Quit " read -p "Enter selection [0-3] > " case "$REPLY" in 0) echo "Program terminated." exit ;; 1) echo "Hostname: $HOSTNAME" uptime ;; 2) df -h ;; 3) if [[ "$(id -u)" -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh "$HOME" fi ;; *) echo "Invalid entry" >&2 exit 1 ;; esac Команда case берет значение слова — в данном примере значение переменной REPLY — и затем сопоставляет его с указанными шаблонами. Найдя соответствие, она выполняет команды, связанные с найденным шаблоном. После нахождения соответствия сопоставление с нижележащими шаблонами уже не производится. Шаблоны Шаблоны обрабатываются командой case точно так же, как пути механизмом под- становки. Шаблоны завершаются символом ). В табл. 31.1 перечислены некото- рые допустимые шаблоны. Команда case 473 Таблица 31.1. Примеры шаблонов в команде case Шаблон Описание a) Соответствует, если слово содержит a [[:alpha:]]) Соответствует, если слово содержит единственный алфавитный символ ???) *.txt) *) Соответствует, если слово содержит ровно три символа Соответствует, если слово заканчивается символами .txt Соответствует любому значению слова. Считается хорошей практи- кой включать этот шаблон в команду case последним, чтобы пере- хватывать любые значения слова, не соответствующие ни одному из предыдущих шаблонов, то есть чтобы перехватывать любые недопу- стимые значения Следующий пример демонстрирует работу шаблонов: #!/bin/bash read -p "enter word > " case "$REPLY" in [[:alpha:]]) echo "is a single alphabetic character." ;; [ABC][0-9]) echo "is A, B, or C followed by a digit." ;; ???) echo "is three characters long." ;; *.txt) echo "is a word ending in '.txt'" ;; *) echo "is something else." ;; esac Мы можем объединить несколько шаблонов, перечислив их через символ верти- кальной черты. В результате получается комбинированный условный шаблон, объединенный по «ИЛИ». Эта возможность может пригодиться, например, для обработки символов верхнего и нижнего регистров: #!/bin/bash # case-menu: программа вывода системной информации, # управляемая с помощью меню clear echo " Please Select: A. Display System Information 474 Глава 31. Управление потоком выполнения: ветвление с помощью case B. Display Disk Space C. Display Home Space Utilization Q. Quit " read -p "Enter selection [A, B, C or Q] > " case "$REPLY" in q|Q) echo "Program terminated." exit ;; a|A) echo "Hostname: $HOSTNAME" uptime ;; b|B) df -h ;; c|C) if [[ "$(id -u)" -eq 0 ]]; then echo "Home Space Utilization (All Users)" du -sh /home/* else echo "Home Space Utilization ($USER)" du -sh "$HOME" fi ;; *) echo "Invalid entry" >&2 exit 1 ;; esac Здесь мы изменили программу case-menu, предложив пользователю выбирать пункты меню вводом букв, а не цифр. Обратите внимание, что новые шаблоны позволяют вводить буквы обоих регистров — верхнего и нижнего. Выполнение нескольких вариантов В версиях bash до 4.0 команда case могла выполнить только один вариант, соот- ветствующий совпавшему шаблону. После этого команда завершалась. Рассмо- трим сценарий, проверяющий введенный символ: #!/bin/bash # case4-1: проверка символа read -n 1 -p "Type a character > " echo case "$REPLY" in Команда case 475 [[:upper:]]) echo "'$REPLY' is upper case." ;; [[:lower:]]) echo "'$REPLY' is lower case." ;; [[:alpha:]]) echo "'$REPLY' is alphabetic." ;; [[:digit:]]) echo "'$REPLY' is a digit." ;; [[:graph:]]) echo "'$REPLY' is a visible character." ;; [[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;; [[:space:]]) echo "'$REPLY' is a whitespace character." ;; [[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;; esac Вот как выглядит результат выполнения этого сценария: [me@linuxbox ~]$ case4-1 Type a character > a 'a' is lower case. В большинстве случаев сценарий прекрасно справляется со своей задачей, но тер- пит неудачу, если символ соответствует нескольким символьным классам POSIX. Например, символ a соответствует классам алфавитных символов и символов нижнего регистра, а также шестнадцатеричных цифр. В bash до версии 4.0 не было никакой возможности заставить case выполнить больше одной успешной провер- ки. Современные версии bash поддерживают дополнительную нотацию ;;& в кон- це каждого варианта, которая используется, как показано ниже: #!/bin/bash # case4-2: проверка символа read -n 1 -p "Type a character > " echo case "$REPLY" in [[:upper:]]) echo "'$REPLY' is upper case." ;;& [[:lower:]]) echo "'$REPLY' is lower case." ;;& [[:alpha:]]) echo "'$REPLY' is alphabetic." ;;& [[:digit:]]) echo "'$REPLY' is a digit." ;;& [[:graph:]]) echo "'$REPLY' is a visible character." ;;& [[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;& [[:space:]]) echo "'$REPLY' is a whitespace character." ;;& [[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;& esac Запустив этот сценарий, мы получим: [me@linuxbox ~]$ case4-2 Type a character > a 'a' is lower case. 476 Глава 31. Управление потоком выполнения: ветвление с помощью case 'a' is alphabetic. 'a' is a visible character. 'a' is a hexadecimal digit. Дополнительный синтаксис ;;& позволяет команде case продолжить проверку вместо простого завершения после первого найденного совпадения. Заключение Команда case является удобным дополнением к нашей коллекции приемов про- граммирования. Как будет показано в следующей главе, она отлично подходит для решения некоторых видов задач. 32 Позиционные параметры Во всех предыдущих наших программах отсутствовала одна особенность — воз- можность принимать и обрабатывать параметры и аргументы командной строки. В этой главе мы исследуем эту возможность и позволим нашим программам об- ращаться к содержимому командной строки. Доступ к командной строке Командная оболочка поддерживает множество переменных, которые называются позиционными параметрами и содержат отдельные слова из командной строки. Эти переменные имеют имена от 0 до 9. Продемонстрируем их: #!/bin/bash # posit-param: сценарий для просмотра параметров командной строки echo " \$0 = $0 \$1 = $1 \$2 = $2 \$3 = $3 \$4 = $4 \$5 = $5 \$6 = $6 \$7 = $7 \$8 = $8 \$9 = $9 " 478 Глава 32. Позиционные параметры Этот очень простой сценарий выводит значения переменных с именами от $0 до $9. Запустим его без аргументов командной строки: [me@linuxbox ~]$ posit-param $0 = /home/me/bin/posit-param $1 = $2 = $3 = $4 = $5 = $6 = $7 = $8 = $9 = Даже в отсутствие аргументов переменная $0 всегда содержит первый элемент командной строки — путь к файлу выполняемой программы. Давайте передадим сценарию несколько аргументов: [me@linuxbox ~]$ posit-param a b c d $0 = /home/me/bin/posit-param $1 = a $2 = b $3 = c $4 = d $5 = $6 = $7 = $8 = $9 = ПРИМЕЧАНИЕ В действительности, если использовать механизм подстановки параметров, можно получить доступ более чем к девяти параметрам. Чтобы указать число больше девяти, следует заключить его в фигурные скобки; например, ${10}, ${55}, ${211} и т. д. Определение числа аргументов Командная оболочка поддерживает также переменную $#, хранящую число аргу- ментов командной строки: Доступ к командной строке 479 #!/bin/bash # posit-param: сценарий для просмотра параметров командной строки echo " Number of arguments: $# \$0 = $0 \$1 = $1 \$2 = $2 \$3 = $3 \$4 = $4 \$5 = $5 \$6 = $6 \$7 = $7 \$8 = $8 \$9 = $9 " Результат: [me@linuxbox ~]$ posit-param a b c d Number of arguments: 4 $0 = /home/me/bin/posit-param $1 = a $2 = b $3 = c $4 = d $5 = $6 = $7 = $8 = $9 = shift — доступ к множеству аргументов Но как быть, если программе передается большое число аргументов, как в следу- ющем примере: [me@linuxbox ~]$ posit-param * Number of arguments: 82 $0 = /home/me/bin/posit-param $1 = addresses.ldif $2 = bin $3 = bookmarks.html 480 Глава 32. Позиционные параметры $4 = debian-500-i386-netinst.iso $5 = debian-500-i386-netinst.jigdo $6 = debian-500-i386-netinst.template $7 = debian-cd_info.tar.gz $8 = Desktop $9 = dirlist-bin.txt В системе, где выполнялся этот пример, механизм подстановки развернул сим- вол * в 82 аргумента. Как обработать такое количество? Командная оболочка предусматривает решение и для подобных случаев, правда, следует отметить, что изяществом оно не отличается. Команда shift выполняет «сдвиг» параметров к началу списка. Фактически, используя shift, можно обойтись единственной переменной-параметром (помимо $0, которая никогда не изменяется). #!/bin/bash # posit-param2: сценарий вывода всех аргументов count=1 while [[ $# -gt 0 ]]; do echo "Argument $count = $1" count=$((count + 1)) shift done Каждый раз, когда выполняется команда shift, значение $2 перемещается в $1, значение $3 перемещается в $2 и т. д. Значение $# при этом уменьшается на 1. В программе posit-param2 мы создали цикл, проверяющий число оставшихся ар- гументов и продолжающийся до тех пор, пока оно не уменьшится до нуля. Цикл выводит текущий аргумент, в каждой итерации увеличивает счетчик обработан- ных аргументов count и, наконец, выполняет shift, чтобы загрузить в $1 следую- щий аргумент. Вот как работает эта программа: [me@linuxbox ~]$ posit-param2 a b c d Argument 1 = a Argument 2 = b Argument 3 = c Argument 4 = d Простые приложения Даже без команды shift можно писать полезные приложения, использующие по- зиционные параметры. Например, ниже приводится простая программа получе- ния информации о файле: Доступ к командной строке 481 #!/bin/bash # file_info: простая программа получения информации о файле PROGNAME="$(basename "$0")" if [[ -e "$1" ]]; then echo -e "\nFile Type:" file "$1" echo -e "\nFile Status:" stat "$1" else echo "$PROGNAME: usage: $PROGNAME file" >&2 exit 1 fi Эта программа выводит тип указанного файла (определяется с помощью команды file) и его атрибуты (командой stat). Интересной особенностью программы являет- ся переменная PROGNAME. Ей присваивается результат выполнения команды basename "$0". Команда basename удаляет начальную часть из пути к файлу, оставляя только базовое имя. В данном примере basename удалит начальную часть из параметра $0, хранящего полный путь к данной программе. Такой результат удобно использовать для конструирования сообщений, например, о правилах использования программы. При подобном подходе можно переименовать сценарий, и при выводе сообщений новое имя программы будет использоваться автоматически. Использование позиционных параметров в функциях Позиционные параметры используются для передачи аргументов не только в сце- нарии, но и в функции командной оболочки. Для демонстрации преобразуем сце- нарий file_info в функцию: file_info () { # file_info: функция для вывода информации о файле if [[ -e "$1" ]]; then echo -e "\nFile Type:" file "$1" echo -e "\nFile Status:" stat "$1" else echo "$FUNCNAME: usage: $FUNCNAME file" >&2 return 1 fi } 482 Глава 32. Позиционные параметры Теперь, если сценарий, включающий функцию file_info, вызовет ее с именем фай- ла в аргументе, аргумент будет передан в функцию. Благодаря этому мы получаем возможность написать множество полезных функ- ций для использования не только в наших сценариях, но и в файле .bashrc. Обратите внимание, что в этом примере вместо переменной PROGNAME использует- ся переменная командной оболочки FUNCNAME. Оболочка автоматически присваи- вает значение этой переменной в момент вызова функции. Отметьте также, что $0 всегда содержит полный путь к первому элементу командной строки (то есть имя программы), а не имя функции, как можно было бы ожидать. Обработка позиционных параметров скопом Иногда бывает необходимо выполнить операцию сразу со всеми позиционными параметрами. Например, может понадобиться написать «обертку» для некоторой программы, то есть сценарий или функцию, упрощающие запуск этой программы. Обертка принимает список непонятных для нее параметров командной строки и просто передает его обертываемой программе. Для этой цели командная оболочка предоставляет два специальных параметра. Они оба замещаются полным списком позиционных параметров, но имеют неко- торые тонкие отличия. Описание этих параметров приводится в табл. 32.1. Таблица 32.1. Специальные параметры $* и $@ Параметр Описание $* $@ Замещается списком позиционных параметров, начиная с $1. Если имя параметра $* заключить в двойные кавычки, позиционные параметры будут перечислены в списке через первый символ в переменной IFS (по умол- чанию пробел), а сам список будет размещен в одной строке и заключен в кавычки Замещается списком позиционных параметров, начиная с $1. Если имя пара- метра $@ заключить в двойные кавычки, механизм подстановки заменит его списком позиционных параметров, заключенных в кавычки по отдельности Следующий сценарий демонстрирует, как действуют эти специальные параметры: #!/bin/bash # posit-params3 : сценарий для демонстрации $* и $@ Обработка позиционных параметров скопом 483 print_params () { echo "\$1 = $1" echo "\$2 = $2" echo "\$3 = $3" echo "\$4 = $4" } pass_params () { echo -e "\n" '$* :'; print_params $* echo -e "\n" '"$*" :'; print_params "$*" echo -e "\n" '$@ :'; print_params $@ echo -e "\n" '"$@" :'; print_params "$@" } pass_params "word" "words with spaces" В этой довольно замысловатой программе мы создали два аргумента, word и words with spaces, и передали их функции pass_params. Эта функция, в свою очередь, передает их функции print_params, с применением каждого из четырех методов, доступных для специальных параметров $* и $@. Вывод сценария показывает раз- ницу между ними: [me@linuxbox ~]$ posit-param3 $* : $1 = word $2 = words $3 = with $4 = spaces "$*" : $1 = word words with spaces $2 = $3 = $4 = $@ : $1 = word $2 = words $3 = with $4 = spaces "$@" : $1 = word $2 = words with spaces $3 = $4 = 484 Глава 32. Позиционные параметры В данном примере оба параметра, $* и $@, возвращают результат из четырех слов: word words with spaces "$*" возвращает результат в виде одного слова, содержащего пробелы: "word words with spaces" "$@" возвращает результат в виде двух слов, второе из которых включает пробелы: "word" "words with spaces" Это соответствует нашим фактическим намерениям. Этот пример показывает, что, несмотря на наличие четырех разных способов получения списка позицион- ных параметров, в большинстве ситуаций предпочтительнее использовать прием с "$@", потому что он сохраняет целостность каждого позиционного параметра. Более сложное приложение После долгой паузы мы продолжим работу над программой sys_info_page. Теперь мы добавим в нее поддержку нескольких параметров командной строки: Выходной файл. Мы добавим параметр, который позволит указать имя файла для вывода результатов работы программы. Сделать это можно будет с помо- щью -f файл или --file файл. Интерактивный режим. При передаче этого параметра программа будет пред- лагать пользователю ввести имя выходного файла и определять, существует ли этот файл. Если файл существует, пользователю будет предложено подтвер- дить свое решение, прежде чем затереть существующий файл. Этот параметр можно будет передать как -i или --interactive. Справка. Передав параметр -h или --help, можно потребовать от программы вывести сообщение с информацией о правилах пользования программой. Далее приводится код, реализующий обработку командной строки: usage () { echo "$PROGNAME: usage: $PROGNAME [-f file | -i]" return } # обработка параметров командной строки interactive= filename= Более сложное приложение 485 while [[ -n "$1" ]]; do case "$1" in -f | --file) shift filename="$1" ;; -i | --interactive) interactive=1 ;; -h | --help) usage exit ;; *) usage >&2 exit 1 ;; esac shift done Сначала мы добавили функцию usage для вывода сообщения, если программа вы- зывается с параметром --help или с неизвестным параметром. Затем следует цикл обработки параметров. Цикл продолжается, пока позицион- ный параметр $1 не получит пустое значение. В конце цикла вызывается команда shift, чтобы сдвинуть позиционные параметры и, в конечном итоге, гарантиро- вать завершение цикла. Внутри цикла инструкция case проверяет текущий позиционный параметр на со- ответствие поддерживаемым вариантам. Если данный параметр поддерживается, выполняется соответствующая операция, если нет — выводится сообщение с ин- формацией о правилах пользования программой и сценарий завершается с при- знаком ошибки. Обратите внимание, как обрабатывается параметр -f. Обнаружив этот параметр, программа выполняет команду shift, которая сдвинет аргумент параметра -f с именем файла в позиционный параметр $1. Далее следует код, реализующий интерактивный режим: # интерактивный режим if [[ -n "$interactive" ]]; then while true; do read -p "Enter name of output file: " filename if [[ -e "$filename" ]]; then read -p "'$filename' exists. Overwrite? [y/n/q] > " case "$REPLY" in Y|y) break ;; 486 Глава 32. Позиционные параметры Q|q) echo "Program terminated." exit ;; *) continue ;; esac elif [[ -z "$filename" ]]; then continue else break fi done fi Если переменная interactive содержит непустое значение, начинается бесконеч- ный цикл, который предлагает ввести имя файла и затем обрабатывает ситуацию, если введенное имя соответствует существующему файлу. Если указанный файл уже существует, пользователю на выбор предлагается три варианта: затереть су- ществующий файл, выбрать другое имя или завершить программу. Если поль- зователь предпочтет затереть существующий файл, выполняется команда break и цикл прерывается. Обратите внимание, что инструкция case различает только вариант перезаписи существующего файла и завершения программы. Любой дру- гой ответ пользователя будет приводить к переходу в начало цикла с повторным предложением ввести имя файла. Для поддержки вывода в файл сначала необходимо имеющийся код вывода стра- ницы преобразовать в функцию. Необходимость такого решения станет понятна чуть позже: write_html_page () { cat $TITLE$TITLE
$TIMESTAMP $(report_uptime) $(report_disk_space) $(report_home_space) _EOF_ return } Заключение 487 # вывод страницы html if [[ -n "$filename" ]]; then if touch "$filename" && [[ -f "$filename" ]]; then write_html_page > "$filename" else echo "$PROGNAME: Cannot write file '$filename'" >&2 exit 1 fi else write_html_page fi Код, обслуживающий логику параметра -f, находится в конце листинга, приве- денного выше. Он проверяет, определено ли имя файла и затем — доступность для записи файла с указанным именем. Для этого выполняется команда touch с последующей проверкой, что файл является обычным файлом. Эти две провер- ки позволяют обработать ситуацию неправильно указанного пути (в этом случае touch потерпит неудачу) и убедиться, что существующий файл является обычным файлом. Как видите, функция write_html_page вызывается, чтобы сгенерировать факти- ческое содержимое страницы, которое затем либо выводится в стандартный вы- вод (если переменная filename содержит пустое значение), либо перенаправляется в указанный файл. Поскольку код HTML может выводиться в файл или в стан- дартный вывод, есть смысл преобразовать процедуру write_html_page в функцию, чтобы избежать повторения кода. Заключение С помощью дополнительных позиционных параметров мы можем теперь писать довольно функциональные сценарии. Позиционные параметры помогают созда- вать очень полезные функции командной оболочки для выполнения повседнев- ных задач, которые можно поместить в файл .bashrc. Наша программа sys_info_page выросла и усложнилась. Ниже приводится пол- ный листинг программы с выделенными последними изменениями: #!/bin/bash # sys_info_page: программа вывода страницы с информацией о системе PROGNAME="$(basename $0)" TITLE="System Information Report For $HOSTNAME" 488 Глава 32. Позиционные параметры CURRENT_TIME=$(date +"%x %r %Z") TIME_STAMP="Generated $CURRENT_TIME, by $USER" report_uptime () { cat System Uptime
$(uptime) _EOF_ return } report_disk_space () { cat Disk Space Utilization$(df -h) _EOF_ return } report_home_space () { if [[ "$(id -u)" -eq 0 ]]; then cat Home Space Utilization (All Users)$(du -sh /home/*) _EOF_ else cat Home Space Utilization ($USER)$(du -sh "$HOME") _EOF_ fi return } usage () { echo "$PROGNAME: usage: $PROGNAME [-f file | -i]" return } write_html_page () { cat $TITLE$TITLE Заключение 489
$TIMESTAMP $(report_uptime) $(report_disk_space) $(report_home_space) _EOF_ return } # обработка параметров командной строки interactive= filename= while [[ -n "$1" ]]; do case "$1" in -f | --file) shift filename="$1" ;; -i | --interactive) interactive=1 ;; -h | --help) usage exit ;; *) usage >&2 exit 1 ;; esac shift done # интерактивный режим if [[ -n "$interactive" ]]; then while true; do read -p "Enter name of output file: " filename if [[ -e "$filename" ]]; then read -p "'$filename' exists. Overwrite? [y/n/q] > " case "$REPLY" in Y|y) break ;; Q|q) echo "Program terminated." exit ;; *) continue ;; esac 490 Глава 32. Позиционные параметры fi done fi # вывод страницы html if [[ -n "$filename" ]]; then if touch "$filename" && [[ -f "$filename" ]]; then write_html_page > "$filename" else echo "$PROGNAME: Cannot write file '$filename'" >&2 exit 1 fi else write_html_page fi У нас уже получился неплохой сценарий, но он еще не закончен. В следующей главе мы добавим в него последнее улучшение. 33 Управление потоком выполнения: цикл for В этой заключительной главе, посвященной управлению потоком выполнения, мы познакомимся еще с одной конструкцией организации циклов в командной обо- лочке. Цикл for отличается от циклов while и until поддержкой средств обработки последовательностей. Это очень полезная возможность. Как следствие, цикл for пользуется большой популярностью среди создателей сценариев для bash. Цикл for реализован, что вполне естественно, в виде команды for. В современных версиях bash поддерживается две формы команды for. for: традиционная форма Оригинальный синтаксис команды for имеет следующий вид: for переменная [in слова]; do команды done где переменная — это имя переменной, значение которой будет увеличиваться в ходе выполнения цикла, слова — необязательный список элементов, которые последовательно будут присваиваться переменной, и команды — это команды, вы- полняемые в каждой итерации. Команду for удобно использовать в командной строке. Рассмотрим, как она ра- ботает: [me@linuxbox ~]$ for i in A B C D; do echo $i; done A B C D 492 Глава 33. Управление потоком выполнения: цикл for В этом примере команда for получает список из четырех слов: A, B, C и D. Для об- хода этого списка выполняется четыре итерации цикла. В начале каждой итерации переменной i присваивается очередное слово. Внутри цикла находится команда echo, она выводит значение i, чтобы показать, что присваивание действительно выполняется. Так же как в случае с циклами while и until, цикл for заканчивается ключевым словом done. По-настоящему мощной особенностью for является разнообразие способов фор- мирования списка слов. Например, можно использовать подстановку в фигурных скобках: [me@linuxbox ~]$ for i in {A..D}; do echo $i; done A B C D или подстановку имен файлов: [me@linuxbox ~]$ for i in distros*.txt; do echo "$i"; done distros-by-date.txt distros-dates.txt distros-key-names.txt distros-key-vernums.txt distros-names.txt distros.txt distros-vernums.txt distros-versions.txt Механизм подстановки имен файлов позволяет получить четкий список с име- нами файлов, который можно обработать в цикле. Единственное, о чем следует позаботиться, — убедиться, что механизм подстановки вернул действительные со- впадения. По умолчанию, если операция подстановки не найдет ни одного файла, соответствующего шаблону, она вернет сам шаблон (в данном случае distros*. txt). Чтобы защититься от этой проблемы, предыдущий код можно преобразовать в сценарий, как показано ниже: for i in distros*.txt; do if [[ -e "$i" ]]; then echo "$i" fi done Добавив проверку существования файла, мы обезопасили себя от ситуации, когда механизм подстановки не нашел ни одного файла. Другой распространенный ме- тод получения списка слов — подстановка команд. for: традиционная форма 493 #!/bin/bash # longest-word : поиск самой длинной строки в файле while [[ -n "$1" ]]; do if [[ -r "$1" ]]; then max_word= max_len=0 for i in $(strings "$1"); do len="$(echo "$i" | wc -c)" if (( len > max_len )); then max_len="$len" max_word="$i" fi done echo "$1: '$max_word' ($max_len characters)" fi shift done Этот пример осуществляет поиск самой длинной строки в файле. Когда в ко- мандной строке указано несколько имен файлов, сценарий вызывает процедуру strings (входит в состав пакета GNU binutils), чтобы получить список «слов» из каждого файла. Цикл for обрабатывает каждое слово по очереди и определяет, является ли оно самым длинным из встречавшихся до сих пор. По завершении цикла сценарий выводит самое длинное слово. Обратите внимание, что здесь, вопреки обычной практике, мы не заключили под- становку команд $(strings "$1") в кавычки. Объясняется это просто: здесь мы действительно хотим разбить строку на слова, чтобы получить список. Заключив подстановку команд в кавычки, мы получили бы одно слово, содержащее все стро- ки из файла. А это не совсем то, что нам нужно. Если необязательный компонент слова в команде for отсутствует, она по умолча- нию обрабатывает позиционные параметры. Чтобы показать использование этого способа, изменим сценарий longest-word: #!/bin/bash # longest-word2 : поиск самой длинной строки в файле for i; do if [[ -r "$i" ]]; then max_word= max_len=0 for j in $(strings "$i"); do 494 Глава 33. Управление потоком выполнения: цикл for len="$(echo "$j" | wc -c)" if (( len > max_len )); then max_len="$len" max_word="$j" fi done echo "$i: '$max_word' ($max_len characters)" fi done Как видите, мы заменили внешний цикл while циклом for. Так как список слов в команде for отсутствует, она перебирает позиционные параметры. Во внутрен- нем цикле вместо переменной i теперь используется переменная j. Кроме того, нам больше не нужна команда shift. ПОЧЕМУ I? Вы могли заметить, что во всех примерах цикла for выше использовалась пере- менная i. Почему? В действительности за этим выбором не стоят какие-то опре- деленные причины, кроме стремления следовать традициям. В команде for можно использовать любую допустимую переменную, но чаще всего используется пере- менная i, а также j и k. Своими корнями эта традиция уходит в язык программирования Fortran. В Fortran необъявленные переменные, начинающиеся с букв I, J, K, L и M, автоматически становились целочисленными, тогда как переменные, начинающиеся с любой другой буквы, — действительными, или вещественными (способными хранить числа с дробной частью). Эта особенность вынуждала программистов использо- вать переменные I, J и K в качестве переменных цикла, так как использование их в качестве временных переменных (чем переменные цикла в действительности и являются) требовало меньших усилий. Из-за этого даже в среде программистов на Fortran ходила острота: «GOD is real, unless declared integer» (Бог действителен, пока явно не объявлен целым). for: форма в стиле языка C В последние версии bash добавлена вторая форма синтаксиса команды for, напо- минающая одноименный оператор в языке программирования C, которая поддер- живается также многими другими языками. for: форма в стиле языка C 495 for (( выражение1; выражение2; выражение3 )); do команды done где выражение1, выражение2 и выражение3 — это арифметические выражения, а коман ды — это команды, выполняемые в каждой итерации цикла. Своим поведением эта форма эквивалентна следующей конструкции: (( выражение1 )) while (( выражение2 )); do команды (( выражение3 )) done выражение1 инициализирует цикл, выражение2 определяет условие завершения цикла, выражение3 выполняется в конце каждой итерации. Ниже приводится пример типичного применения: #!/bin/bash # simple_counter : демонстрация команды for в стиле языка C for (( i=0; iHome Space Utilization (All Users)
$(du -sh /home/*) _EOF_ else cat Home Space Utilization ($USER)$(du -sh "$HOME") _EOF_ fi return } Теперь мы можем переписать ее, добавив вывод информации о домашнем катало- ге каждого пользователя и включив в вывод общее число файлов и подкаталогов в каждом из них: report_home_space () { local format="%8s%10s%10s\n" local i dir_list total_files total_dirs total_size user_name if [[ "$(id -u)" -eq 0 ]]; then dir_list=/home/* user_name="All Users" else dir_list="$HOME" user_name="$USER" fi echo "Home Space Utilization ($user_name)" for i in $dir_list; do total_files="$(find "$i" -type f | wc -l)" total_dirs="$(find "$i" -type d | wc -l)" total_size="$(du -sh "$i" | cut -f 1)" Заключение 497 echo "
$i" echo "
" printf "$format" "Dirs" "Files" "Size" printf "$format" "----" "-----" "----" printf "$format" "$total_dirs" "$total_files" "$total_size" echo "" done return } В этой новой версии применено многое из того, что мы узнали к данному моменту. Она все еще проверяет наличие привилегий суперпользователя, но вместо того, чтобы выполнить полный набор операций в каждой из ветвей if, здесь устанавли- ваются некоторые переменные, которые затем используются в цикле for. В функ- ции использованы несколько локальных переменных и команда printf для фор- матирования части вывода. 34 Строки и числа Любые компьютерные программы обрабатывают данные. В предыдущих главах основное внимание уделялось обработке данных на уровне файлов. Однако мно- гие задачи решаются с использованием меньших единиц данных, таких как строки и числа. В этой главе мы рассмотрим некоторые возможности командной оболочки для работы со строками и числами. Командная оболочка поддерживает большое раз- нообразие способов подстановки параметров, которые выполняют строковые операции. В дополнение к подстановке результатов арифметических выражений (о которой рассказывалось в главе 7) существует программа командной строки bc, выполняющая математические операции. Подстановка параметров Механизм подстановки параметров уже рассматривался в главе 7, но там этот ме- ханизм не был описан детально, потому что большая часть его возможностей ис- пользуется в сценариях, а не в командной строке. Мы уже знакомы с некоторыми формами подстановки параметров, например, с подстановкой значений перемен- ных командной оболочки. Но в командной оболочке их намного больше. ПРИМЕЧАНИЕ Старайтесь всегда заключать операции подстановки параметров в двойные ка- вычки, чтобы предотвратить нежелательное разделение строк на слова, если для этого нет веских причин. Это особенно актуально при работе с именами файлов, потому что они могут включать пробелы и другие неожиданные символы. Подстановка параметров 499 Простые параметры Простейшую форму подстановки параметров можно наблюдать в использовании переменных. Например, запись $a после подстановки превращается в содержимое переменной a. Простые параме- тры можно заключать в фигурные скобки, например: ${a} Это не оказывает влияния на результат подстановки, но является необходимым, если сразу за именем переменной следует какой-то другой текст, который может сбивать с толку командную оболочку. В следующем примере выполняется попыт- ка сконструировать имя файла добавлением строки _file к содержимому перемен- ной a: [me@linuxbox ~]$ a="foo" [me@linuxbox ~]$ echo "$a_file" Если выполнить эту последовательность команд, результатом будет пустое значе- ние, потому что командная оболочка попытается выполнить подстановку значе- ния переменной a_file вместо a. Эта проблема устраняется с помощью фигурных скобок: [me@linuxbox ~]$ echo "${a}_file" foo_file Мы видели также, что доступ к позиционным параметрам с порядковыми номе- рами выше 9 тоже осуществляется с помощью фигурных скобок. Например, про- читать 11-й позиционный параметр можно следующим образом: ${11} Подстановка пустых переменных Некоторые формы подстановки параметров помогают решать проблемы с несу- ществующими, или пустыми, переменными. Эти формы удобно использовать для обработки ситуаций отсутствия позиционных параметров и назначения им значе- ний по умолчанию. Ниже приводится пример такой подстановки: ${параметр:-слово} 500 Глава 34. Строки и числа Если параметр не определен (то есть отсутствует) или содержит пустое значение, механизм подстановки вернет значение указанного слова. Если параметр не пу- стой, механизм подстановки вернет значение параметра. [me@linuxbox ~]$ foo= [me@linuxbox ~]$ echo ${foo:-"substitute value if unset"} substitute value if unset [me@linuxbox ~]$ echo $foo [me@linuxbox ~]$ foo=bar [me@linuxbox ~]$ echo ${foo:-"substitute value if unset"} bar [me@linuxbox ~]$ echo $foo bar Вот еще один вариант подстановки, где вместо дефиса используется знак «равно»: ${параметр:=слово} Если параметр не определен или содержит пустое значение, механизм подстанов- ки вернет значение указанного слова и дополнительно присвоит его параметру. Если параметр не пустой, механизм подстановки вернет значение параметра. [me@linuxbox ~]$ foo= [me@linuxbox ~]$ echo ${foo:="default value if unset"} default value if unset [me@linuxbox ~]$ echo $foo default value if unset [me@linuxbox ~]$ foo=bar [me@linuxbox ~]$ echo ${foo:="default value if unset"} bar [me@linuxbox ~]$ echo $foo bar ПРИМЕЧАНИЕ Таким способом нельзя присваивать значения позиционным и другим специ- альным параметрам. Ниже демонстрируется форма со знаком вопроса: ${параметр:?слово} Если параметр не определен или содержит пустое значение, механизм под- становки завершит сценарий с ошибкой и выведет значение указанного слова Подстановка параметров 501 в стандартный вывод ошибок. Если параметр не пустой, механизм подстановки вернет значение параметра. [me@linuxbox ~]$ foo= [me@linuxbox ~]$ echo ${foo:?"parameter is empty"} bash: foo: parameter is empty [me@linuxbox ~]$ echo $? 1 [me@linuxbox ~]$ foo=bar [me@linuxbox ~]$ echo ${foo:?"parameter is empty"} bar [me@linuxbox ~]$ echo $? 0 Ниже демонстрируется форма со знаком «плюс»: ${параметр:+слово} Если параметр не определен или содержит пустое значение, механизм подстанов- ки вернет пустое значение. Если параметр не пустой, механизм подстановки вер- нет значение слова, но сам параметр не изменится. [me@linuxbox ~]$ foo= [me@linuxbox ~]$ echo ${foo:+"substitute value if set"} [me@linuxbox ~]$ foo=bar [me@linuxbox ~]$ echo ${foo:+"substitute value if set"} substitute value if set Получение имен переменных Командная оболочка может возвращать имена переменных. Это используется в некоторых экзотических ситуациях. ${!префикс*} ${!префикс@} Эти две формы подстановки возвращают имена существующих переменных, на- чинающиеся с указанного префикса. Согласно документации bash, обе формы действуют совершенно одинаково. Следующая команда выводит список всех пе- ременных окружения с именами, начинающимися с BASH: [me@linuxbox ~]$ echo ${!BASH*} BASH BASH_ARGC BASH_ARGV BASH_COMMAND BASH_COMPLETION BASH_COMPLETION_DIR BASH_ LINENO BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION 502 Глава 34. Строки и числа Операции со строками Существует множество форм подстановки, которые можно использовать для ра- боты со строками. Многие из них особенно хорошо подходят для операций с пу- тями. Форма ${#параметр} вернет длину строки, содержащуюся в указанном параметре. Обычно роль пара- метра играет строка, но если передать @ или *, то механизм подстановки вернет число позиционных параметров. [me@linuxbox ~]$ foo="This string is long." [me@linuxbox ~]$ echo "'$foo' is ${#foo} characters long." 'This string is long.' is 20 characters long. Следующая форма подстановки: ${параметр:смещение} ${параметр:смещение:длина} используется для извлечения фрагмента строки, содержащейся в параметре. Из- влечение начинается с указанного смещения от начала строки и продолжается до конца строки, если не указана длина. [me@linuxbox ~]$ foo="This string is long." [me@linuxbox ~]$ echo ${foo:5} string is long. [me@linuxbox ~]$ echo ${foo:5:6} string Если указать отрицательное смещение, его отсчет начнется с конца строки вместо начала. Обратите внимание, что отрицательному значению должен предшество- вать пробел, чтобы предотвратить путаницу с формой ${параметр:-слово}. Длина, если указана, в этом случае не должна быть меньше 0. Если в качестве параметра передать @, результатом подстановки будет длина по- зиционных параметров, начиная с указанного смещения. [me@linuxbox ~]$ foo="This string is long." [me@linuxbox ~]$ echo ${foo: -5} long. [me@linuxbox ~]$ echo ${foo: -5:2} lo Подстановка параметров 503 Следующие две формы: ${параметр#шаблон} ${параметр##шаблон} возвращают значение параметра, удаляя из него начальную часть, определяемую указанным шаблоном. В шаблоне допускается использовать групповые символы: например, те, что ис- пользуются в подстановке путей. Эти две формы отличаются тем, что форма # удаляет кратчайшее совпадение, тогда как форма ## удаляет самое длинное со- впадение. [me@linuxbox ~]$ foo=file.txt.zip [me@linuxbox ~]$ echo ${foo#*.} txt.zip [me@linuxbox ~]$ echo ${foo##*.} zip Следующие две формы: ${параметр%шаблон} ${параметр%%шаблон} действуют так же, как формы # и ##, представленные выше, но удаляют текст с конца строки, содержащейся в параметре. [me@linuxbox ~]$ foo=file.txt.zip [me@linuxbox ~]$ echo ${foo%.*} file.txt [me@linuxbox ~]$ echo ${foo%%.*} file Следующие формы: ${параметр/шаблон/строка} ${параметр//шаблон/строка} ${параметр/#шаблон/строка} ${параметр/%шаблон/строка} выполняют поиск с заменой в содержимом указанного параметра. Если в пара- метре будет найдено совпадение с шаблоном, который может содержать груп- повые символы, это совпадение будет заменено содержимым указанной строки. Первая форма заменит только первое совпадение с шаблоном. Форма // заменит 504 Глава 34. Строки и числа все найденные совпадения. Форма /# выполняет замену, только если совпадение с шаблоном найдено в самом начале строки, а форма /% выполняет замену, только если совпадение найдено в конце строки. Часть /строка можно опустить, и тогда совпавший фрагмент будет удален. [me@linuxbox ~]$ foo=JPG.JPG [me@linuxbox ~]$ echo ${foo/JPG/jpg} jpg.JPG [me@linuxbox ~]$ echo ${foo//JPG/jpg} jpg.jpg [me@linuxbox ~]$ echo ${foo/#JPG/jpg} jpg.JPG [me@linuxbox ~]$ echo ${foo/%JPG/jpg} JPG.jpg Механизм подстановки параметров — ценный инструмент. Его возможности для работы со строками можно использовать вместо других широко используемых команд, таких как sed и cut. Применение механизма подстановки способствует увеличению производительности сценария за счет отсутствия необходимости выполнять внешние программы. Например, изменим программу longest-word из предыдущей главы, задействовав подстановку параметра ${#j} взамен под- становки команды $(echo $j | wc -c), которая к тому же выполняется в подобо- лочке: #!/bin/bash # longest-word3 : поиск самой длинной строки в файле for i; do if [[ -r "$i" ]]; then max_word= max_len= for j in $(strings $i); do len="${#j}" if (( len > max_len )); then max_len="$len" max_word="$j" fi done echo "$i: '$max_word' ($max_len characters)" fi shift done Подстановка параметров 505 Далее, сравним эффективность двух версий с помощью команды time: [me@linuxbox ~]$ time longest-word2 dirlist-usr-bin.txt dirlist-usr-bin.txt: 'scrollkeeper-get-extended-content-list' (38 characters) real 0m3.618s user 0m1.544s sys 0m1.768s [me@linuxbox ~]$ time longest-word3 dirlist-usr-bin.txt dirlist-usr-bin.txt: 'scrollkeeper-get-extended-content-list' (38 characters) real 0m0.060s user 0m0.056s sys 0m0.008s Первоначальной версии потребовалось 3,618 секунды, чтобы просканировать текстовый файл, тогда как новой версии, использующей механизм подстановки параметров, понадобилось всего 0,06 секунды — весьма существенное улучшение. Преобразование регистра символов bash поддерживает четыре подстановки параметров и два варианта команды declare для преобразования регистра символов в строках. Где может пригодиться возможность преобразования регистра символов? Поми- мо очевидной эстетической ценности, она играет важную роль в программиро- вании. Рассмотрим случай поиска в базе данных. Представьте, что пользователь ввел строку и мы должны найти ее в базе данных. Пользователь может ввести зна- чение только заглавными или только строчными буквами или их комбинацией. Разумеется, мы не можем позволить себе хранить в базе данных все возможные варианты написания всех строк с заглавными и строчными буквами. Как же быть? Подобные проблемы часто решаются путем нормализации пользовательского вво- да. То есть его преобразованием в стандартную форму перед поиском в базе дан- ных. Для этого можно преобразовать все символы во вводе пользователя в ниж- ний или верхний регистр и аналогичным образом нормализовать все записи в базе данных. Для нормализации строк — приведения их к верхнему или нижнему регистру — можно использовать команду declare. C помощью declare мы сможем гаранти- ровать, что переменная всегда будет содержать строки в желаемом формате, неза- висимо от их первоначального вида. 506 Глава 34. Строки и числа #!/bin/bash # ul-declare: демонстрация преобразования регистра символов с использованием declare declare -u upper declare -l lower if [[ $1 ]]; then upper="$1" lower="$1" echo "$upper" echo "$lower" fi В предыдущем сценарии мы использовали declare для создания двух перемен- ных, upper и lower. Затем мы присвоили им значение первого аргумента команд- ной строки (позиционного параметра $1) и вывели их на экран. [me@linuxbox ~]$ ul-declare aBc ABC abc Как видите, аргумент командной строки (aBc) был нормализован. Кроме команды declare поддерживаются также четыре подстановки параметров, выполняющие преобразование символов в верхний/нижний регистр. Они пере- числены в табл. 34.1. Таблица 34.1. Операции подстановки параметров, выполняющие преобразование регистра символов Формат Результат ${параметр,,шаблон} Возвращает значение указанного параметра после преобра- зования всех символов в нижний регистр. Необязательный шаблон можно использовать для ограничения символов, подлежащих преобразованию (например [A-F]). Подробное описание шаблонов можно найти в странице справочного руководства (man) для команды bash ${параметр,шаблон} Возвращает значение указанного параметра после преобра- зования в нижний регистр только первого символа ${параметр^^шаблон} Возвращает значение указанного параметра после преоб- разования всех символов в верхний регистр ${параметр^шаблон} Возвращает значение указанного параметра после преобра- зования в верхний регистр только первого символа Подстановка параметров 507 Работу этих операций подстановки демонстрирует следующий сценарий: #!/bin/bash # ul-param: демонстрация преобразования регистра символов с использованием подстановки параметров if [[ "$1" ]]; then echo "${1,,}" echo "${1,}" echo "${1^^}" echo "${1^}" fi Результат выполнения этого сценария: [me@linuxbox ~]$ ul-param aBc abc aBc ABC ABc Здесь мы снова обрабатываем первый аргумент командной строки и выводим результаты выполнения четырех поддерживаемых вариантов подстановки пара- метров. Сценарий использует первый позиционный параметр, но вообще в опе- рацию подстановки можно передать любую строку, переменную или строковое выражение. Вычисление и подстановка арифметических выражений В главе 7 мы видели, как работает механизм подстановки результатов арифме- тических выражений. Он используется для выполнения разных арифметических операций с целыми числами. Ниже приводится его базовый синтаксис $((выражение)) где выражение — это любое допустимое арифметическое выражение. Этот вид подстановки тесно связан с составной командой (( )), использовавшей- ся в главе 27 для вычисления арифметических выражений (оценки истинности). В предыдущих главах мы видели некоторые наиболее типичные выражения и опе- раторы, а здесь рассмотрим более полный их список. 508 Глава 34. Строки и числа Основание системы счисления В главе 9 мы познакомились с восьмеричными (в системе счисления с основани- ем 8) и шестнадцатеричными (в системе счисления с основанием 16) числами. В арифметических выражениях командная оболочка позволяет использовать це- лочисленные константы в системах счисления с любым основанием. В табл. 34.2 показаны формы записи чисел с указанием основания системы счисления. Таблица 34.2. Определение основания системы счисления Форма записи Описание Число 0число По умолчанию числа без упоминания системы счисления ин- терпретируются как десятичные числа (в системе счисления с основанием 10) В арифметических выражениях числа, начинающиеся с нуля, интерпретируются как восьмеричные (в системе счисления с основанием 8) 0xчисло Форма записи шестнадцатеричных чисел основание#число Число в системе счисления с указанным основанием Несколько примеров: [me@linuxbox ~]$ echo $((0xff)) 255 [me@linuxbox ~]$ echo $((2#11111111)) 255 В этих примерах выводится значение шестнадцатеричного числа ff (наиболь- шее двухзначное число) и наибольшее восьмизначное двоичное число (в системе счисления с основанием 2). Унарные операторы Оболочка поддерживает два унарных оператора, + и -, используемых для обозна- чения положительных и отрицательных чисел соответственно. Простая арифметика В табл. 34.3 перечислены обычные арифметические операторы. Подстановка параметров 509 Таблица 34.3. Арифметические операторы Оператор Описание + - * / ** % Сложение Вычитание Умножение Целочисленное деление Степень числа Деление по модулю (остаток от целочисленного деления) Действия большинства из перечисленных операторов не вызывают вопросов, кро- ме целочисленного деления и деления по модулю, которые требуют дополнитель- ного рассмотрения. Поскольку оболочка поддерживает только арифметические операции с целыми числами, результатом деления всегда будет целое число: [me@linuxbox ~]$ echo $(( 5 / 2 )) 2 Это обстоятельство увеличивает важность операции определения остатка от де- ления: [me@linuxbox ~]$ echo $(( 5 % 2 )) 1 Используя операторы деления и деления по модулю, можно определить, что деле- ние 5 на 2 дает в результате 2 с остатком 1. Вычисление остатка от деления удобно использовать в циклах. Это позволяет вы- полнять в цикле определенные операции с заданным интервалом. В примере ниже выводится строка чисел, в которой выделяются числа, кратные 5: #!/bin/bash # modulo : демонстрация оператора деления по модулю for ((i = 0; i " "$i" else 510 Глава 34. Строки и числа printf "%d " "$i" fi done printf "\n" Запустив этот сценарий, вы получите следующий результат: [me@linuxbox ~]$ modulo 1 2 3 4 6 7 8 9 11 12 13 14 16 17 18 19 Присваивание Хотя на данном этапе это не очевидно, тем не менее арифметические выражения могут выполнять операцию присваивания. Мы уже выполняли присваивание много раз, хотя и в других контекстах. Каждый раз, передавая переменной число, мы выполняем присваивание. То же самое можно делать в арифметических вы- ражениях: [me@linuxbox ~]$ foo= [me@linuxbox ~]$ echo $foo [me@linuxbox ~]$ if (( foo = 5 ));then echo "It is true."; fi It is true. [me@linuxbox ~]$ echo $foo 5 В примере выше мы сначала присвоили переменной foo пустое значение и прове- рили, что она действительно получила пустое значение. Далее выполнили коман- ду if с составной командой (( foo = 5 )). Эта команда имеет два интересных аспек- та: (1) она присваивает значение 5 переменной foo и (2) оценивает ее значение как истинное, потому что присваивание прошло успешно. ПРИМЕЧАНИЕ Важно запомнить значение оператора = в примере выше. Одиночный знак = выполняет присваивание: выражение foo = 5 говорит: «Сделать значение пере- менной foo равным 5». Двойной знак == определяет эквивалентность: выражение foo == 5 говорит: «Переменная foo равна 5?» Это обстоятельство может вызывать путаницу, потому что команда test интерпретирует одиночный знак = как опе- ратор сравнения строк. Это еще одна причина предпочесть более современные составные команды [[ ]] и (( )) вместо test. В дополнение к оператору = командная оболочка поддерживает еще несколько очень полезных операторов присваивания, перечисленных в табл. 34.4. Подстановка параметров 511 Таблица 34.4. Операторы присваивания Форма записи Описание параметр = значение Простое присваивание. Присваивает указанное значение указанному параметру параметр += значение Присваивание со сложением. Эквивалентно выражению параметр = параметр + значение параметр -= значение Присваивание с вычитанием. Эквивалентно выражению параметр = параметр - значение параметр *= значение Присваивание с умножением. Эквивалентно выражению параметр = параметр × значение параметр /= значение Присваивание с целочисленным делением. Эквивалентно выражению параметр = параметр ÷ значение параметр %= значение Присваивание с делением по модулю. Эквивалентно выра- жению параметр = параметр % значение параметр++ параметр-- ++параметр --параметр Постинкремент переменной. Эквивалентно выражению па- раметр = параметр + 1. (Но см. пояснение ниже.) Постдекремент переменной. Эквивалентно выражению па- раметр = параметр - 1 Преинкремент переменной. Эквивалентно выражению пара- метр = параметр + 1 Предекремент переменной. Эквивалентно выражению пара- метр = параметр - 1 Эти операторы присваивания обеспечивают удобный и компактный способ запи си многих арифметических вычислений. Особый интерес представляют операторы инкремента (++) и декремента (--), они увеличивают или уменьша- ют значение своего параметра на 1. Эти операторы заимствованы из языка про- граммирования C и внедрены в несколько других языков программирования, включая bash. Операторы инкремента и декремента могут находиться перед параметром или после него. Хотя в обоих случаях они увеличивают или уменьшают значение параметра на 1, тем не менее их местоположение играет важную роль. Если опе- ратор помещается перед параметром, сначала выполняется операция инкремен- та (или декремента) и только потом возвращается измененное значение пара- метра. Если оператор помещается за параметром, операция выполняется после 512 Глава 34. Строки и числа возврата значения. Такое поведение может показаться странным, но оно реализо- вано с умыслом. Взгляните на следующий пример: [me@linuxbox ~]$ foo=1 [me@linuxbox ~]$ echo $((foo++)) 1 [me@linuxbox ~]$ echo $foo 2 Если присвоить переменной foo значение 1 и затем увеличить ее значение с помо- щью оператора ++, следующего за именем переменной, выражение вернет прежнее значение 1 переменной foo. Однако если вывести значение переменной второй раз, мы увидим увеличенное значение. Если поместить оператор ++ перед параме- тром, мы получим более ожидаемый результат: [me@linuxbox ~]$ foo=1 [me@linuxbox ~]$ echo $((++foo)) 2 [me@linuxbox ~]$ echo $foo 2 Для большинства приложений на языке командной оболочки более полезным бу- дет префиксный оператор. Операторы ++ и -- часто используются совместно с циклами. Внесем некоторые улучшения в сценарий, демонстрирующий применение оператора деления по мо- дулю, чтобы немного сократить его: #!/bin/bash # modulo2 : демонстрация оператора деления по модулю for ((i = 0; i " "$i" else printf "%d " "$i" fi done printf "\n" Битовые операции Командной оболочкой поддерживается класс операторов, которые манипулируют числами не совсем обычным способом. Эти операторы действуют на уровне битов. Подстановка параметров 513 Они применяются для выполнения некоторых низкоуровневых операций, часто связанных с установкой или чтением битовых флагов. Описание битовых опера- торов приводится в табл. 34.5. Таблица 34.5. Битовые операторы Оператор Описание ~ > & | ^ Поразрядное отрицание. Изменяет значения всех битов в числе на противо- положные Поразрядный сдвиг влево. Сдвигает все биты в числе на один разряд влево Поразрядный сдвиг вправо. Сдвигает все биты в числе на один разряд вправо Поразрядная операция И (AND). Выполняет операцию И над всеми битами двух чисел Поразрядная операция ИЛИ (OR). Выполняет операцию ИЛИ над всеми битами двух чисел Поразрядная операция ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR). Выполняет операцию ИСКЛЮЧАЮЩЕЕ ИЛИ над всеми битами двух чисел Обратите внимание, что для всех битовых операторов, кроме поразрядного отри- цания, существуют соответствующие операторы присваивания (например, = == != && || Меньше или равно Больше или равно Меньше Больше Равно Не равно Логическое И (AND) Логическое ИЛИ (OR) выражение1?выражение2: выражение3 Тернарный (трехместный) оператор сравнения. Если вы- ражение1 вернет ненулевое значение (арифметическую истину), будет выполнено выражение2, иначе — выражение3 При использовании логических операторов в арифметических выражениях дей- ствуют следующие правила: выражение, возвращающее 0, считается ложным, а выражение, возвращающее ненулевое значение, — истинным. Составная коман- да (( )) отображает результаты в обычные для командной оболочки коды завер- шения: [me@linuxbox ~]$ if ((1)); then echo "true"; else echo "false"; fi true [me@linuxbox ~]$ if ((0)); then echo "true"; else echo "false"; fi false Самым странным из логических операторов выглядит тернарный (или трех- местный) оператор. Этот оператор (заимствованный из языка программирова- ния C) самостоятельно выполняет логическую проверку. Его можно использо- вать вместо инструкции if/then/else. Он оперирует тремя арифметическими выражениями (этот оператор не работает со строками), и если первое выраже- ние оценивается как истинное (то есть возвращает ненулевое значение), выпол- няется второе выражение. Иначе выполняется третье выражение. Опробуем его в командной строке. [me@linuxbox ~]$ a=0 [me@linuxbox ~]$ ((a&2 } # Убедиться, что аргумент является каталогом if [[ ! -d "$1" ]]; then usage exit 1 fi # Инициализировать массив for i in {0..23}; do hours[i]=0; done # Собрать данные for i in $(stat -c %y "$1"/* | cut -c 12-13); do j="${i#0}" ((++hours[j])) ((++count)) done # Вывести данные echo -e "Hour\tFiles\tHour\tFiles" echo -e "----\t-----\t----\t-----" for i in {0..11}; do j=$((i + 12)) printf "%02d\t%d\t%02d\t%d\n" \ "$i" \ "${hours[i]}" \ "$j" \ "${hours[j]}" done printf "\nTotal files = %d\n" $count Сценарий состоит из одной функции (usage) и основного тела с четырьмя раз- делами. В первом разделе проверяется, является ли аргумент командной строки 524 Глава 35. Массивы именем каталога. Если нет, сценарий выводит сообщение с информацией о поряд- ке использования и завершается. Второй раздел инициализирует массив hours. Для этого каждому элементу мас- сива присваивается значение 0. Массивы не требуют специальной инициали- зации перед использованием, но нашему сценарию важно, чтобы в массиве не оставалось элементов с пустыми значениями. Обратите внимание на необыч- ный способ организации цикла. Используя подстановку в фигурных скобках ({0..23}), мы смогли без труда сгенерировать последовательность слов для коман ды for. Следующий раздел осуществляет сбор данных, вызывая программу stat для каж- дого файла в каталоге. С помощью cut из результата извлекается двузначный час. Внутри цикла выполняется удаление ведущих нулей из поля с часом, потому что иначе командная оболочка попытается (и, в конечном счете, потерпит неудачу) интерпретировать значения с 00 по 09 как восьмеричные числа (см. табл. 34.1). Далее сценарий увеличивает на единицу значение элемента массива, соответству- ющего полученному часу дня. Наконец, будет увеличен счетчик (count), храня- щий общее число файлов в каталоге. Последний раздел в сценарии выводит содержимое массива. Сначала выводится пара строк заголовка, а затем начинает выполняться цикл, осуществляющий вы- вод в четыре колонки. В заключение выводится общее число файлов. Операции с массивами Массивы поддерживают множество типовых операций, таких как удаление мас- сивов, определение их размеров, сортировка и др., которым можно найти много вариантов применения в сценариях. Вывод содержимого всего массива Для доступа к каждому элементу массива используются индексы * и @. Так же как и в случае с позиционными параметрами, индекс @ имеет большую практическую ценность. Например: [me@linuxbox ~]$ animals=("a dog" "a cat" "a fish") [me@linuxbox ~]$ for i in ${animals[*]}; do echo $i; done a dog a cat Операции с массивами 525 a fish [me@linuxbox ~]$ for i in ${animals[@]}; do echo $i; done a dog a cat a fish [me@linuxbox ~]$ for i in "${animals[*]}"; do echo $i; done a dog a cat a fish [me@linuxbox ~]$ for i in "${animals[@]}"; do echo $i; done a dog a cat a fish Мы создали массив animals и сохранили в нем три строки по два слова в каждой. Затем выполнили четыре цикла, чтобы посмотреть, как выполняется разбиение содержимого массива на слова. Инструкции ${animals[*]} и ${animals[@]} дей- ствуют идентично, если они не заключены в кавычки. Индекс * возвращает содер- жимое массива, разбитое на отдельные слова, тогда как индекс @ возвращает три «слова», соответствующие «истинному» содержимому массива. Определение числа элементов в массиве Определить число элементов в массиве, так же как длину строки, можно с помо- щью механизма подстановки параметров. Например: [me@linuxbox ~]$ a[100]=foo [me@linuxbox ~]$ echo ${#a[@]} # число элементов в массиве 1 [me@linuxbox ~]$ echo ${#a[100]} # длина элемента с индексом 100 3 Мы создали массив a и записали строку foo в элемент с индексом 100. Далее с по- мощью механизма подстановки параметров мы определили длину массива, ис- пользуя при этом форму записи индекса @. Затем определили длину элемента с индексом 100, содержащего строку foo. Обратите внимание, что даже притом, что мы присвоили строку элементу с индексом 100, bash сообщает, что в массиве имеется только один элемент. Такое поведение необычно для тех языков, в кото- рых неиспользуемые элементы массива (элементы с индексами 0–99) были бы инициализированы пустыми значениями и учитывались бы при определении раз- мера массива. В bash элемент массива существует, только если ему присвоено зна- чение, независимо от индекса. 526 Глава 35. Массивы Поиск используемых индексов Так как bash позволяет создавать разреженные массивы путем присваивания зна- чений отдельным элементам, иногда требуется определить, какие элементы дей- ствительно существуют. Это можно сделать с помощью механизма подстановки параметров, как показано ниже: ${!массив[*]} ${!массив[@]} где массив — это имя переменной-массива. Как и в других случаях использова- ния * и @ в операциях подстановки, форма @, заключенная в кавычки, оказывается наиболее полезной, так как выполняет подстановку нераздробленных значений элементов: [me@linuxbox ~]$ foo=([2]=a [4]=b [6]=c) [me@linuxbox ~]$ for i in "${foo[@]}"; do echo $i; done a b c [me@linuxbox ~]$ for i in "${!foo[@]}"; do echo $i; done 2 4 6 Добавление элементов в конец массива Знание количества элементов в массиве не поможет, если понадобится добавить значения в конец массива, потому что значения, возвращаемые индексами * и @, не сообщают наибольший занятый индекс в массиве. К счастью, командная оболочка предоставляет собственное решение. Оператор присваивания += автоматически добавляет значения в конец массива. Ниже мы записали три значения в массив, а затем добавили в конец еще три. [me@linuxbox ~]$ foo=(a b c) [me@linuxbox ~]$ echo ${foo[@]} a b c [me@linuxbox ~]$ foo+=(d e f) [me@linuxbox ~]$ echo ${foo[@]} a b c d e f Сортировка массива Так же как и при работе с электронными таблицами, при работе с массивами часто возникает необходимость отсортировать значения. Командная оболочка не имеет прямой поддержки операции сортировки, но ее нетрудно реализовать самому: Операции с массивами 527 #!/bin/bash # array-sort : сортировка массива a=(f e d c b a) echo "Original array: ${a[@]}" a_sorted=($(for i in "${a[@]}"; do echo $i; done | sort)) echo "Sorted array: ${a_sorted[@]}" Если запустить этот сценарий, он выведет следующее: [me@linuxbox ~]$ array-sort Original array: f e d c b a Sorted array: a b c d e f Сценарий копирует содержимое исходного массива (a) во второй массив (a_ sorted), выполняя трюк с подстановкой команды. Этот простой прием можно ис- пользовать для выполнения самых разных операций с массивами, просто изменяя состав конвейера. Удаление массива Удалить массив можно с помощью команды unset: [me@linuxbox ~]$ foo=(a b c d e f) [me@linuxbox ~]$ echo ${foo[@]} a b c d e f [me@linuxbox ~]$ unset foo [me@linuxbox ~]$ echo ${foo[@]} [me@linuxbox ~]$ Командой unset можно также удалить единственный элемент массива: [me@linuxbox ~]$ foo=(a b c d e f) [me@linuxbox ~]$ echo ${foo[@]} a b c d e f [me@linuxbox ~]$ unset 'foo[2]' [me@linuxbox ~]$ echo ${foo[@]} a b d e f В этом примере мы удалили третий элемент массива, с индексом 2. Не забывайте, что индексация элементов массива начинается с 0, а не с 1! Отметьте также, что элемент массива нужно заключить в кавычки, чтобы предотвратить подстановку путей оболочкой. 528 Глава 35. Массивы Интересно отметить, что присваивание пустого значения массиву не уничтожает его содержимое: [me@linuxbox ~]$ foo=(a b c d e f) [me@linuxbox ~]$ foo= [me@linuxbox ~]$ echo ${foo[@]} b c d e f Любая ссылка на переменную-массив без индекса возвращает элемент с индек- сом 0: [me@linuxbox ~]$ foo=(a b c d e f) [me@linuxbox ~]$ echo ${foo[@]} a b c d e f [me@linuxbox ~]$ foo=A [me@linuxbox ~]$ echo ${foo[@]} A b c d e f Ассоциативные массивы bash версии 4.0 и выше поддерживает ассоциативные массивы. Для индексации элементов в ассоциативных массивах используются строки, а не целые числа, как в обычных массивах. Эта возможность открывает новые подходы к управлению данными. Например, можно создать массив colors и в качестве индексов исполь- зовать названия цветов: declare -A colors colors["red"]="#ff0000" colors["green"]="#00ff00" colors["blue"]="#0000ff" В отличие от обычных массивов с целочисленной индексацией, которые создают- ся простой ссылкой на них, ассоциативные массивы можно создать только коман- дой declare с новым параметром -A. Доступ к элементам ассоциативного массива осуществляется во многом так же, как к элементам обычных массивов с целочис- ленными индексами: echo ${colors["blue"]} В следующей главе мы рассмотрим сценарий, использующий ассоциативные мас- сивы для создания одного интересного отчета. Заключение 529 Заключение Если на странице справочного руководства (man) для bash выполнить поиск сло- ва array, можно найти множество его упоминаний, где описываются приемы рабо- ты с переменными-массивами. Большая часть этих описаний довольно туманна, но иногда они содержат весьма полезные сведения. Фактически массивы недоста- точно широко используются в программировании на языке командной оболочки, в основном потому, что традиционные командные оболочки для Unix (такие, как sh) не поддерживают их. Об этом недостатке остается только сожалеть, потому что массивы очень популярны в других языках программирования и являются мощным инструментом, позволяющим решать многие задачи программирования. Массивы и циклы по своей природе близки друг другу и часто используются вме- сте. Например, следующая форма цикла хорошо подходит для вычисления индек- сов массива: for ((выражение1; выражение2; выражение3)) 36 Экзотика В этой главе, завершающей наше путешествие, обратимся к совершенно случай- ным темам. Несмотря на то что в предыдущих главах мы рассмотрели основные темы, немало особенностей bash остались неохваченными. Многие из них плохо освещены в документации и полезны в основном для тех, кто занимается интегра- цией bash в дистрибутивы Linux. Но есть среди них и такие, которые, хотя и ис- пользуются нечасто, могут пригодиться при решении некоторых задач програм- мирования. Их-то мы и рассмотрим. Группы команд и подоболочки bash поддерживает возможность группировки команд. Воспользоваться ею мож- но двумя способами: либо путем группировки команд, либо путем применения по- доболочки. Ниже приводятся примеры синтаксиса обоих подходов. Группа команд: { команда1; команда2; [команда3; ...] } Подоболочка: (команда1; команда2; [команда3;...]) Группа команд заключается в фигурные скобки, а подоболочка оформляется кру- глыми скобками. Вот и вся разница. Однако обратите внимание, что из-за осо- бенностей реализации группировки команд в bash фигурные скобки должны от- деляться от команд пробелами и последняя команда должна завершаться точкой с запятой или символом перевода строки. Группы команд и подоболочки 531 Итак, где могут пригодиться группы команд и подоболочки? Даже притом, что между ними имеются важные различия (которые будут раскрыты далее), и те и другие используются в основном для перенаправления. Рассмотрим фрагмент сценария, выполняющий перенаправление вывода множества команд: ls -l > output.txt echo "Listing of foo.txt" >> output.txt cat foo.txt >> output.txt Выглядит достаточно просто: вывод трех команд перенаправляется в файл с име- нем output.txt. Воспользовавшись приемом группировки, то же самое можно вы- разить более кратко: { ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt Подоболочка используется аналогично: (ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt Этот прием помог нам сэкономить силы и время на вводе текста сценария, но ис- тинная мощь групп команд и подоболочек проявляется в конвейерах. Создавая конвейеры из команд, мы часто сталкиваемся с необходимостью объединения ре- зультатов нескольких команд в общий поток. Группы команд и подоболочки упро- щают эту задачу: { ls -l; echo "Listing of foo.txt"; cat foo.txt; } | lpr Здесь мы объединили вывод трех команд и передали его по конвейеру на вход команды lpr, чтобы напечатать отчет. В следующем сценарии мы задействуем группы команд и рассмотрим несколько приемов, которые можно использовать в сочетании с ассоциативными массива- ми. Получив имя каталога, сценарий с названием array-2 выводит список фай- лов, находящихся в этом каталоге, с именами пользователей и групп, владеющих файлами. В конце сценарий выводит общее количество файлов, принадлежащих каждому пользователю и каждой группе. Ниже приводится сокращенный (для экономии места) вывод сценария для каталога /usr/bin: [me@linuxbox ~]$ array-2 /usr/bin /usr/bin/2to3-2.6 root root /usr/bin/2to3 root root /usr/bin/a2p root root /usr/bin/abrowser root root /usr/bin/aconnect root root /usr/bin/acpi_fakekey root root 532 Глава 36. Экзотика /usr/bin/acpi_listen root root /usr/bin/add-apt-repository root root --пропущено-- /usr/bin/zipgrep root root /usr/bin/zipinfo root root /usr/bin/zipnote root root /usr/bin/zip root root /usr/bin/zipsplit root root /usr/bin/zjsdecode root root /usr/bin/zsoelim root root File owners: daemon : 1 file(s) root : 1394 file(s) File group owners: crontab : 1 file(s) daemon : 1 file(s) lpadmin : 1 file(s) mail : 4 file(s) mlocate : 1 file(s) root : 1380 file(s) shadow : 2 file(s) ssh : 1 file(s) tty : 2 file(s) utmp : 2 file(s) Далее следует исходный код сценария (с номерами строк): 1 #!/bin/bash 2 3 # array-2: Используем массивы для подсчета файлов, принадлежащих # разным владельцам 4 5 declare -A files file_group file_owner groups owners 6 7 if [[ ! -d "$1" ]]; then 8 echo "Usage: array-2 dir" >&2 9 exit 1 10 fi 11 12 for i in "$1"/*; do 13 owner="$(stat -c %U "$i")" 14 group="$(stat -c %G "$i")" 15 files["$i"]="$i" 16 file_owner["$i"]="$owner" 17 file_group["$i"]="$group" Группы команд и подоболочки 533 18 ((++owners[$owner])) 19 ((++groups[$group])) 20 done 21 22 # Вывод списка файлов 23 { for i in "${files[@]}"; do 24 printf "%-40s %-10s %-10s\n" \ 25 "$i" "${file_owner["$i"]}" "${file_group["$i"]}" 26 done } | sort 27 echo 28 29 # Вывод списка владельцев 30 echo "File owners:" 31 { for i in "${!owners[@]}"; do 32 printf "%-10s: %5d file(s)\n" "$i" "${owners["$i"]}" 33 done } | sort 34 echo 35 36 # Вывод списка групп 37 echo "File group owners:" 38 { for i in "${!groups[@]}"; do 39 printf "%-10s: %5d file(s)\n" "$i" "${groups["$i"]}" 40 done } | sort Рассмотрим механику работы этого сценария. Строка 5: ассоциативные массивы должны создаваться командой declare с пара- метром -A. Этот сценарий создает пять таких массивов: files хранит имена файлов, найденных в каталоге, индексируется именами файлов. file_group хранит имена групп, владеющих файлами, индексируется именами файлов. file_owner хранит имена пользователей, владеющих файлами, индексируется именами файлов. groups хранит число файлов, принадлежащих каждой группе, индексируется именами групп. owners хранит число файлов, принадлежащих каждому пользователю, индек- сируется именами пользователей. Строки 7–10: эти строки проверяют имя каталога, переданное в позиционном па- раметре. Если каталог с указанным именем не существует, выводится сообщение, описывающее порядок использования сценария, после чего сценарий завершает- ся с кодом 1. 534 Глава 36. Экзотика Строки 12–20: Эти строки выполняют обход файлов в каталоге. Команды stat в строках 13 и 14 извлекают имена пользователя и группы, владеющих файлом, которые затем сохраняются в соответствующих массивах (строки 16 и 17), с ис- пользованием имени файла в роли индекса. Само имя файла сохраняется в масси- ве files (строка 15). Строки 18–19: количество файлов, принадлежащих пользователю и группе, уве- личивается на 1. Строки 22–27: вывод списка файлов. Вывод осуществляется с использованием подстановки параметров "${массив[@]}", которая возвращает полный список эле- ментов массива, каждый из которых интерпретируется как отдельное слово. При таком подходе не возникает проблем с именами файлов, содержащими пробелы. Отметьте также, что весь цикл заключен в фигурные скобки, формирующие груп- пу команд. Это позволяет передать вывод, полученный в цикле, команде sort. Это важно, потому что операция подстановки в строке 12 возвращает имена файлов в неотсортированном порядке. Строки 29–40: эти два цикла реализованы подобно циклу вывода списка файлов и отличаются только использованием подстановки "${!массив[@]}", которая воз- вращает не список элементов, а список индексов массива. Подстановка процессов Несмотря на внешнее сходство и возможность объединения потоков для последу- ющего перенаправления, между группами команд и подоболочками существуют важные отличия. Все команды, входящие в группу, выполняются в текущей обо- лочке, подоболочка (как можно догадаться из названия) выполняет свои команды в дочерней копии текущей командной оболочки. Это означает, что в момент запу- ска подоболочки создается копия окружения текущей оболочки и передается но- вому экземпляру оболочки. Когда подоболочка завершается, ее копия окружения уничтожается, соответственно, теряются любые изменения в окружении подобо- лочки (включая значения переменных). Поэтому если нет прямой необходимости в использовании подоболочки, предпочтительнее использовать группы команд. Группы команд выполняются быстрее и требуют меньше памяти. В главе 28 мы столкнулись с одной из проблем, характерных для подоболочек, когда выяснили, что команда read действует в конвейерах не так, как можно было бы ожидать. Там мы сконструировали следующий конвейер: echo "foo" | read echo $REPLY Подстановка процессов 535 после выполнения которого переменная REPLY всегда оставалась пустой, потому что команда read выполняется в подоболочке и ее копия REPLY уничтожается по ее завершении. Так как конвейеры команд всегда выполняются в подоболочке, любые команды, присваивающие значения переменным, будут сталкиваться с этой проблемой. К счастью, командная оболочка поддерживает экзотическую форму подстановки, которая называется подстановкой процессов и может использоваться для преодо- ления указанных трудностей. Подстановка процессов оформляется двумя способами: для процессов, отправля- ющих результаты в стандартный вывод: (список) где список — это список команд. Ниже показано, как использовать подстановку процессов для решения проблемы с командой read: read &1 exit 0 } exit_on_signal_SIGTERM () { echo "Script terminated." 2>&1 exit 0 } trap exit_on_signal_SIGINT SIGINT trap exit_on_signal_SIGTERM SIGTERM for i in {1..5}; do echo "Iteration $i of 5" sleep 5 done Этот сценарий дважды использует команду trap, настраивая ловушки для двух сигналов. В каждой ловушке используется своя функция, которая будет вызвана для обработки конкретного сигнала. Обратите внимание на включение команды exit в обе функции обработки сигналов. Без этого сценарий продолжил бы вы- полняться после завершения функции. Если во время выполнения этого сценария пользователь нажмет комбинацию CTRL+C, он увидит следующее: [me@linuxbox ~]$ trap-demo2 Iteration 1 of 5 Iteration 2 of 5 ^CScript interrupted. Ловушки 539 ВРЕМЕННЫЕ ФАЙЛЫ Одним из побудительных мотивов включения обработчиков сигналов в сценарии является необходимость удаления временных файлов, которые сценарии могут создавать для хранения промежуточных результатов. Выбор имен для временных файлов — целое искусство. Традиционно программы в Unix-подобных системах создают свои временные файлы в каталоге /tmp, общем для всех и предназначен- ном именно для таких файлов. Однако из-за того, что каталог является общим, возникает проблема безопасности, особенно остро проявляющаяся в программах, действующих с привилегиями суперпользователя. Помимо очевидной необходи- мости установки соответствующих разрешений для файлов, которые могут быть доступны всем пользователям в системе, важно также давать временным файлам непредсказуемые имена. Это поможет избежать атак вида гонка за временными файлами (temp race attack). Ниже показан один из способов создания непред- сказуемого (но все еще осмысленного) имени: tempfile=/tmp/$(basename $0).$$.$RANDOM Эта команда сконструирует имя файла из имени программы, идентификатора процесса (PID) и случайного целого числа. Но имейте в виду, что переменная командной оболочки $RANDOM возвращает значения только из диапазона от 1 до 32 767, не очень большого по компьютерным меркам, поэтому единственного экземпляра переменной недостаточно, чтобы противостоять заинтересованному злоумышленнику. Лучший результат дает программа mktemp (не путайте с функцией mktemp из стан- дартной библиотеки) — она автоматически выбирает имя и создает временный файл. Программа mktemp принимает аргумент с шаблоном, на основе которого конструирует имя файла. Шаблон должен включать последовательность симво- лов X, которые будут заменены соответствующим числом случайных букв и цифр. Чем длиннее последовательность из символов X, тем длиннее последовательность случайных символов. Например: tempfile=$(mktemp /tmp/foobar.$$.XXXXXXXXXX) Эта команда создаст временный файл и сохранит его имя в переменной tempfile. Символы X в шаблоне будут заменены случайными буквами и цифрами, соответ- ственно, окончательное имя файла (которое в данном примере включает также значение специального параметра $$, возвращающего идентификатор процесса) может выглядеть, например, так: /tmp/foobar.6593.UOZuvM6654 В сценариях, предназначенных для запуска рядовыми пользователями, разумнее отказаться от использования каталога /tmp и создать каталог для временных файлов в домашнем каталоге пользователя, например, так: [[ -d $HOME/tmp ]] || mkdir $HOME/tmp 540 Глава 36. Экзотика Асинхронное выполнение с командой wait Иногда возникает необходимость решать одновременно несколько задач. Мы зна- ем, что все современные операционные системы, даже те, которые не являются многопользовательскими, поддерживают многозадачность. Сценарии тоже мож- но конструировать так, что они будут действовать в многозадачном режиме. Обычно такие сценарии запускают один или несколько дочерних сценариев, ре- шающих вспомогательные задачи, пока родительский сценарий продолжает вы- полнять основной алгоритм. Однако когда таким способом запускается целая се- рия сценариев, возникает проблема координации действий родителя и потомков. Например, представьте, что родитель зависит от результатов работы потомка или, наоборот, и он должен дождаться, пока другой сценарий завершится, прежде чем завершиться самому. В bash имеется встроенная команда, помогающая управлять асинхронным выпол- нением в подобных ситуациях. Команда wait приостанавливает выполнение ро- дительского сценария, пока не завершится указанный процесс (то есть дочерний сценарий). Для демонстрации нам понадобятся два сценария. Ниже приводится родительский сценарий: #!/bin/bash # async-parent : пример асинхронного выполнения (родитель) echo "Parent: starting..." echo "Parent: launching child script..." async-child & pid=$! echo "Parent: child (PID= $pid) launched." echo "Parent: continuing..." sleep 2 echo "Parent: pausing to wait for child to finish..." wait $pid echo "Parent: child is finished. Continuing..." echo "Parent: parent is done. Exiting." и дочерний сценарий: #!/bin/bash # async-child : пример асинхронного выполнения (потомок) Именованные каналы 541 echo "Child: child is running..." sleep 5 echo "Child: child is done. Exiting." В этом примере дочерний сценарий тривиально прост. Фактическая работа вы- полняется родителем. Родительский сценарий запускает дочерний сценарий и пе- реводит его в фоновый режим выполнения. Идентификатор дочернего процесса сохраняется в переменной pid путем присваивания ей значения параметра $!, ко- торый всегда содержит идентификатор процесса последнего задания, переведен- ного в фоновый режим. Родительский сценарий продолжает работу и в конце выполняет команду wait с идентификатором процесса дочернего сценария. Это вызывает приостановку родительского сценария до завершения дочернего сценария, после чего родитель- ский сценарий возобновляет работу и тут же завершается. В ходе выполнения родительский и дочерний сценарии производят следующий вывод: [me@linuxbox ~]$ async-parent Parent: starting... Parent: launching child script... Parent: child (PID= 6741) launched. Parent: continuing... Child: child is running... Parent: pausing to wait for child to finish... Child: child is done. Exiting. Parent: child is finished. Continuing... Parent: parent is done. Exiting. Именованные каналы В большинстве Unix-подобных систем существует возможность создавать файлы специального типа, которые называются именованными каналами (named pipe). Именованные каналы создают соединения между двумя процессами и могут ис- пользоваться как обычные файлы. Они не пользуются большой популярностью, но знать о такой возможности и уметь пользоваться ею желательно. В программировании широко известна архитектура под названием клиент/сер- вер, основанная на использовании механизмов взаимодействий процессов, таких как именованные каналы или сетевые соединения. Наиболее широко архитектура клиент/сервер используется в веб-приложениях, где веб-браузеры взаимодействуют с веб-серверами. Веб-браузер действует 542 Глава 36. Экзотика как клиент, посылая запросы серверу, в ответ на которые сервер посылает веб- страницы. Именованные каналы имеют некоторое сходство с файлами, но на самом деле образуют буферы, действующие по принципу очереди: первым пришел, первым вышел (First-In, First-Out, FIFO). Так же как в случае с обычными (неименован- ными) каналами, данные записываются с одного конца канала и извлекаются из другого. С применением именованных каналов можно, например, выполнять сле- дующие команды: процесс1 > именованный_канал и процесс2 pipe1 После нажатия клавиши ENTER появится ощущение, что команда «зависла». Это объясняется тем, что с другого конца канала данные еще не были прочитаны. Заключение 543 В таких ситуациях говорят, что канал заблокирован. Разблокировка произойдет автоматически, как только мы подключим процесс с другого конца канала и про- читаем данные из него. Во втором окне терминала введите следующую команду: [me@linuxbox ~]$ cat < pipe1 Во втором терминале появится список содержимого каталога, созданный в пер- вом окне, как результат работы команды cat. Команда ls в первом окне терминала благополучно разблокируется и завершится. Заключение Итак, мы закончили наше путешествие. Единственное, что осталось, — это прак- тика, практика и еще раз практика. Даже притом, что на своем пути мы охвати- ли широкий круг вопросов, в действительности мы лишь затронули верхушку айсберга под названием «командная строка». Существуют еще тысячи программ командной строки, которые вам предстоит открыть и изучить. Начните свои ис- следования с каталога /usr/bin, и вы увидите их! У. Шоттс Командная строка Linux. Полное руководство 2-е международное издание Перевел с английского А. Киселев Заведующая редакцией Ведущий редактор Литературный редактор Художественный редактор Корректоры Верстка Ю. Сергиенко К. Тульцева А. Руденко В. Мостипан С. Беляева, Н. Викторова Л. Егорова Изготовлено в России. Изготовитель: ООО «Прогресс книга». Место нахождения и фактический адрес: 194044, Россия, г. Санкт-Петербург, Б. Сампсониевский пр., д. 29А, пом. 52. Тел.: +78127037373. Дата изготовления: 08.2019. Наименование: книжная продукция. Срок годности: не ограничен. Налоговая льгота — общероссийский классификатор продукции ОК 034-2014, 58.11.12.000 — Книги печатные профессиональные, технические и научные. Импортер в Беларусь: ООО «ПИТЕР М», 220020, РБ, г. Минск, ул. Тимирязева, д. 121/3, к. 214, тел./факс: 208 80 01. Подписано в печать 12.07.19. Формат 70×100/16. Бумага офсетная. Усл. п. л. 43,860. Тираж 1200. Заказ 0000.