Для поиска файлов с интересующими нас правами доступа можно при-
менить другой метод, хотя и более медленный. Он основывается на том,
что вместо поиска прав доступа по числу, можно искать их по символь-
ной строке. Для этого нужно применять grep. Альтернативная команда
выглядит так:

# find $* -exec ls -ld {} \; | grep "^[^ ]*s[^ ]*"

Этот вариант команды работает несколько иначе. Он находит каждый
файл из указанных каталогов и применяет к каждому из них команду "ls
-ld". Затем весь список данных передается по конвейеру команде grep.
Grep использует для распознавания интересующих нас файлов такой шаб-
лон поиска: начиная с начала строки, найти повторяющийся символ, от-
личный от пробела, затем символ "s", за которым следует повторяющийся
символ, отличный от пробела. Такому шаблону соответствуют все режимы
прав доступа, содержащие s, будь то бит установки пользовательского
идентификатора или бит установки группового идентификатора. Не су-
щественно, входят ли в данный режим "r", "w", "x" или "-". Убедив-
шись, что пробелов нет, мы обеспечиваем соответствие шаблона только
полю прав доступа. Важно, чтобы после s следовали символы, отличные
от пробелов, так как s может встретиться либо в порции прав доступа,
относящейся к владельцу, либо в групповой порции и нигде больше.
Мы не очень рекомендуем этот метод, поскольку он привлекает
очень интенсивную обработку. Однако, если бы мы не испробовали этот
метод первым, мы бы не оценили, что использование команды find со
строками "-perm" является более предпочтительным. В силу невероятной
гибкости системы UNIX, имеется очень много различных способов выпол-
нения одной и той же работы, и многие из этих способов могут давать
одинаково хорошие результаты, но за разную цену в смысле быстродейс-
твия и процессорных затрат. Перед тем как применять универсальную
утилиту вроде grep для решения задачи, рассмотрите, нет ли другой ко-
манды со встроенной опцией распознавания по шаблону. Скорее всего,
применение такой команды окажется оптимальнее и гораздо быстрее, чем
использование grep. С другой стороны, иногда вам нужно быстро решить
нечасто встречающуюся задачу, не слишком заботясь об эффективности
решения.


--------------------------------------------------------
ИМЯ: suw
--------------------------------------------------------

suw

    НАЗНАЧЕНИЕ



Просматривает протокольный файл команды su и печатает имена всех
пользователей, которые нелегально превратились в суперпользователя
при помощи команды su (substituted user, замененный пользователь).


    ФОРМАТ ВЫЗОВА



suw [-m] [sulog]


    ПРИМЕР ВЫЗОВА



suw Запуск в режиме по умолчанию, проверка файла
/usr/adm/sulog и выдача записей о нарушителях
в стандартный вывод.

    ТЕКСТ ПРОГРАММЫ



1 static char id[]="@(#)suw v1.0 Author: Russ Sage";

3 # include

5 # define FALSE 0
6 # define TRUE 1
7 # define MATCH 0
8 # define BSIZ 80

10 main(argc,argv)
11 int argc;
12 char *argv[];
13 {
14 register int alert, c, mail, n;
15 FILE *fp1, *fp2;
16 char *p, *uname, line[BSIZ], tmp[BSIZ],
17 *log = "/usr/adm/sulog";

19 static char *legal[] = {sage-root\n","root-root\n",NULL};
20 static char *adm[] = {"sage",NULL};


23 mail = FALSE;

25 if (argc > 1 && argv[1][0] == '-')
26 switch (argv[1][1])
27 {
28 case 'm':
29 mail = TRUE;
30 --argc;
31 ++argv;
32 break;
33 default:
34 fprintf(stderr,"suw: invalid argument %s\n",
argv[1]);
35 fprintf(stderr,"usage: suw [-m] [sulog]\n");
36 exit(1);
37 }

39 if (argc == 2)
40 log = *++argv;

42 if ((fp1 = fopen(log,"r")) == NULL)
43 {
44 fprintf(stderr,"suw: error opening %s\n",log);
45 fprintf(stderr,"usage: suw [-m] [sulog]\n");
46 exit(1);
47 }

49 sprintf(tmp,"/tmp/suw%d",getpid());
50 if ((fp2 = fopen(tmp,"w+")) == NULL)
51 {
52 fprintf(stderr,"suw: error opening %s\n",tmp);
53 fprintf(stderr,"usage: suw [-m] [sulog]\n");
54 exit(1);
55 }

57 while (fgets(line,sizeof(line),fp1) != NULL)
58 {
59 p = line + 15;
60 if (*p == '+')
61 {
62 p = p + 2;
63 while (*p != ' ') p++;
64 p++;
65 uname = p;
66 while (*p && *p++ != '-')
67 continue;

69 if (strcmp (p,"root\n") == MATCH)
70 {
71 alert = TRUE;
72 for (n=0; legal[n] != NULL; n++)
73 if (strcmp (uname,legal[n])
== MATCH)
74 {
75 alert = FALSE;
76 break;
77 }
78 if (alert)
79 fprintf(fp2,"Illegal --> %s",
line);
80 }
81 }
82 }

84 if (mail)
85 {
86 fclose(fp2);
87 for (n=0; adm[n] != NULL; n++)
88 {
89 sprintf(line,"cat %s | mail %s",tmp,adm[n]);
90 system(line);
91 }
92 }
93 else
94 {
95 rewind(fp2);
96 while ((c = getc(fp2)) != EOF)
97 putc(c, stdout);
98 fclose(fp2);
99 }

101 fclose(fp1);
102 unlink(tmp);
103 }


ОПИСАНИЕ

ЗАЧЕМ НАМ НУЖНА ПРОГРАММА suw?

Вы помните, что команда su, позволяющая пользователям изменять
свою индивидуальность (и права доступа) может быть источником проблем
безопасности. Система хранит протокол всех транзакций su в файле
sulog. Хотя более опытные нарушители могут уметь затирать свои следы,
файл sulog полезен для отслеживания потенциальных лазеек в системе
защиты. Этим способом можно поймать многих нарушителей-любителей. Ес-
тественно, мы хотим автоматизировать этот процесс, чтобы система вы-
полняла проверку и сигнализировала нам при обнаружении чего-либо
опасного. Кроме того, данная программа демонстрирует методику, кото-
рую можно использовать для отслеживания других протокольных файлов.


ЧТО ДЕЛАЕТ suw?

Программа suw читает и анализирует протокольные файлы команды
su. Каждое успешное превращение в суперпользователя при помощи коман-
ды su, обнаруженное в протокольном файле, сверяется со списком разре-
шенных суперпользователей. Если пользователю не разрешено быть супер-
пользователем, то конкретная запись о нем печатается, чтобы
оповестить администратора.
По умолчанию записи о нарушителях печатаются в стандартный вы-
вод. Протокольным файлом по умолчанию является /usr/adm/sulog. Если
применяется опция -m, то записи о нарушителях рассылаются по почте
администраторам, занесенным в предопределенный список. Если нужно
проверить другой протокольный файл, например /usr/adm/Osulog, то его
имя можно указать в командной строке.


    ПРИМЕРЫ



1. # suw -m

Проверить файл /usr/adm/sulog и разослать записи о нарушителях
администраторам, определенным в тексте программы.

2. # suw /usr/adm/Osulog

Проверить файл /usr/adm/Osulog и напечатать записи о нарушителях
в стандартный вывод.


    ПОЯСНЕНИЯ



В самом начале программы определяются и инициализируются все пе-
ременные и списки. В строке 14 определены два флага: alert (сигнал
тревоги) и mail (почта). Они имеют значение TRUE или FALSE. В этой
программе используются два файла: протокольный файл команды su, кото-
рый вы выбираете, и временный файл. Поскольку мы собираемся применять
некоторые подпрограммы стандартного ввода-вывода (stdio), мы пользу-
емся указателями на файлы fp1 и fp2, а не дескрипторами файлов. При-
меняется два буфера: один для зачитывания в него данных из протоколь-
ного файла команды su, а другой - для хранения имени временного
файла. Первоначально именем протокольного файла является sulog. Если
вместо него используется другое имя файла, то переменная log переус-
танавливается на это имя.
Затем инициализируются предопределенные списки разрешенных су-
перпользователей и администраторов. В нашем примере двумя разрешенны-
ми суперпользователями являются sage (sage-root) и root (root-root).
Для того чтобы приспособить это для вашей системы, поместите здесь
имена людей, которым вы хотите разрешить пользоваться командой su для
превращения в суперпользователей. Список администраторов также должен
содержать соответствующие имена. В данном примере в список админист-
раторов входит одно имя - sage. Это значит, что sage будет единствен-
ным, кто получит почту, если указана почтовая опция.
Подразумеваемое состояние рассылки почты устанавливается в стро-
ке 23 на значение FALSE, т. е. почты нет. Это делается для того, что-
бы после разбора командной строки переменная mail имела правильное
значение.
Далее выполняется проверка на ошибки. Первая проверка выглядит
несколько странной, но на самом деле вполне понятна. Переменная argc
возвращает число аргументов командной строки, а argv указывает на
массив, содержащий сами аргументы, каждый из которых сам является
массивом символов. Если командная строка вообще имеет какие-либо ар-
гументы (argc > 1) и первым символом первого аргумента (argv[1][0])
является дефис, то проверяется, корректная ли это опция. С целью про-
верки второго символа первого аргумента используется оператор case.
Если аргументом является символ m, то флаг mail устанавливается
в состояние TRUE, число аргументов (argc) уменьшается на единицу, а
указатель на массив аргументов (argv) на единицу увеличивается. Это
служит той же цели, что и удаление аргументов из командной строки в
командных файлах интерпретатора shell. Напомним, что argv является в
действительности массивом указателей, а массивы трактуются точно так
же, как строки, с базовым адресом и смещением. Поэтому argv[0] ==
argv, и argv[1] == ++argv. Если опция отличается от -m, то в стан-
дартный вывод печатается сообщение об ошибке и программа завершается.
В строке 39 проверяется счетчик аргументов, чтобы понять, есть
ли еще один аргумент. Напомним, что argv всегда на единицу отстает от
argc, потому что само имя команды является первым аргументом массива.
Следовательно, для того чтобы получить второй аргумент, мы должны пе-
рейти к следующей позиции (*++argv). Если аргумент имеется, он должен
быть именем протокольного файла, и это имя заносится в переменную
log.
Далее мы пытаемся открыть файлы, используемые в программе. Сна-
чала открывается для чтения протокольный файл. Если это не срабатыва-
ет (возвращен нулевой указатель), то печатается сообщение об ошибке и
программа завершается. Затем в строке 49 создается имя временного
файла с добавлением к нему идентификатора процесса, чтобы гарантиро-
вать уникальность имени файла. Этот файл открывается на чтение и за-
пись. Если эта операция не удается, печатается сообщение об ошибке и
выполнение завершается.
В строках 57-103 заключен главный цикл. Управляющая часть глав-
ного цикла определяется тем, все ли данные прочитаны из протокольного
файла. Если больше никакие байты прочитать нельзя, то fgets (строка
57) возвращает нулевой указатель, что завершает цикл while. Когда
строка данных читается из протокольного файла, эти данные помещаются
в массив line. Мы применяем указатель на символ для прохода вдоль
строки и поиска записи о нарушителе.
Изучая запись в вашем файле sulog, вы можете увидеть, где нахо-
дятся интересующие нас поля. Сначала указатель смещается на 15 симво-
лов от начала строки. Это позиция флага, определяющего, была ли ус-
пешной команда su. Если она была успешной, указатель увеличивается на
два, чтобы пропустить пробел и установить указатель на имени термина-
ла. Это имя имеет переменную длину, поэтому мы ориентируемся по про-
белу в конце имени. В строках 63-64 применяется цикл while, который
заставляет указатель пропустить все символы, отличные от пробела. За-
тем он увеличивается на единицу еще раз, чтобы пропустить пробел меж-
ду именем терминала и строкой имени пользователя.
В этот момент указатель находится в последнем поле строки. Ука-
затель на это поле помещается в переменную uname, чтобы затем ее мож-
но было использовать для сравнения строк. Следующий цикл while (стро-
ки 66-67) проходит по строке, пока не попадет на символ дефиса. Цикл
while выполняется до тех пор, пока указатель не указывает на конец
строки (т.е. на нелевой указатель, *p == '\0') и еще не указывает на
дефис. Поскольку в цикле используется p++, то когда мы попадаем на
нулевой указатель, цикл завершается, причем p указывает на следующий
символ.
Выполняется проверка, был ли суперпользователем тот, кто приме-
нил команду su. Если был, то взводится флаг alert, и мы должны прове-
рить, разрешенный ли это суперпользователь. Цикл for (строки 72-77)
пробегает по всем именам в массиве, содержащем имена разрешенных
пользователей, до тех пор, пока мы не обнаружим последнюю запись, ко-
торая является пустой. Строка с именем пользователя, установленная
предварительно, сверяется с каждой записью в массиве разрешенных
имен. Если они совпадают, то мы можем предположить, что с данным при-
менением команды su все в порядке. Тогда мы выключаем флаг alert и
завершаем сравнение имен. Если же по окончании цикла флаг alert все
еще взведен, значит имя не содержится в списке разрешенных - это,
возможно, нарушитель. Вся запись целиком записывается во временный
файл и управление передается назад в начало цикла для следующей опе-
рации чтения.
Когда все данные прочитаны, цикл while завершается. В строке 84
проверяется, взведен ли флаг mail. Если да, то временный файл закры-
вается (чтобы сработала команда cat) и выполняется еще один цикл for
(строки 87-91). Он перебирает всех администраторов и завершается,
когда попадает на нулевой указатель в конце массива. Для каждого из
обозначенных администраторов конструируется команда mail и посылается
в систему UNIX при помощи системного вызова (строка 90). Метод отп-
равки по почте временного файла заключается в применении к этому фай-
лу команды cat и передаче данных по конвейеру команде mail.
Если флаг mail выключен, то временный файл нужно напечатать в
стандартный вывод, а не отправить по почте. Для того чтобы сделать
это как можно быстрее, временный файл (который пока еще открыт) "пе-
рематывается" (мы позиционируемся в его начало) и посимвольно пропус-
кается по циклу. Обратите внимание, что данный цикл является сердце-
виной команды cat, как описано на странице 153 книги Кернигана и
Ритчи "Язык программирования Си" (B.W.Kernighan, D.M.Ritchie. The C
Programming Language). После того как временный файл напечатан, он
закрывается. Наконец, закрывается протокольный файл и удаляется вре-
менный файл.


    * ГЛАВА 10. Смешанные приемы *



Введение
Способы преобразования в языке shell
conv
Модули преобразования
dtoh
dtoo
htod
htoo
Тонкости bc
otod
otoh
Приемы языка shell для обеспечения гибкости программ
Хитрости языка shell
Читайте ввод с клавиатуры, пока находитесь в цикле,
присоединенном к програмному каналу
Запуск дочернего языка shell
Уровни языка shell и ввод-вывод
Встроенный ввод
С редактором ed
С файлом a.out
C архивами языка shell
Управление статусом цикла
Фильтры и синтаксис
Недостатки/особенности программирования на языке shell
Программа для перенаправления ошибки
Некорректный код возврата
Хитрости редактора Vi
Возвращение в язык shell
Поддержка Escape
Макросы
Команда "One-Liners" - крошечная, но мощная
.
- 2 -

Введение

Эта книга является итогом многолетней работы по подбору и
развитию инструментальных средств ОС UNIX. Многие вещи, которые не
хотелось бы оставлять без внимания, не вписались в контекст
предыдущих глав. Это и законченные процедуры, подобные представленным
ранее, и небольшие, но очень мощные фрагменты программ. Кроме того,
высказаны некоторые полезные идеи и представлены методы обработки
общих ситуаций на языке shell.

Способы преобразования

Поскольку компьютеры и их резидентные утилиты используют при
работе разные системы счисления, часто возникает необходимость
преобразования оснований систем счисления. Эти преобразования
обеспечиваются хорошо знакомыми специалистам командами UNIX bc
(калькулятор произвольной точности) и dc (которая предположительно
расшифровывается как настольный калькулятор ("desk calculator")).
Большинство из существующих возможностей либо носят очень
ограниченный характер, либо их тяжело использовать в ряде ситуаций,
поэтому будет рассмотрен вопрос как использовать существующие
возможности UNIX, чтобы любое преобразование было как можно более
легко осуществимым.

---------------------------------------------------------------------
Название: conv
---------------------------------------------------------------------

conv Переводит числа из одной системы счисления в другую

Назначение: Обеспечивает возможность преобразования основания системы
счисления

Вызов conv

Пример вызова:
$conv Вызвать главное меню различных преобразований

2 Выбрать опцию 2 ( из шестнадцатиричной в десятичную)

FFF Ввести шестнадцатиричное число FFF. На выходе программы
получим десятичный эквивалент


Исходный текст для функции conv

1 :
2 # @(#) conv v1.0 Преобразование основания системы
счисления, используя shell Автор: Russ Sage
3
4 while :
5 do
6 echo "
7
8 Преобразование оснований
9 ------------------------
10 1 - Десятичное в шестнадцатиричное
11 2 - Шестнадцатиричное в десятичное
12 3 - Десятичное в восьмеричное
13 4 - Восьмеричное в десятичное
14 5 - Восьмеричное в шестнадцатиричное
15 6 - Шестнадцатиричное в восьмеричное
16
17 enter choice (1-6, <>): \c"
18 read CHOICE
19
20 case $CHOICE in
21 "") exit;;
22 1) echo "\пВведите десятичное число (<> to exit): \c"
23 read DEC
24 if [ "$DEC" = ""]
25 then exit
26 fi
27 HEX='. dtoh'
28 echo "\n${DEC}d = ${HEX}x";;
29 2) echo"\nВведите шестнадцатиричное число
в верхнем регистре (<> to exit): \c"
30 read HEX
31 if [ "$HEX" = ""]
32 then exit
33 fi
34 DEC='. htod'
35 echo "\n${HEX}x= ${DEC}d;;
36 3) echo "\nВведите десятичное число в
верхнем регистре (<> to exit): \c"
37 read DEC
38 if [ "$DEC" = ""]
39 then exit
40 fi
41 OCT='. dtoo'
42 echo "\n${DEC}d = ${OCT}o";;
43 4) echo "\nВведите восьмеричное число
(<> to exit): \c"
44 read OCT
45 if [ "$OCT" = ""]
46 then exit
47 fi
48 OCT='. otod'
49 echo "\n${OCT}o = ${DEC}d";;
50 5) echo "\nВведите восьмеричное число
(<> to exit): \c"
51 read OCT
52 if [ "$OCT" = ""]
53 then exit
54 fi
55 HEX='. otoh'
56 echo "\n${OCT}o = ${HEX}x";;
57 6) echo "\nВведите шестнадцатиричное число
в верхнем регистре (<> to exit): \c"
58 read НЕХ
59 if [ "$НЕХ" = ""]
60 then exit
61 fi
62 OCT='. htoo'
63 echo "\n${HEX}x = ${OCT}o";;
64 *) echo "\n$CHOICE-неизвестная команда";;
65 esac
66 done

Переменные окружения

CHOICE - Выбор команд из главного меню
DEC - Выдает десятичное значение как результат
преобразования
HEX - Выдает шестнадцатиричное значение как

- 4 -
результат преобразования
OCT - Выдает восьмеричное значение как
результат преобразования

Описание

Зачем нам нужна функция conv ?

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

Что делает conv?

Это инструментальное средство обеспечивает возможность перевода
чисел из одной системы счисления в другую. Можно переводить
десятичные, шестнадцатиричные и восьмеричные данные. Число,
записанное в одной из этих форм, может быть переведено в любую из
двух оставшихся форм.
Режим работы программы выбирается из главного меню. В меню есть
шесть пунктов. После того как Вы выбираете число между 1 и 6,
программа просит Вас ввести число которое Вы хотите преобразовать.
Происходит преобразование и на выходе программы Вы получаете два
значения - число, которое Вы преобразовываете и число, к которому оно
было преобразовано. Преобразования осуществляются путем вызова
внешних процедур, о которых будет идти речь дальше в этой главе, так
что перед запуском conv необходимо убедиться, что Вы включили их в
вашу систему и разместили в том же каталоге, что и conv.
Если Вы введете команду, не вошедшую в вышеупомянутый перечень,
то будет выдано сообщение об ошибке и опять будет выведено главное
меню.

