Мы советуем вам не создавать каталогов больше, чем вам необходимо. Вы должны знать, что существуют несколько специальных размеров каталогов. Каталог, содержащий до 30 файлов (считая файлы . и .), умещается на одном блоке диска и может быть просмотрен очень быстро. Каталог, содержащий до 286 файлов, еще можно считать маленьким, использование каталога большего размера в качестве рабочего уже связано с определенными неудобствами. Мы не советуем делать рабочие каталоги больше, чем стандартные. Особенно важно сохранять загрузочный каталог маленьким, желательно размером в 1 блок. Помните, что каталоги, как правило, не ужимаются. Если ваш каталог содержит более 30 или 286 файлов, то поиск будет выполняться неэффективно. Более того, даже если вы удалите файлы так, что их у вас станет меньше, чем указанные пределы, операционная система все равно будет просматривать каталог неэффективно.
Возможности командного языка Shell наиболее явно чувствуются при виде того, как легко можно объединить много утилит для выполнения нужных команд. В этом разделе приводятся примеры процедур. Изучая эти примеры, вы приобретете навыки программирования процедур Shell. Обратите внимание на то, что все процедуры начинаются с команды (:), а символ # открывает строку комментариев.
Для каждой процедуры надо выполнить следующие действия:
1. Создайте файл с именем процедуры и запишите ее туда.
2. Измените права доступа к файлу с помощью команды chmod.
3. Переместите файл в каталог, где хранятся команды, например, в ваш bin.
4. Убедитесь, что в переменной PATH имеется имя вашего ката лога bin.
5. Выполните указанную команду.
BINUNIQ
:
ls /bin /usr/bin | sort | uniq -d
Эта процедура определяет, какие файлы с одинаковыми именами находятся в каталогах /bin и в /usr/bin. Файлы в каталоге /bin в большинстве случаев заменяют собой одноименные файлы в каталоге /usr/bin.
COPYPAIRS
:
# Формат: copypairs file1 file2 ...
# Копирует file1 в file2, file3 в file4, ...
while test "$2" != ""
do
cp $1 $2
shift; shift
done
if test "$1" !=""
then echo "$0: odd number of arguments" >&2
fi
В этой процедуре показывается, как использовать цикл while для обработки списка позиционных параметров, определенным образом связанных друг с другом. Здесь цикл while предпочтительнее цикла for, поскольку с помощью команды shift можно производить настройку позиционных параметров на обработку связанных с ними аргументов.
COPYTO
:
# Формат: copyto dir file ...
# Копирует перечисленные файлы в каталог "dir"
# при условии, что указано не менее двух
# аргументов, что "dir" является каталогом,
# и что каждый последующий аргумент
# представляет собой файл, доступный для чтения.
if test $# -lt 2
then echo "$0: usage: copyto directory file ...">&2
elif test ! -d $1
then echo "$0: $1 is not a directory";>&2
else dir=$1; shift
for eachfile
do cp $eachfile $dir
done
fi
Эта процедура использует команду if для вывода сообщений о неправильном применении процедуры. Цикл for в конце процедуры просматривает все аргументы, кроме первого.
DISTINCT1
:
# Формат: distinct1
# Читает стандартный ввод и выводит список
# алфавитно-цифровых строк, различающихся только
# размером букв, представляя их в строчной форме
tr -cs 'A-Za-z0-9' '\012' | sort -u |\
tr 'A-Z' 'a-z' | sort | uniq -d
Эта процедура является примером процесса, создаваемого конвейером. Символ \ в конце первой строки означает, что следующая строка является продолжением. На первый взгляд представляется неясным, как эта команда работает. С принципами работы команд tr, sort и uniq можно ознакомиться в User's Reference. Команда tr преобразует все символы, кроме букв и цифр, в символы "новая строка", после чего уплотняет повторяющиеся символы. В результате каждый набор литер окажется в отдельной строке. Команда sort сортирует строки и оставляет из любой последовательности повторяющихся строк только одну строку. Следующая команда tr преобразует все буквы в строчные, делая тем самым идентификаторы, отличающиеся лишь величиной букв, одинаковыми. Вывод вновь сортируется, все дубликаты собираются вместе. Команда "uniq -d" формирует перечень строк, встречающихся в тексте более одного раза.
Процесс построения подобного конвейера основан на том факте, что каналы и файлы могут взаимозаменяться. Первая из нижеприведенных строк эквивалентна двум последующим при условии наличия достаточного дискового пространства:
cmd1 | cmd2 | cmd3
cmd1 > temp1; < temp1 cmd2 > temp2; < temp2 cmd3
rm temp[123]
Подав на стандартный ввод файл с тестовыми данными и приступив к выполнению команд слева направо, мы создаем конвейер, в котором каждая команда считывает данные из предыдущего файла и записывает результат в следующий файл. Цель - создать серию преобразований, превращающих исходные данные в требуемый результат.
DRAFT
:
# Формат: draft file(s)
# Вывод страниц на принтер.
for i in $*
do nroff -man $i | lpr
done
Пример использования явных флагов, не имеющих значений по умолчанию.
EDFIND
:
# Формат: edfind file arg
# Ищет последнее появление в файле "file" строки,
# начинающейся с подстроки "arg", после чего
# выводит 3 строки (предыдущую, найденную,
# последующую)
ed - $1 << -EOF
?^$2?
-,+p
q
EOF
Иллюстрация использования команды ed для редактирования исходных текстов, в которых Shell выполняет подстановку значений переменных.
EDLAST
:
# Формат: edlast file
# Выводит последнюю строку файла
# и удаляет ее.
ed - $1 << -\!
$p
$d
w
q
!
echo done
Иллюстрация считывания данных из файла до восклицательного знака.
FSPLIT
:
# Формат: fsplit file1 file2
# Чтение стандартного ввода и разбиение его на 3 части
# с добавлением строки, содержащей не менее одной буквы,
# к файлу file1, добавления другой строки с цифрами, но
# без букв к файлу file2, и сбрасыванием остального.
count=0 gone=0
while read next
do
count="`expr $count +1`"
case "$next" in
*[A-Za-z]*)
echo "$next" >> $1 ;;
*[0-9]*)
echo "$next" >> $2 ;;
*)
gone="`expr $gone + 1`"
esac
done
echo "$count lines read, $gone thrown away"
На каждой итерации цикла из вводного потока считывается строка и анализируется. Цикл завершается только тогда, когда команда read обнаружит конец файла. Обратите внимание на использование команды expr.
LISTFIELDS
:
grep $* | tr ":" "\012"
Эта процедура выводит в виде строк свой аргумент. Она заменяет каждый символ ":" на символ новой строки. Так, если в качестве аргумента указать:
joe newman: 13509 NE 78th St: Redmond, Wa98062
то эта процедура выведет:
joe newman
13509 NE 78th St
Redmond, Wa98062
MKFILES
:
# Формат: mkfiles pref [quantity]
# Создает файлы "quantity".
# По умолчанию - 5, как показано на следующей строке.
quantity=${2-5}
i=1
while test "$i" -le "quantity"
do
> $1$i
i="`expr $i + 1`"
done
Процедура mkfiles использует переадресацию вывода для создания файлов нулевой длины. Команда expr используется для подсчета числа итераций в цикле while.
NULL
:
# Формат: null files
# Создает каждый из указанных файлов как пустой.
for eachfile
do
>$eachfile
done
Процедура использует тот факт, что в результате переадресации вывода, если выводного файла с таким именем нет, создается пустой выводной файл.
PHONE
:
# Формат: phone initials ...
# Вывод номеров телефонов людей с
# указанными инициалами.
echo 'initsext home'
grep "$1" <<END
jfk 1234 999-2345
lbj 2234 583-2245
hst 3342 988-1010
jqa 4567 555-1234
END
Пример использования встроенной программы для поддержки не большой базы данных.
TEXTFILE
:
if test "$1"= "-s"
then
# Возвращает код завершения
shift
if test -z "`$0 $*`" # проверка возвращаемого значения
then
exit 1
else
exit 0
fi
fi
if test $# -lt 1
then echo "$0: Usage: $0 [ -s ] file ..." 1>&2
exit 0
fi
file $* | fgrep 'text' | sed 's/:.*//'
Чтобы определить, какие файлы в каталоге содержат только текстовую информацию, textfile фильтрует списки аргументов для других команд. Например, следующая командная строка напечатает все текстовые файлы в текущем каталоге:
pr `textfile *` | lpr
В процедуре используется флаг -s, отбирающий из списка текстовые файлы.
WRITEMAIL
:
# Формат: writemail message user
# Если пользователь зарегистрировался,
# сообщение выводится на терминал;
# в противном случае, посылается пользователю.
echo "$1" | { write "$2" || mail "$2" ; }
Иллюстрация использования командной группировки. Сообщение, обозначенное как "$1", передается команде write, и, в случае неудачного завершения последней, команде mail.