Типичные приемы работы


Скрипты, псевдонимы (aliases) и функции (functions)

Что такое скрипты, зачем они нужны и как их делать

Что такое скрипт


Скрипт -- это программа на языке shell. Вместо термина скрипт иногда используется термин сценарий.

Скрипт в Unix -- это практически то же самое, что командный файл в Dos (.bat-файлы) и VMS (.com-файлы). Отличие в том, что, поскольку скрипты являются обычными программами, то вызываются они просто по имени (а не специальной командой типа "call" или "@"), могут иметь произвольное имя (специальное расширение типа .bat не нужно), и нет ограничений на глубину вызовов скриптов из других скриптов.

Простейший скрипт -- это несколько обычных команд shell, которые должны выполняться последовательно.

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

Язык shell, однако, довольно мощный -- он позволяет работать с переменными, содержит конструкции типа "while", "for", "if-then-else", функции и другие атрибуты, присущие языкам структурного программирования. Это позволяет выполнять на shell достаточно сложные действия. Многие программы в Unix являются именно скриптами -- например, startx и больщинство программ, выполняющих инициализацию/останов системы (они расположены в /etc/rc.d/).

Хотя термин "скрипт" обычно подразумевает сценарий на языке shell, в Unix есть и другие скрипт-языки, зачастую специализированные. Все они объединяются тем, что являются интерпретаторами -- т.е. программа является текстовым файлом, который исполняется сразу же, не требуя предварительной компиляции в бинарный файл.

В многообразном мире Unix у такого подхода есть большое достоинство: один и тот же скрипт может без всяких изменений использоваться в разных видах Unix (в том числе и браться ими непосредственно из одного файла -- например, по NFS).

Как сделать скрипт


Как говорилось выше, простейший скрипт -- это несколько команд shell, записанных в файл. Создавать файлы скриптов удобнее всего в текстовом редакторе, но можно и при помощи команды "cat >файл" -- при этом cat принимает ввод с клавиатуры до нажатия <Ctrl+D>.

Чтобы дать понять системе, что это исполняемый файл (программа), надо установить для него атрибут "x".

Пример: создание скрипта, содержащего команду "ls -l | less":

bobby:~% cat >lls
ls -l | less
<Ctrl+D>
bobby:~% ls -l lls
-rw-r--r--   1 ivanov   lab5           13 May 14 17:36 lls
bobby:~% chmod +x lls
bobby:~% ls -l lls
-rwxr-xr-x   1 ivanov   lab5           13 May 14 17:36 lls
bobby:~% _

Запустить затем этот скрипт можно, набрав "./lls".

Но, находясь в другой директории, придется вызывать этот скрипт по полному имени (например, "~/lls"), что довольно неудобно. Поэтому следует поместить такую "свежесделанную" программу в одну из директорий, перечисленных в переменной окружения PATH.

Чтобы у каждого пользователя была возможность создавать и удобно запускать свои программы, в переменной PATH всегда присутствует ссылка на поддиректорию bin/ в home-директории пользователя:

bobby:~% echo $PATH
/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/users/ivanov/bin
bobby:~% _

Если в home-директории bin/ отсутствует, то ее надо просто создать командой "mkdir ~/bin".

Скрипты (как, впрочем, и любые другие программы), расположенные в ~/bin/, вызываются просто по имени:

bobby:~% lls
zsh: command not found: lls
bobby:~% mkdir bin
bobby:~% mv lls bin/
bobby:~% lls
...Запускается скрипт lls...

Если в первой строчке скрипта стоит "#!/bin/sh"...


Поскольку скрипты написаны на языке shell, то они должны исполняться (интерпретироваться) какой-то программой. По умолчанию в качестве интерпретатора система использует /bin/sh.

Но можно явно указать интерпретатор, если в начале самой первой строки файла поставить символы "#!" ("решетка" и восклицательный знак), а затем -- полный путь к программе-интерпретатору. Например, чтобы воспользоваться в скрипте дополнительными возможностями, предоставляемыми zsh по сравнению с sh, надо поставить первой строкой

#!/bin/zsh

Таким образом, при отсутствии "#!" система ведет себя так, как если бы там стояло "#!/bin/sh". Вообще говоря, хорошим тоном считается в обычных shell-скриптах ставить эту строку, хотя она и не обязательна -- тогда по файлу сразу ясно, что он является shell-скриптом.

В скриптах на других языках в первой строке обязательно указывается программа-интерпретатор -- например, для Perl -- "#!/usr/bin/perl".

Скрипты с параметрами


Поскольку обычные программы могут вызываться с некоторыми параметрами (например, "ls /etc"), то рано или поздно такая же необходимость возникает и в скриптах.

Для подстановки значения первого указанного параметра используется название "$1", второго -- "$2" и т.д. -- аналогично массиву "argv[]" в программах на языке C. Таким образом, если переделать наш скрипт lls из

ls -l | less
в

ls -l $1| less
то он будет показывать при помощи less длинный листинг не текущей, а указанной директории. Если при вызове lls не будет указана никакая директория, то "$1" будет равен "ничему" (пустой строке), и в результате получится команда "ls -l | less" -- та же, что и раньше.