Пояснение

Строки 4-66 - это один большой бесконечный цикл while. Мы
используем бесконечный цикл, чтобы в случае ошибочного ввода
программа вернулась в главное меню для повторного ввода. Для того,
чтобы выйти из программы, нужно прервать цикл, т.е. выйти из цикла.
Строки 6-17 печатают меню и выдают подсказку для выбора. Если Вы
просто нажмете "Ввод", программа завершит свою работу.
Строка 18 читает ввод с клавиатуры, и строки 20-65 выполняют
выбор по условию для этой величины. Если получен нулевой (пустой)
ввод, то программа завершает свою работу.
Строки 22-28 осуществляют перевод чисел из десятичной в
шестнадцатиричную системы счисления. Поскольку все модули перевода
отвечают одному и тому же образцу, то детально мы рассмотрим только
данный модуль.
Подсказка запрашивает число в строке 23. В строках 24-26
проверяется, не было ли введенное значение пустым. Строка 27 выглядит
несколько загадочно, вызывая один из внешних командных файлов dtoh
для преобразования десятичных чисел в шестнадцатииричные. Обратите
внимание на то как одна программа выполняет другую.
Командный файл dtoh запускается, используя команду ".". Это
означает : "Выполните программу, используя тот же shell". Процедура
dtoh использует переменную DEC для ввода числа и выдает

