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

exec
и в некоторых других программах, например в конце обобщенной программы
cal
, когда происходит обращение к
/usr/bin/cal
.

Кстати, сигнал 9 — это тот сигнал, который нельзя перехватить или игнорировать: он всегда уничтожает процесс. На языке

shell
его посылка задается с помощью

$ kill -9 номер_процесса

Обращение

kill -9
не является стандартным, поскольку процессу, уничтоженному таким способом, не дается время для приведения в порядок своих дел перед "смертью".

Упражнение 5.14

В приведенной выше версии команды

nohup
стандартный поток диагностики команды соединяется со стандартным выходным потоком. Хорошее ли это решение? Если нет, то как бы вы разделили их явно?

Упражнение 5.15

Найдите встроенную команду

times
и добавьте к вашему файлу строку
.profile
, чтобы при вашем выходе из системы интерпретатор выдавал использованное вами процессорное время.

Упражнение 5.16

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

/etc/passwd
. Если у вас есть энтузиазм (и право доступа), сделайте из нее команду, устанавливающую нового пользователя системы. Какие нужны для нее права доступа? Как следует ей обращаться с прерываниями?

5.5 Команда
overwrite
: замена файла

В команде

sort
есть флаг
для замены файла:

$ sort файл1 -о файл2

Ее эквивалент:

$ sort файл1 > файл2

Если

файл1
и
файл2
— это один и тот же файл, то после операции переключения
>
входной файл станет пустым перед сортировкой. Но с флагом
команда выполняется правильно, потому что входной файл сортируется и сохраняется во временном файле перед созданием выходного файла.

Могут использовать флаг

и другие команды. Например, редактор
sed
может редактировать файл с заменой:

$ sed 's/UNIX/UNIX (TM)/g' -o ch2
Так не получится!

Непрактично изменять все подобные команды, вводя флаг — это не лучшее решение. Более целесообразным представляется централизованное выполнение функций, как в случае операции

>
интерпретатора, для чего мы создадим программу
overwrite
. Первый ее вариант выглядит так:

$ sed 's/UNIX/UNIX (TM)/g' гл2 | overwrite гл2

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

# overwrite: copy standard input to output after EOF

# version 1. BUG here


PATH=/bin:/usr/bin


case $# in

1) ;;

*) echo 'Usage: overwrite file' 1>&2; exit 2

esac


new=/tmp/overwr.$$

trap 'rm -f $new; exit 1' 1 2 15


cat >$new # collect the input

cp $new $1 # overwrite the input file

rm -f $new

Команда

cp
используется вместо команды
mv
, чтобы не изменились права доступа и остался прежним владелец выходного файла, если он уже существует. Хотя этот вариант и чрезвычайно прост, здесь возможна "фатальная" ошибка: если пользователь нажмет клавишу DEL (УДЛ) во время выполнения команды
cp
, первоначальный выходной файл будет уничтожен. Необходимо соблюдать осторожность, поскольку прерывание может остановить замену входного файла:

# overwrite: copy standard input to output after EOF

# version 2. BUG here too


PATH=/bin:/usr/bin

case $# in 1) ;;

*) echo 'Usage: overwrite file' 1>&2; exit 2

esac


new=/tmp/overwr1.$$

old=/tmp/overwr2.$$

trap 'rm -f $new $old; exit 1' 1 2 15


cat >$new # collect the input

cp $1 $old # save original file

trap '' 1 2 15 # we are committed; ignore signals

cp $new $1 # overwrite the input file


rm -f $new $old

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

cp
не прервется. Если команда
cp
начала выполняться, команда
overwrite
обязана заменить исходный файл.

Здесь есть некоторая тонкость. Рассмотрим последовательность:

$ sed 's/UNIX/UNIX(TM)g' special | overwrite special

command garbled: s/UNIX(TM)g

$ ls -l special

-rw-rw-rw- 1 you 0 Oct 1 09:02 special #$%@*!

$

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

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

Во избежание такого финала можно предложить несколько решений. Команда

overwrite
могла бы запрашивать подтверждение перед заменой файла, но, сделав команду диалоговой, мы потеряем большую часть ее достоинств. Она могла бы проверять, что ее входной поток не пуст (с помощью
test -2
), но это некрасиво и к тому же неверно: выходной поток мог быть создан до обнаружения ошибки.

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

overwrite
, чтобы можно было проверить ее код завершения. Это, правда, противоречит традициям и здравому смыслу: ведь в конвейере команда
overwrite
обычно должна быть последней, но для правильной работы она должна идти первой. Однако
overwrite
ничего не выдает в стандартный выходной поток, поэтому можно считать, что не происходит потери общности. Более того, ее синтаксис не является каким-то необычным:
time
,
nice
,
nohup
представляют собой команды, аргументами которых служат другие команды. Ниже приведен безопасный вариант:

# overwrite: copy standard input to output after EOF

# final version


opath=$PATH

PATH=/bin:/usr/bin


case $# in

0|1) echo 'Usage: overwrite file cmd [args]' 1>&2; exit 2

esac


file=$1; shift

new=/tmp/overwr1.$$; old=/tmp/overwr2.$$

trap 'rm -f $new $old; exit 1' 1 2 15 # clean up files


if PATH=$opath "$@" >$new # collect input

then

 cp $file $old # save original file

 trap '' 1 2 15 # we are committed; ignore signals

 cp $new $file

else

 echo "overwrite: $1 failed, $file unchanged" 1>&2 exit 1

fi

rm -f $new $old

Встроенная команда интерпретатора

shift
сдвигает весь список аргументов на одну позицию влево:
$2
становится
$1
,
$3
становится
$2
и т.д. Строка обозначает все аргументы (после
shift
), как и
$*
, но без интерпретации; мы вернемся к ее рассмотрению в разд. 5.7.

Заметьте, что значение

PATH
нужно восстановить перед выполнением команды пользователя; если этого не сделать, то команды, не находящиеся в