Специальное название "$*" означает "все параметры". Таким образом, если переделать наш скрипт в

ls -l $* | less
то, к примеру, команда

lls /bin /usr/bin /usr/local/bin
будет воспринята как

ls -l /bin /usr/bin /usr/local/bin | less

Использование переменных


Как известно из теории программирования, практически любая программа рано или поздно потребует для гибкости использования переменных.

Переменные в языке shell -- это почти то же самое, что переменные окружения. В отличие от таких языков, как C или Pascal, все переменные в shell -- строковые (а не числовые).

Для присвоения переменной значения служит конструкция вида

ИМЯ=значение
(вокруг "=" не должно быть пробелов!).

Если присваиваемое переменной значение содержит пробелы или какие-либо символы, имеющие специальный смысл для shell (*, <, >, & и т.д.), то значение надо заключить в кавычки.

Для получения значения переменной используется конструкция $ИМЯ (в отличие от Makefile, скобки вокруг имени указывать не следует).

Заметьте, что это очень похоже на использование параметров $1, $2 и т.д. -- для shell они являются псевдопеременными ("псевдо" потому, что значения их изменять нельзя).

Таким образом, и использованием переменных наш скрипт можно переписать следующим образом:

LS="ls -l"
VIEWER=less

$LS $* | $VIEWER

(Конечно, не самый осмысленный пример, но весьма наглядный.)

Другие скрипт-языки


Кроме shell, в Unix существует еще довольно много языков для скриптов. В таблице ниже приведены наиболее распространенные и частоиспользуемые из них.

Какие бывают script-языки и зачем они нужны
ПрограммаЯзыкПрименение
/bin/shShellОсновной язык системы, в т.ч. initscripts
/usr/bin/perlPerlЯзык, похожий по синтаксису на C, для создания мощных скриптов, со сложными структурами данных
/bin/awkawkМощнее shell, но проще perl
/bin/sedsed"Stream editor" -- Скорее не язык, а фильтр
/usr/bin/tclshTcl"Tool Command Language" -- язык для написания простых приложений
/usr/bin/wishTcl/TkЯзык на основе Tcl для программ с оконным интерфейсом
/usr/bin/expectExpectЯзык для мультизадачных сценариев

Замечание
В данной таблице перечислены стандартные пути к интерпретаторам, но в некоторых дистрибутивах они могут и отличаться.

Где можно почитать про создание shell-скриптов


Хорошим пособием является книги Б.Керниган, Р.Пайк "UNIX - универсальная среда программирования", главы 3-5, и У.Дэвис "Операционные системы", М., Мир, 1986.

В качестве справочника по конструкциям языка shell стоит использовать man-страницу на sh или bash (собственно, именно bash "работает" в Linux в качестве sh, в этом легко убедиться при помощи команды "ls -l /bin/sh").

Псевдонимы (aliases)

Для простых частоиспользуемых команд необязательно создавать скрипт -- достаточно сделать "псевдоним" (alias).

Shell всегда проверяет самое первое слово каждой команды (т.е. собственно имя команды), не является ли оно псевдонимом, и если да, то заменяет его "значением" псевдонима.

К примеру, если сделать "ll" псевдонимом для "ls -l", то команда

ll /etc
будет исполняться как

ls -l /etc

Для определения псевдонимов используется оператор alias. Пример:

alias ll='ls -l'
Аналогично определению переменных, вокруг символа "=" не должно быть пробелов, а если в "значении" псевдонима есть пробелы или спецсимволы, то его следует "закавычить".

Посмотреть список псевдонимов можно командой alias без параметров:

bobby:~% alias
ls='/bin/ls --color=tty -F -T 0'
run-help=man
which-command=whence
bobby:~% _

"Разопределить" псевдоним можно командой unalias:

bobby:~% alias
ls='ls --color=tty -F -T 0'
run-help=man
which-command=whence
bobby:~% alias ll='ls -l'
bobby:~% alias
ll='ls -l'
ls='ls --color=tty -F -T 0'
run-help=man
which-command=whence
bobby:~% unalias ll
bobby:~% alias
ls='/bin/ls --color=tty -F -T 0'
run-help=man
which-command=whence
bobby:~% _

У псевдонимов есть две уникальных особенности. Во-первых, shell проверяет, не псевдоним ли это, до того, как начнет искать команду с таким именем. Во-вторых, после "подстановки" значения псевдонима первое слово получившейся команды опять проверяется на предмет того, не является ли оно псевдонимом, но "уже найденные" псевдонимы при этом не учитываются.

Таким образом, можно определить псевдоним с именем уже существующей команды, слегка модифицировав ее поведение или даже полностью заменив ее. В приведенном выше примере команда ls модифицирована так, чтобы всегда автоматически указывались коючи "--color=tty", "-F" и "-T 0".

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


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

Единственное, чего псевдонимы не умеют -- так это "работать" в качестве скриптов с параметрами. Но для этого служат функции.

Функции (functions)

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

Функция -- это как бы скрипт внутри скрипта.

Синтаксис определения функции следующий:

function ИМЯ_ФУНКЦИИ () {
  команды тела функции
}

Внутри функции имена "$1", "$2" и т.д. содержат параметры, указанные в вызове функции, аналогично тому, как обычно они содержат параметры, указанные скрипту (которые, в свою очередь, изнутри функции недоступны).

Например, переписать наш скрипт lls в виде функции можно следующим образом:

function lls () {
  ls -l $* | less
}

Посмотреть список функций можно командой "typeset -f", а "разопределить" функцию -- командой "unset -f":

bobby:~% typeset -f
lls () {
        ls --color=tty -F -T 0 -l $* | less
}
setenv () {
        export $1=$2
}
bobby:~% unset -f lls
bobby:~% typeset -f
setenv () {
        export $1=$2
}
bobby:~% _

Функции удобно применять в скриптах, а псевдонимы -- в интерактивной работе (т.е. для команд, вводимых непосредственно в командной строке).

Замечание
В zsh (но не в скриптах!) вместо "typeset -f" для просмотра списка функций можно использовать команду functions, а для "разопределения" вместо "unset -f" -- команду unfunction.

В определении функций ключевое слово "function" необязательно, и его можно просто опускать.

В оболочках csh/tcsh функции не поддерживаются.


Временное исполнение

Что такое временное исполнение

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

Есть два варианта временного исполнения: однократное исполнение команд в указанный день в указанное время (команда at) и периодическое исполнение одних и тех же команд -- например, раз в сутки (команда crontab).

В обоих случаях можно запускать только неинтерактивные команды -- т.е. такие, которые не взаимодействуют с терминалом, т.к. команды запускаются "сами по себе", без терминала. Хотя, в принципе, можно запускать таким образом X-программы, делать это следует с осторожностью.

Результат исполнения команд (т.е. то, что они в обычной ситуации выдали бы на экран) будет послан по e-mail. Если выдача пустая, то письмо не посылается.

Исполнение скрипта в указанное время -- at

Команда at позволяет указать системе, что некую последовательность команд (реально -- скрипт на языке shell) надо исполнить в указанное время. Этот набор команд называется заданием. ("At" в переводе означает "в", т.е. "исполнить В такое-то время".)

Формат вызова at следующий:

at [-f файл-содержащий-команды] <время>

Если есть ключ "-f", то at считает скрипт из указанного файла, если нет -- то со стандартного ввода (обычно с клавиатуры, завершить ввод при этом надо комбинацией <Ctrl+D>).

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

Ниже приведено несколько примеров указания времени. При этом предполагается что они указываются в субботу, 15 мая 1999 года в 18:10.

Несколько примеров указания времени команде at
УказаниеСмысл
19:00Сегодня в 7 часов вечера 0 минут
17:30Завтра в 5 часов 30 минут после полудня
now + 53 minutesЧерез 53 минуты (т.е. в 19:03)
now + 1 dayЗавтра в это же время
13:20 tomorrowЗавтра в 13:20
10:00 today + 5 daysВ 10 часов утра через пять дней (т.е. 20 мая)
14:30 Feb 15,2001В 14:30 15 февраля 2001 года

День указывается после времени. Если день не указан, то подразумевается текущий день или, если это время уже прошло (как во втором примере), следующий. Аналогично, если указанные месяц/день уже прошли, то считается, что они относятся к следующему году.

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

Если указывается уже прошедшее время, то разные системы ведут себя по разному -- к примеру, в ответ на "03:00 saturday" Linux выполнит скрипт немедленно, а Irix скажет "too late".

Приведенных примеров обычно достаточно, но если хочется посмотреть полную спецификацию формата указания времени, то она есть (в виде специальной формы нотации Бэкуса-Наура) в файле

/usr/doc/at-3.1.7/timespec

Пример использования at:

bobby:~% whoami
ivanov
bobby:~% at 23:00 today
at> who
at> <EOT>
warning: commands will be executed using /bin/sh
job 12 at 1999-05-15 23:00
bobby:~% _

В этом примере в 11 часов вечера текущего дня будет выполнена команда who (показывающая список пользователей), а результат ее исполнения будет отправлен пользователю "ivanov@bobby".

При чтении списка команд не из файла (т.е. без ключа "-f"), at предваряет каждую вводимую строку приглашением "at>". Символы "<EOT>" показывают то место, где мы нажали <Ctrl+D>.

В конце at печатает напоминание, что скрипт будет выполняться при помощи интерпретатора /bin/sh, а также идентификатор задания и время исполнения в виде "год-месяц-день часы:минуты".

Идентификатор задания служит для дальнейших ссылок на него -- он показывается в листинге заданий и должен указываться при необходимости удалить задание. В разных системах идентификаторы заданий имеют разный формат -- в Linux это просто последовательно увеличивающееся число, а в других системах оно может, к примеру, иметь вид "926798400.a" или "947883600.a+500".

Для просмотра списка заданий служит команда atq:

viper:~% atq
12      1999-05-15 23:00 a
viper:~% _

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

bobby:~% at now
at> sleep 120
at> w
at> <EOT>
warning: commands will be executed using /bin/sh
job 14 at 1999-05-15 19:05
bobby:~% atq
12      1999-05-15 23:00 a
14      1999-05-15 19:05 =
bobby:~% _