- 5 -
преобразованное число на стандартный вывод. Чтобы записать это число
в переменную, мы делаем присвоение, потом запускаем программу,
используя командную подстановку. Строка 28 выдает на экран
первоначальное десятичное число и шестнадцатиричное, к которому оно
было преобразовано.
Варианты 2, 3, 4, 5 и 6 работают аналогично. Единственное, что
меняется - это имя переменной, которое соответствует типу
преобразования и название командного файла (скрипта), который
вызывается для этого преобразования.


Модули преобразования

Теперь давайте рассмотрим отдельно каждый из модулей перевода.
Эти модули или командные файлы языка shell используют команду UNIX
bc, чтобы осуществлять преобразования оснований систем счисления.
Нельзя сказать, что команда bc - это наиболее простой и удобный
способ перевода, но тем не менее она работает, и единственное, что
нам нужно, - это изучить ее и поместить в командный файл.

---------------------------------------------------------------------
Название : dtoh
---------------------------------------------------------------------

dtoh Десятичные в шестнадцатиричные.

Назначение: Преобразовывает входные десятичные числа в выходные
шестнадцатиричные числа.

Синтаксис: $DEC="decimal_number"; HEX='.dtoh'

Пример вызова

$DEC="25";HEX='.dtoh'
$echo $HEX

