UNIX — страница 28 из 115

chmod +x filename

Единственное, что требуется выяснить — как сообщить команде

cx
имя файла, так как при каждом запуске
cx
оно будет иным.

Если интерпретатор выполняет командный файл, то каждое вхождение

$1
заменяется первым аргументом, каждое вхождение
$2
 — вторым и т.д. до
$9
. Поэтому если файл
cx
содержит строку

chmod +x $1

то при выполнении команды

$ cx nu

порожденный интерпретатор заменит "

$1
" на первый аргумент "
nu
". Рассмотрим всю последовательность операций:

$ echo 'chmod +x $1' >cx      
Вначале создадим cx

$ sh cx сх                    
Сделать сам файл cx выполняемым

$ echo echo Hi, there! >hello 
Приготовим тест

$ hello                       
Попробуем

hello: cannot execute

$ cx hello                    
Сделаем файл выполняемым

$ hello                       
Попробуем снова

Hi, there!                    
Работает

$ mv cx /usr/you/bin          
Установим команду cx

$ rm hello                    
Уберем ненужное

$

Заметьте, что мы задали

$ sh cx сх

в точности так, как сделал бы автоматически интерпретатор, если бы

cx
была выполняемой и можно было бы задать

$ cx сх

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

cx
воздействовать сразу на несколько файлов? Прямолинейное решение состоит в том, чтобы включить девять аргументов в командный файл:

chmod +x $1 $2 $3 $4 $5 $6 $7 $8 $9

(Это годится только для девяти аргументов, так как конструкция

$10
распознается как "первый аргумент, за которым следует 0"!) Если пользователь такого командного файла задаст меньше девяти аргументов, то недостающие окажутся пустыми строками. Это приведет к тому, что только настоящие аргументы будут переданы
chmod
порожденным интерпретатором. Такое решение, конечно, приемлемо, но не вполне корректно и не подходит для случая с числом аргументов более девяти.

С учетом упомянутой выше трудности интерпретатор предоставляет сокращенную запись

$*
, означающую "все аргументы". В этом случае правильно определить
cx
:

chmod +x $*

что является эффективным при любом числе аргументов.

Используя

$*
в своем репертуаре, вы можете создать некоторые полезные командные файлы, такие, как
lc
или
m
:

$ cd /usr/you/bin

$ cat lc

#lc: подсчет числа строк в файлах

wc -l $*

$ cat m

#m: точный способ послать почту

mail $*

$

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

$*
будет пустым, и
wc
и
mail
вообще не получат никаких аргументов. С аргументами или без них команда вызывается правильно:

$ lc /usr/you/bin/*

 1 /usr/you/bin/cx

 2 /usr/you/bin/lc

 2 /usr/you/bin/m

 1 /usr/you/bin/nu

 2 /usr/you/bin/what

 1 /usr/you/bin/where

 9 total

$ ls /usr/you/bin | lc

 6

$

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

/bin
, поэтому вряд ли они должны стать общедоступными. В гл. 5 мы исследуем вопрос создания общедоступных программ на языке
shell
.

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

/usr/you/lib/phone-book
, содержащий строки следующего вида:

dial-a-joke      212-976-3838

dial-a-prayer    212-246-4200

dial santa       212-976-3636

dow jones report 212-976-4141

то для поиска в нем можно воспользоваться командой

grep
. (Ваш собственный каталог
lib
— хорошее хранилище таких частных баз данных.) Поскольку команда
grep
не определяет формат информации, можно искать имена, адреса, индексы или еще какие-нибудь нужные вам сведения. Составим справочную программу для каталога, которой дадим имя
411
по номеру одной из телефонных справочных служб:

$ echo 'grep $* /usr/you/lib/phone-book' > 411

$ cx 411

$ 411 joke

dial-a-joke      212-976-3838

$ 411 dial

dial-a-joke      212-976-3838

dial-a-prayer    212-246-4200

dial santa       212-976-3636

$ 411 'dow jones'

grep: can't open jones
Что-то не так

$

Последний пример вскрывает потенциальную проблему: хотя

dow jones
представляет для команды
411
единый аргумент, он содержит пробел и уже не заключен в апострофы, поэтому порожденный интерпретатор, выполняющий команду
411
, преобразует его в два аргумента для
grep
, как если бы вы задали

$ grep dow jones /usr/you/lib/phone-book

что, очевидно, неверно.

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

'...'
, не затрагивается, интерпретатор "заглядывает" внутрь
"..."
в поиске комбинаций с
$
,
\
,
`...`
. Поэтому если изменить команду 411 следующим образом:

$ grep "$*" /usr/you/lib/phone-book

то

$*
заменяется на аргументы, но команде
grep
передается как один аргумент, даже при наличии пробелов:

$ 411 dow jones

dow jones report 212-976-4141

$

Кстати, можно сделать с помощью флага

-y
команду
grep
(а значит, и
411
) независимой от использования строчных или прописных букв:

$ grep -y pattern ...

При наличии флага

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

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

$0
— это имя выполняемой программы; в случае
cx $0
есть
"cx"
. Новое применение
$0
находит в реализации программ
2
,
3
,
4
, …, которые печатают свой выходной поток в несколько столбцов:

$ who | 2

drh tty0 Sep 28 21:23 cvw tty5 Sep 28 21:09

dmr tty6 Sep 28 21:10 scj tty7 Sep 28 22:11

you tty9 Sep 28 23:00 jib ttyb Sep 28 19:58

$

Реализация команд

2
,
3
, … идентична. По существу, они являются связями с одним файлом:

$ ln 2 3; ln 2 4; ln 2 5; ln 2 6

$ ls -l [1-9]

167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 2

167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 3

167222 -rwxrwxrwx 5 you 51 Sep 28 23:21 4