Чтение онлайн

ЖАНРЫ

UNIX — универсальная среда программирования
Шрифт:
Упражнение 5.19

Увеличьте надежность команды

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

Упражнение 5.20

Можно ли использовать

replace
для замены
i
на
index
всюду в программе? Какие вы внесли бы изменения, чтобы добиться этого?

Упражнение 5.21

Достаточно ли команда

replace
эффективна и удобна, чтобы находиться в каталоге
/usr/bin
? Не лучше ли вводить по мере необходимости подходящие команды редактора
sed
(да
или нет)? Обоснуйте свой ответ.

Упражнение 5.22

(Усложненное.) Команда

$ overwrite файл 'who | sort'

не выполняется. Объясните причину этого и исправьте ее. Подсказка: посмотрите

eval
в справочном руководстве по
sh(1)
. Как ваше решение повлияет на интерпретацию специальных символов в команде?

5.6 Команда

zap
: уничтожение процесса по имени

Команда

kill
только завершает процесс с указанным номером. Если нужно уничтожить определенный фоновый процесс, обычно приходится выполнить команду
ps
, чтобы узнать номер процесса, а затем ввести этот номер в качестве аргумента для команды
kill
. Однако нелепо иметь программу, выдающую номер процесса, который сразу же передается вручную другой программе. Имеет смысл написать программу, скажем
zap
, для автоматического выполнения такой работы. Здесь, правда, есть одно препятствие: уничтожение процессов опасно, поэтому следует принять меры для обеспечения сохранности нужных процессов. Хорошей защитой всегда служат диалоговое выполнение zap и использование команды
pick
для выбора "жертв".

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

pick
: она выдает поочередно свои аргументы, спрашивая ответ у пользователя; если ответ —
y
, то аргумент выводится (команда
pick
обсуждается в следующем разделе). В нашем случае
pick
используется для подтверждения, что процессы, выбранные по имени, — именно те, которые пользователь хочет уничтожить:

$ cat zap

# zap pattern: kill all processes matching pattern

# BUG in this version

PATH=/bin:/usr/bin

case $# in

0) echo 'Usage: zap pattern' 1>&2; exit 1

esac

kill `pick \`ps -ag | grep "$*"\` | awk '{print $1}'`

Обратите внимание на вложенные знаки слабого ударения, защищенные символами обратной дробной черты,

awk
программа выделяет номер процесса из выходных данных команды
ps
, выбранной с помощью
pick
:

$ sleep 1000 &

2216

$ ps -ag

 PID TTY TIME CMD

...

2216 0 0:00 sleep 1000

...

$ zap sleep

2216?

0? q
Что происходит?

$

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

ps
разбиты на слова, которые воспринимаются и обрабатываются командой
pick
как отдельные аргументы вместо того, чтобы обрабатываться сразу по строке. Обычная процедура интерпретатора заключается в разбиении строк на аргументы с границами пробел/не
пробел, как показано ниже:

for i in 1 2 3 4 5

В этой программе нужно контролировать процесс разбиения интерпретатором строк на аргументы, чтобы только символ перевода строки разделял соседние "слова".

Внутренняя переменная интерпретатора

IFS
(internal field separator — внутренний разделитель полей) представляет собой строку символов, которая разделяет слова в списке аргументов, находящихся в знаках слабого ударения или циклах
for
. Обычно
IFS
содержит пробелы, символы табуляции и конца строки, но мы можем заменить ее на что-либо нужное, например просто на символ перевода строки:

$ echo 'echo $#' >nargs

$ cx nargs

$ who

you tty0 Oct 1 05:59

pjw tty2 Oct 1 11:26

$ nargs 'who'

10
10 полей, разделенных пробелом и концом строки

$ IFS='

'
Только конец строки

$ nargs `who`

2
Две строки, два поля

$

После установки

IFS
равным символу перевода строки команда
zap
выполняется отлично:

$ cat zap

# zap pat: kill all processes matching pat

# final version

PATH=/bin:/usr/bin IFS='

' # just a newline

case $1 in

"") echo 'Usage: zap [-2] pattern' 1>&2; exit 1 ;;

– *) SIG=$1; shift

esac

echo ' PID TTY TIME CMD'

kill $SIG `pick \`ps -ag | egrep "$*"\` | awk '{print $1}`"

$ ps -ag

PID TTY TIME CMD

...

2216 0 0:00 sleep 1000

...

$ zap sleep

PID TTY TIME CMD

2216 0 0:00 sleep 1000? y

2314 0 0:02 egrep sleep? N

$

Мы здесь кое-что добавили: необязательный аргумент, обозначающий сигнал (обратите внимание на то, что

SIG
будет неопределенным, а значит, должен рассматриваться как пустая строка, если аргумент не задан), а также
egrep
вместо
grep
, чтобы разрешить более сложные шаблоны типа
'sleep | date'
. Первая команда
echo
выдает столбец из заголовков выходных данных команды
ps
.

Вас может заинтересовать, почему эта команда называется

zap
, а не просто
kill
. Основная причина заключается в том, что в отличие от случая с командой
cal
мы не даем действительно новой команды
kill
:
zap
по необходимости является диалоговой командой, с одной стороны, а с другой — мы хотим сохранить имя
kill
для настоящей команды. К тому же
zap
чрезвычайно медленна из-за накладных расходов на все дополнительные программы, хотя самую длинную по времени реализации команду
ps
все равно нужно выполнять. В следующей главе будет продемонстрировано более эффективное решение.

Поделиться с друзьями: