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