(Команда sleep "спит" (т.е. ничего не делает) в течение указанного количества секунд.)

Удалить задание из очереди можно командой atrm, ей указывается идентификатор задания:

bobby:~% atq
12      1999-05-15 23:00 a
bobby:~% atrm 12
bobby:~% atq
bobby:~% _

При попытке удалить уже исполняющееся задание выдается сообщение "Warning: deleting running job", и хотя задание из очереди удаляется, оно продолжает исполняться.

Просмотреть содержимое задания можно командой "at -c". Ее выдача довольно показательна: в начало скрипта добавляется настройка переменных окружения (т.е. всем переменным окружения, существующим на момент запуска at, присваиваются такие же значения).

Замечание
В системах клона SystemV (таких, как Solaris и IRIX) вместо команды atrm используется "at -r", а вместо atq -- "at -l". Команда "at -c" в них не поддерживается.

Периодическое исполнение команд -- crontab

Для периодического исполнения команд служит подсистема Cron, а команда для редактирования списка команд используется команда crontab. (Слово "cron" -- это сокращение от "chronograph".)

У каждого пользователя для управления периодическим исполнением своих команд есть собственный так называемый crontab-файл ("crontab" -- "CRON driving TABle" -- "таблица управления Cron'ом). Этот (изначально пустой) файл расположен в недоступной для самого пользователя директории, и может просматриваться и редактироваться только при помощи команды crontab.

У команды crontab есть четыре варианта вызова:

КомандаДействие
crontab ФАЙЛСкопировать ФАЙЛ в crontab-файл
crontab -lВыдать содержимое crontab-файла
crontab -rУдалить crontab-файл
crontab -eРедактировать crontab-файл

Для редактирования по умолчанию вызывается vi (причем в RedHat 5.2 crontab его не находит и "обламывается"). Чтобы использовать другой текстовый редактор (например, joe), надо указать его в переменной окружения EDITOR:

bobby:~% export EDITOR=joe
bobby:~% crontab -e

В каждой строке crontab-файла указывается команда и периодичность ее запуска. Пустые строки и строки, начинающиеся с символа "#", игнорируются.

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

Минута Час ДеньМесяца Месяц ДеньНедели Команда

Например, строка

0 23 * * 0 /usr/bin/who
будет каждый понедельник в 23:00 запускать команду who и отсылать по e-mail выдаваемый ею список пользователей.

Команды могут быть любыми, а не только скриптами. Вообще говоря, могут указываться любые сложные команды, допустимые в языке shell, в том числе содержащие перенаправление ввода/вывода. Но команды следует указывать с полным путем -- т.е. /home/users/ivanov/bin/mystatcmd, а не просто "mystatcmd". (Другой вариант -- присвоить в начале crontab-файла значение переменной окружения PATH, но мы это рассматривать не будем.)

Замечание
Узнать полный путь программы можно при помощи команды which. Например, команда "which finger" выдаст "/usr/bin/finger".

Месяцы нумеруются с 1 по 12, дни недели -- с 0 по 6 (0 -- воскресенье, 1 -- понедельник, 6 -- суббота). В Linux месяцы и дни недели можно указывать трехсимвольными именами (т.е. Jan, Feb, Mar...; Sun, Mon, Tue...).

Cron исполняет команду тогда, когда значения всех полей Минута, Час, Месяц и ДеньНедели или ДеньМесяца совпадают с текущим временем. Символ "*" означает "любое" или "каждое". Можно указывать несколько значений через запятую, например "10,30,50" в поле Минута означает "в 10, 30 и 50 минут". Допускаются также диапазоны через дефис, например, "1-5" в поле ДеньНедели означает "с понедельника по пятницу".

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

30 4 1,15 * 5 /usr/bin/vmstat
будет запускать vmstat в 4:30 утра по первым и пятнадцатым числам каждого месяца, плюс каждую пятницу.

Несколько примеров:

# Запускать df каждый день в 7:00 утра 
0 7 * * * /bin/df

# Заказывать кофе каждый рабочий день в 10:00, 13:00 и 17:00
0 10,13,17 * * 1-5 echo Now| mail -s "Coffee, please" waitor@restaurant

# Производить подсчет своего бюджета 6 и 21 числа каждого месяца
0 14 6,21 * * /home/users/ivanov/bin/calcbudget

Очень хорошая и полная документация на crontab-файлы есть в man-странице crontab(5) (т.е. для просмотра надо набрать "man 5 crontab").

Замечание
В старых BSD-системах пользоваться подсистемой Cron может только "root" (если только стандартный пакет Cron не заменен на Vixie-Cron, что, впрочем, в последнее время стало почти правилом).


Поиск файлов на диске

Команды для поиска файлов

Для поиска файлов в Unix предназначены две команды -- find и locate. Первая выполняет поиск непосредственно в момент запуска, а вторая делает поиск по списку файлов (базе данных), который обновляется обычно раз в сутки (в большинстве систем -- поздно ночью или рано утром).

У обеих команд есть как достоинства, так и недостатки. Так, find выдает результаты по текущему, "мгновенному" состоянию диска, но, к примеру, поиск по всему диску может занимать очень много времени. Напротив, locate выполняет поиск очень быстро, но во-первых, в базе данных может быть несколько устаревшая информация, а во-вторых, база данных составляется "из-под" пользователя "nobody" и не содержит информации о тех директориях, которые для него закрыты.

Кроме того, для быстрого поиска файлов, относящихся к программам, в RedHat Linux можно использовать команду rpm с ключом "-qal" в паре с командой grep.

Как find, так и locate позволяют указывать шаблон имени файла, содержащий специальные символы "*", "?" и т.д. Такие шаблоны надо заключать в кавычки, чтобы shell не пытался их интерпретировать.

Поиск по диску сейчас -- find

Вызов find


Команда find ищет в указанных директориях и ниже файлы, отвечающие заданному критерию ("test"). Причем критерием может быть не только имя файла, но и, к примеру, время модификации, размер или тип (файл/директория/линк и т.д). Критерии могут объединяться при помощи булевских операторов, так что можно найти, например, все файлы с расширением .txt размером не более 10К, модифицированные не позднее прошлого месяца.

Формат вызова find следующий:

find [список директорий] [критерий]

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

Директории в списке указываются через пробел. Имена критериев всегда начинаются с "-", аналогично ключам.

Поиск файла по имени


Самое частое использование find -- поиск файлов по имени. Для этого служит критерий "-name", которому указывается шаблон имени. К примеру, чтобы найти на всем диске все файлы с расширением .txt, надо воспользоваться командой

find / -name '*.txt'

GNU-версия find, используемая в Linux, понимает также критерий "-iname", отличающийся тем, что маленькие и большие буквы не различаются. Так, команда

find / -name '*.exe'
найдет все файлы с расширениями .exe, .EXE, .Exe и т.д.

Поиск с несколькими критериями


Если указать несколько критериев, то find найдет только файлы, соответствующие всем критериям сразу.

К примеру, критерий "-type" позволяет указать тип файла -- обычный ("f"), директория ("d") и т.д. Таким образом, чтобы найти в директории /etc все директории, имена которых начинаются на "cron", надо сделать слежующее:

bobby:~% find /etc -name 'cron*' -type d
find: /etc/X11/xdm/authdir: Permission denied
/etc/cron.daily
/etc/cron.hourly
/etc/cron.monthly
/etc/cron.weekly
bobby:~% _

В то время как поиск просто по имени дал бы несколько лишних файлов:

bobby:~% find /etc -name 'cron*'
find: /etc/X11/xdm/authdir: Permission denied
/etc/rc.d/init.d/crond
/etc/logrotate.d/cron
/etc/cron.daily
/etc/cron.hourly
/etc/cron.monthly
/etc/cron.weekly
bobby:~% _

Исполнение команды с найденными файлами


Кроме критериев команде find можно указывать также действия ("actions"). Если никакое действие не указано, то find по умолчанию добавляет "-print", чтобы напечатать имя файла. Т.е. команда

find / -name '*.txt'
реально работает как

find / -name '*.txt' -print

Наиболее часто требуется действие "-exec", позволяющее выполнить с найденным файлом некую команду, чаще всего -- ls -l.

Пример (выдать "длинный" листинг всех файлов из директории /etc, в имени которых содержится "pass"):

bobby:~% find /etc -name '*pass*' -type f -exec ls -l '{}' ';' 
-rw-r--r--   1 root     root         1362 Apr 16 20:21 /etc/passwd
-rwxr-xr-x   1 root     root           97 Oct 13  1998 /etc/X11/wmconfig/u
serpasswd
find: /etc/X11/xdm/authdir: Permission denied
-rw-r--r--   1 root     root          239 Apr 27  1998 /etc/pam.d/passwd
-rw-r--r--   1 root     root         1362 Apr 16 20:10 /etc/passwd-
-rw-r--r--   1 root     root         1363 Apr 16 20:20 /etc/passwd.OLD
bobby:~% _

Необходимое пояснение: "-exec" воспринимает все, что стоит после него и до слова ";", как команду, которую надо выполнить. Причем сочетание символов "{}" заменяется на имя файла. Поскольку и ";", и "{}" являются специальными символами для shell, их надо "закавычить".

Поиск по списку файлов диска -- locate

Команда locate ищет указанные имена в базе данных, содержащей список файлов. Формат вызова locate следующий:

locate ИМЯ [ИМЯ...]

Пример:

bobby:~% locate Monitors
/usr/X11R6/lib/X11/doc/Monitors
/usr/X11R6/share/Xconfigurator/MonitorsDB
bobby:~% _

При указании нескольких имен locate найдет файлы, соответствующие любому из имен:

bobby:~% locate xsnow xroach
/etc/X11/wmconfig/xsnow
/usr/share/icons/mini/mini-xsnow.xpm
/usr/X11R6/bin/xsnow
/usr/X11R6/include/X11/bitmaps/xsnow
/usr/X11R6/man/man1/xsnow.1x
/usr/X11R6/bin/xroach
/usr/X11R6/man/man1/xroach.man
/var/catman/X11R6/cat1/xroach.man.gz
bobby:~% _

Если ИМЯ не содержит специальных символов шаблона, то locate находит все файлы, в любом месте полного имени которых (т.е. включая директорию) встречается указанная строка. Т.е. locate в этом случае работает просто как grep по списку файлов.

Если же ИМЯ содержит символы "*", "?" или "[]", то locate находит только строки, которые точно соответствуют шаблону.

Например, команда

locate '/usr/X11R6/*jewel'
покажет все файлы из директории /usr/X11R6, имя которых заканчивается на "jewel", а именно программу xjewel, но не man-страницу по ней:

bobby:~% locate '/usr/X11R6/*jewel'
/usr/X11R6/bin/xjewel
bobby:~% _

Для того же, чтобы увидеть все файлы из /usr/X11R6, в имени которых встречается "jewel", надо в конце шаблона также поставить "*":

bobby:~% locate '/usr/X11R6/*jewel*'
/usr/X11R6/bin/xjewel
/usr/X11R6/man/man1/xjewel.1x
bobby:~% _

В любом случае символы "." и "/" не являются особенными. Таким образом, шаблон "*et*pass*" найдет файл /etc/passwd.

Предупреждение
По умолчанию в базу данных, которую просматривает locate, попадают лишь файлы с корневой файловой системы (т.е. /). Если же, к примеру, все home-директории пользователей расположены на отдельном разделе (который монтируется, например, в /home), то администратор системы должен добавить этот раздел в список индексируемых.

В Linux индексирование (т.е. создание базы данных) выполняется программой updatedb, которая автоматически запускается ежедневно при помощи Cron. В RedHat Linux это выполняется из файла /etc/cron.daily/updatedb.cron. Таким образом, чтобы заставить updatedb индексировать, к примеру, раздел, смонтированный в /home, надо просто добавить его (через пробел) в список разделов в ключе "--localpaths" (т.е. чтобы было что-то типа "--localpaths='/ /home").

Замечание
Команда locate есть только в BSD-системах и в Linux, в системах клона SystemV она отсутствует.


Программа wget

Что такое wget

Wget -- это программа для "вытягивания" файлов из Internet при помощи протоколов HTTP или FTP.

Основные достоинства Wget:

  • Wget -- неинтерактивная программа, что позволяет запускать ее в фоновом режиме или при помощи at.
  • Wget может выкачивать файлы "рекурсивно", т.е. автоматически следовать по "ссылкам". Это позволяет, например, скачивать web-страницы вместе с имеющимися в них картинками, или выкачивать по FTP целые директории вместе с содержимым (включая поддиректории).
  • Wget умеет "докачивать" файлы после обрыва соединений, что особо ценно при медленной или нестабильной связи (хорошим примером которой является ситуация в Академгородке в настоящий момент).

Wget выдает список всех опций при вызове с ключом "--help", причем поскольку выдача довольно длинная, лучше пользоваться командой

wget --help | less

Для того, чтобы узнать все возможности Wget, следует заглянуть в info-документацию по нему.

Использование

Поскольку у wget есть огромное количество ключей, модифицирующих его поведение, мы рассмотрим на примерах несколько наиболее часто используемых случаев.

Простое скачивание файла


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

bobby:~/soft% wget ftp://rdist.inp.nsk.su/pub/Linux/redhat-5.2/i386/dosuti
ls/lodlin16.tgz
--15:23:43--  ftp://rdist.inp.nsk.su:21/pub/Linux/redhat-5.2/i386/dosutils
/lodlin16.tgz
           => `lodlin16.tgz'
Connecting to rdist.inp.nsk.su:21... connected!
Logging in as anonymous ... Logged in!
==> TYPE I ... done.  ==> CWD pub/Linux/redhat-5.2/i386/dosutils ... done.
==> PORT ... done.    ==> RETR lodlin16.tgz ... done.
Length: 87,210 (unauthoritative)

    0K -> .......... .......... .......... .......... .......... [ 58%]
   50K -> .......... .......... .......... .....                 [100%]

15:23:43 (604.01 KB/s) - `lodlin16.tgz' saved [87210]

bobby:~/soft% _

При скачивании каждого килобайта отображается один символ ".".

Можно указать подряд несколько URL -- в этом случае файлы будут скачаны один за другим.

Скачивание при плохой связи


Для того, чтобы wget повторял попытки взять файл до тех пор, пока не скачает его целиком, надо указывать ключи "-c" и "-t 0". Первый означает "продолжать качать с того места, где соединение оборвалось" (continue), а второй позволяет указать число попыток, 0 -- бесконечно.

Например, чтобы скачать исходные тексты проигрывателя .mp3-файлов X11Amp, можно воспользоваться командой

wget -c -t 0 http://www.x11amp.bz.nu/files/x11amp-0.9-beta1.1.tar.gz

Реально ключи "-c -t 0" стоит указывать практически всегда, кроме как разве что в локальной сети.

Шаблоны


При перекачке по FTP можно в именах файлов указывать шаблоны (естественно, "закавычивая" их). Например, команда

wget 'ftp://rdist.inp.nsk.su/pub/Linux/redhat-5.2/updates/i386/XFree86*.rpm'
скачает все обновления к системе XFree86.

Рекурсивное скачивание


Для рекурсивного скачивания используются ключи "-r", "-l" и "-np". Ключ "-r" (recursive) включает рекурсивность. Ключ "-l" (level) позволяет указать насколько "глубоко" программа должна следовать по ссылкам; 0 -- бесконечно глубоко, по умолчанию установлено 5. И, наконец, ключ "-np" (no parent) запрещает следовать ссылкам выше указанной директории.

При рекурсивном скачивании wget автоматически создает соответствующее дерево директорий.

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

wget -r -l 0 -np http://www.h-hosts.com/users/goofy/

При этом файлы будут помещаться в директорию ./www.h-hosts.com/users/goofy/.

Замечание
Wget, будучи культурной программой, при рекуррентном скачивании уважает т.н. "Протокол Web-роботов", и может пропускать часть файлов (запрещенных в файле robots.txt на сервере). Чтобы отключить эту "культурность", можно воспользоваться ключом "-e robots=off".

Почитать про Протокол Web-роботов можно в info-документации по Wget, воспользовавшись командой

info wget appendices robots
или по адресу

http://info.webcrawler.com/mak/projects/robots/robots.html

Фоновый режим


При указании ключа "-b" (background) программа сразу переходит в фоновый режим, так что пользователь может заниматься другими делами или даже выйти из системы -- wget будет продолжать свою работу.

При сообщения, которые выдавались бы на экран, будут идти в файл wget.log (а если он уже есть, то в wget.log.1, wget.log.2 и т.д.).

Пример:

bobby:~/soft% wget -b http://www.mit.edu/afs/sipb/user/xiphmont/cdpa
ranoia/download/cdparanoia-III-alpha9.5.src.tgz
Continuing in background.
Output will be written to `wget-log'.
bobby:~/soft% _

Для просмотра log-файла по мере скачивания удобно пользоваться командой "tail -f":

bobby:~/soft% tail -f wget-log
--16:16:45--  http://www.mit.edu:80/afs/sipb/user/xiphmont/cdparanoia/down
load/cdparanoia-III-alpha9.5.src.tgz
           => `cdparanoia-III-alpha9.5.src.tgz'
Connecting to www.mit.edu:80... connected!
HTTP request sent, awaiting response... 200 OK
Length: 92,595 [application/octet-stream]

    0K -> .......... .......... .......... .......... .......... [ 55%]
   50K -> ..._

Использование прокси-серверов


Wget умеет пользоваться прокси-серверами как для HTTP, так и для FTP. Достаточно перед вызовом программы присвоить адрес прокси-сервера переменным окружения http_proxy или ftp_proxy соответственно (имена маленькими буквами!).

Wget понимает как "официальное" указание адреса, так и сокращенное. Поэтому команды

export http_proxy=http://proxy.nsc.ru:8080/
и

export http_proxy=proxy.nsc.ru:8080
эквивалентны.

Скачивание "через перевалочную базу"


Современные академгородковские реалии таковы, что зачастую скачать файл с некоего зарубежного сервера напрямую не удается из-за плохой связи. При этом иногда не помогает даже способность Wget к докачке.

Можно обойти эту проблему, если есть доступ (по telnet, rlogin или ssh) к unix-эккаунту где-нибудь за рубежом, где связь намного лучше.

При этом выполняются следующие действия:

  1. При помощи Wget файл скачивается на зарубежную машину. Естественно, Wget на ней должен быть установлен. Если нет, то не является проблемой скомпилировать его из исходных текстов (см. пример в разделе "Установка программ из исходных текстов").
  2. Там его стоит упаковать, если это еще не сделано (а .html-файлы пакуются очень хорошо), и, возможно, разбить на несколько более мелких файлов про помощи программы split.
  3. Затем такой упакованный и/или разбитый файл скачивается с зарубежной машины к себе при помощи Wget по FTP или HTTP.

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

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


Работа с дискетами Dos

Пакет mtools

Для копирования информации между компьютерами и в качестве архива очень часто используются дискеты. Хотя в принципе у каждой операционной системы (Dos/Windows, MacOS, SunOS/Solaris, Linux и т.д.) есть свой формат для дискет, чаще всего используются дискеты с разметкой MS-Dos.

Хотя самый прямой способ работы с дискетами Dos в Linux -- это просто смонтировать дискету, например, командой

mount -t msdos /dev/fd0 /mnt/floppy
есть более удобное средство -- пакет Mtools.

Программы из этого пакета позволяют работать как с дискетами 3.5 и 5.25 дюйма, так и с дисками Zip и Jaz. Причем поддерживаются длинные имена файлов Windows95/NT (т.е. расширения файловой системы VFAT).

Пакет Mtools содержит следующие основные программы, выполняющие те же действия, что и аналогичные программы MS-Dos: mdir, mcopy, mren, mdel, mmd, mrd, mformat и еще некоторые.

Несколько примеров использования программ из пакета Mtools
КомандаКомментарий
mdir a:Показать директорию диска A:
mdel a:/report.txtУдалить с диска A: файл report.txt
mcopy a:/misc/nc110.tgz ./Скопировать с диска A: из директории misc файл nc110.tgz в текущую директорию
mmd a:/papers
mcopy conf99.tex a:/papers
Создать в корне диска A: директорию papers и затем скопировать туда из текущей директории файл conf99.tex
mformat b:Отформатировать диск B:

Несколько общих правил и отличий от MS-Dos:

  • Вместо символа "\" (обратная косая черта) в качестве разделителя директорий следует использовать "/" (прямая косая черта), т.к. "\" является специальным символом для shell.
  • Шаблоны имен Dos, содержащие "*" и "?" следует "закавычивать", т.е. вместо команды

    mdir a:*.txt
    следует использовать

    mdir "a:*.txt"

    Кроме того, Mtools следует принятому в Unix соглашению о шаблонах -- "любой файл" это не "*.*", а просто "*".

  • По умолчанию A: означает первый флоппи-диск (устройство /dev/fd0), а B: -- второй (устройство /dev/fd1), причем в Linux тип диска (3.5/5.25) и его разметка определяются автоматически.
  • При доступе к дискетам программы из Mtools обращаются к соответствующим устройствам из директории /dev/, при этом используются права доступа этих устройств. По умолчанию доступ на чтение открыт всем, а доступ на запись -- только пользователю "root" и группе "floppy" (которая пуста):

    bobby:~% ls -l /dev/fd?
    brw-rw-r--   1 root     floppy     2,   0 May  6  1998 /dev/fd0
    brw-rw-r--   1 root     floppy     2,   1 May  6  1998 /dev/fd1
    bobby:~% _
    

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

    bobby:~% mcopy a:autoexec.bat .
    bobby:~% mcopy files.bbs a:
    Can't open /dev/fd0: Permission denied
    Cannot initialize 'A:'
    Bad target a:
    bobby:~% _
    

    Чтобы дать возможность записи, надо или открыть эти устройства всем на запись командой

    chmod 666 /dev/fd?
    или, что гораздо правильнее, занести тех, кто должен иметь возможность писать на дискеты, в группу "floppy"; всем остальным при этом лучше даже убрать право на чтение --

    chmod 660 /dev/fd?
  • Форматирование дискет.
    • В отличие от команды format в Dos, команда mformat не выполняет низкоуровневого форматирования (т.е. mformat -- это эквивалент команды Dos "format /q"), поэтому оно должно быть выполнено заранее. В Unix для этого служит программа fdformat.
    • Программе fdformat следует указывать "плотность" дискеты. Это делается путем указания правильного файла устройства -- к примеру, /dev/fd0H1440 означает "дискета на 1.44М".
    • В свою очередь, программе mformat надо указывать количество дорожек, секторов и сторон (эти числа пишет fdformat при низкоуровневом форматировании). Это делается при помощи ключей "-t", "-s" и "-h" соответственно.
    • Кроме того, mformat не проверяет диск на испорченные блоки (bad-блоки), для этого служит программа mbadblocks. Впрочем, практически любой Unix (и Linux здесь не исключение) очень плохо относится к испорченным дискетам, так что лучше их просто выкидывать.

Детали по использованию каждой конкретной программы следует смотреть на ее man-странице. Кроме того, достаточно полное описание содержится в info-документации по mtools.


Практические задания
  1. Сделать псевдоним с именем "l", который вызывал бы команду less с ключами "-i" и "-S" и поместить его в свой файл .zshrc.
  2. Создать функцию с именем "ds", которая выполняла бы команду "du -s" для указанных директорий, и поместить ее в свой файл .zshrc.
  3. Заставить Cron в текущем часу каждые пять минут выполнять команду w.
  4. Прочитав в man-странице crontab(5) про указание "шагов", заставить Cron в текущем часу каждые две минуты выполнять команду uptime.
  5. При помощи find найти в директории /etc/ все файлы, в имени которых встречается "syst".
  6. Сделать то же самое при помощи locate.
  7. При помощи wget начать скачивать файл

    ftp://rdist.inp.nsk.su/pub/Linux/redhat-5.2/i386/images/boot.img
    и когда будет скачано около половины, прервать перекачку, нажав <Ctrl+C>. Затем докачать файл.
  8. При помощи wget одной командой скачать в директорию /tmp/ все .exe-файлы из директории

    ftp://rdist.inp.nsk.su/pub/Linux/redhat-5.2/i386/dosutils/
  9. При помощи wget скачать в директорию /tmp/ целиком директорию

    ftp://rdist.inp.nsk.su/pub/Linux/redhat-5.2/i386/doc/rhmanual/
  10. При помощи at заставить wget скачать в поддиректорию htmls/ в своей home-директории одиночную страницу по адресу http://www.inp.nsk.su/people/ через 5 минут. (Естественно, htmls/ предварительно надо создать.)
  11. Временно став пользователем "root", сделать себе доступ на запись к диску A:.
  12. Выполнить низкоуровневое форматирование дискеты, а затем отформатировать ее в формате MS-Dos (при этом надо будет указать команде mformat число сторон, дорожек и секторов; воспользоваться значениями, которые напишет программа низкоуровневого форматирования).
  13. Скопировать файл /etc/termcap на дискету.
  14. Скопировать директорию /etc/X11/AnotherLevel на дискету.
  15. Скопировать ту же директорию с дискеты в директорию /tmp/.

----------------------------------------

© 1999 Дмитрий Болховитянов