Присвоить DEC начальное значение 25, вызвать dtoh для его
преобразования и записать результат в HEX. Вывести результаты на
экран.


Исходный текст для dtoh

1 :
2 # @(#) dtoh v1.0 Преобразование языка shell--десятичные в
шестнадцатиричные Автор: Russ Sage
3
4 bc < 5 obase = 16
6 $DEC
7 !


Описание

Зачем нам нужен dtoh ?

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

Что делает dtoh?


- 6 -
Dtoh выполняет преобразование десятичных в шестнадцатиричные,
используя существующее базовое преобразование, которое UNIX
обеспечивает посредством команды bc. Манипулируя вводом и выводом
команды bc, мы можем использовать ее возможности преобразования.

Пояснение

Технология, по которой все это происходит, весьма проста. Метод
выполнения - используя команду языка shell ".". Это означает исполь-
зовать тот же shell для запуска команды, а не дочерний. Когда команда
выполняется этим же языком shell, все переменные, которые в данный
момент имеют какие-то значения, доступны для командного файла dtoh.
Мы используем это преимущество и предполагаем для dtoh, что Вы пра-
вильно установили переменные языка shell.
Строка 4 вызывает команду bc, которая представляет из себя
калькулятор самого нижнего уровня. По умолчанию bc использует
десятичный ввод и вывод. Мы устанавливаем основание выхода в 16 в
строке 5. Это означает, что любое число, которое мы печатаем с этого
момента, печатается как шестнадцатиричное число.
Строка 6 вводит десятичное число для bc, расширяя его значение
как ввод для bс. Bc выдает на экран число, записанное в
шестнадцатиричной системе счисления. Мы преобразовали число.
Единственное, что осталось сделать - это поместить результат в
переменную языка shell в вызывающей программе. Но это уже сделано в
вызывающей программе благодаря способу вызова процедуры. Преобразова-
ние происходит внутри bc.

Исследования

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

$ DEC="150"; (. .dtoh) > HEX
$ echo "шестнадцатиричное число: 'cat HEX'"

Обозначение () запускает вызываемую процедуру в дочернем
языке shell. Используя "." для ее выполнения, мы по прежнему имеем