Глава 1 Строки

1.18. Программа: psgrep

Многие программы (в том числе ps, netstat, Is -/, find -Is и Icpdump) часто выдают большие объемы данных. Файлы журналов тоже быстро увеличиваются в размерах, что затрудняет их просмотр. Такие данные можно обработать программой-фильтром типа grep и отобра В частности, нам хотелось бы иметь возможность обращаться с полноценными запросами к выводу программы или файлу журнала. Допустим, вы спрашиваете у ps: "Покажи мне все непривилегированные процессы размером больше 10Кб" или "Какие команды работают на псевд Программа psgrep умеет делать все это и бесконечно большее, потому что в ней критерии отбора не являются регулярными выражениями; они состоят из полноценного кода Peri. Каждый критерий последовательно применяется к каждой строке вывода. В результате вывод o Строки со словами, заканчивающимися на sh:
% psgrep '/sh\b/'
o Процессы с именами команд, заканчивающимися на sh:
% psgrep 'command =~ /sh$/'
o Процессы с идентификатором пользователя, меньшим 10:
% psgrep 'uid < 10'
o Интерпретаторы с активными консолями:
% psgrep 'command =~ '/"-/' 'tty ne "?'"
o Процессы, запущенные на псевдоконсолях:
% psgrep 'tty =~ /"[p-t]'
o Отсоединенные непривилегированные процессы:
% psgrep 'uid && tty eq "?"'
o Большие непривилегированные процессы:
% psgrep 'size > 10 * 2**10' 'uid != О'
Ниже показаны данные, полученные при последнем вызове psgrep на нашем компьютере. Как и следовало ожидать, в них попал только net sea ре и его вспомогательный процесс:
FLAGS UID PID PPID PRI NI SIZE RSS WCHAN STA TTY TIME COMMAND
0 101 9751 1 0 0 14932 9652 do.select S p1 0:25 netscape 100000 101 9752 9751 0 0 10636 812 do_select S p1 0:00 (dns helper)

В примере 1.6 приведен исходный текст программы psgrep.
Пример 1.6. psgrep
#!/usr/bin/peri -w
#psgrep - фильтрация выходных данных ps
# с компиляцией пользовательских запросов в программный код
#
use strict;
# Все поля из заголовка PS
my ©fieldnames = qw(FLAGS UID PID PPID PRI NICE SIZE RSS WCHAN STAT TTY TIME COMMAND);
# Определение формата распаковки (в примере
# жестко закодирован формат ps для Linux)
my $fmt = cut2fmt(8, 14, 20, 26, 30, 34, 41, 47, 59, 63, 67, 72);
my %fields; # Для хранения данных
die "Thanatos unless @ARGV;
usage: $0 criterion ...
Each criterion is a Peri expression involving:
@fieldnames
All criteria must be met for a line to be printed. Thanatos
# Создать синонимы для uid, size, UID, SIZE и т.д.
# Пустые скобки необходимы для создания прототипа без аргументов
for my $name (@fieldname) {
no strict 'rets';
-name = *{lc $name} = sub () { $fields{$name} };
}
my $code = "sub is_desirable { " . join(" and ", @ARGV) . " } ";
unless (eval $code.1) {
die "Error in code: $@>\n\t$code\n";
}
open (PS, "ps wwaxi |") || die "cannot fork: $!";
print scalar ; # Строка-заголовок while ( {
@fields{@fieldnames} = trim(unpack($fmt, $_));
print if is_desirable(); # Строки, удовлетворяющие критериям
}
close(PS) || die "ps failed!"; # Преобразовать позиции разреза в формат распаковки sub cut2fmt {
my(@positions) = @_;
my Stemplate = ' ';
my $lastpos = 1;
foreach $place(positions) {
$template .= "A" . ($place - $lastpos) . " ";
$lastpos = $place;
}
$template .= "A*";
return $template;
}
suu irim {
my @out = @_;
for (Oout) {
s/"\s+//;
s/\s+$//;
} return wantarray ? Oout : $out[0];
}

# Следующий шаблон использовался для определения позиций разреза.
# Далее следует пример входных данных
й-123456789012345678901234567890123456789012345678901234567890123456789012345

#

1

 

 

2

3

 

 

4

 

 

5 6

 

 

 

 

7

 

 

# Позиции

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

# 8

14

20

26 30

34

41

47

59

63

67

72

 

 

Я I

 

 

 

 

|

I I

|

I

|

I

|

|

|

 

 

END

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

FLAGS

UID

PID

PPID

PRI

NI

SIZE

RSS

WCHAN

STA

TTY

TIME

COMMAND

100

0

1

0

0

0

760

432

doselect

S

?

0:02

init

140

0

187

1

0

0

784

452

doselect

S

?

0:02

syslogd

100100

101

428

1

0

0

1436

944

doexit

S

1

0:00

/bin/

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

login

100140

99 30217

402

0

0

1552

1008

posixlock

S

7

0:00

httpd

0

101

593

428

0

0

1780

1260

copythread

S

1

0:00

-tcsh

100000

101 30639

9562

17

0

924

496

 

 

R

Р1

0:00

ps axl

0

101

25145

9563

0

0

2964

2360

idetaperea

S

Р2

0:06

trn

100100

0 10116

9564

0

0

1412

928

setupframe

Т

РЗ

0:00

ssh -C

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

WWW

100100

0 26560

26554

0

0

1076

572

setupframe

т

Р2

0:00

less

100000

101 19058

9562

0

0

1396

900

setupframe

т

Р1

0:02

nvi /

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Imp/a



В программе psgrep объединены многие приемы, представленные в книге. Об удалении начальных и конечных пропусков рассказано в рецепте 1.14. Преобразование позиций разреза в формат unpack для извлечения полей с фиксированным положением рассматривает Многострочный текст, передаваемый die, представляет собой встроенный документ (см. рецепты 1.10 и 1.11). Присваивание @fields{@fieldnames} заносит сразу несколько величин в хэш %fields. Хэши рассматриваются в рецептах 4.7 и 5.10. Входные данные программы-примера, расположенные под __END__, описаны в рецепте 7.6. На стадии разработки для тестирования использовались "консервированные" данные, полученные через файловый манипулятор DATA. Когда программа заработала, мы перевели ее на п Настоящая сила и выразительность psgrep обусловлены тем, что в Peri строковые аргументы могут представлять собой не просто строки, а программный код Peri. Похожий прием использован в рецепте 9.9, за исключением того, что is psgrep аргументы пользователя " eval "sub is_desirable { uid < 10 } " . 1; Загадочное . 1 в конце присутствует для того, чтобы при компиляции пользовательского кода команда eval возвращала истинное значение. В этом случае нам даже не придется проверять $@ на предмет ошибок компиляции, как это делается в рецепте 10.12. Использование произвольного кода peri в фильтрах для отбора записей - невероятно мощная возможность, но она не является абсолютно оригинальной. peri многим обязан языку программирования awk, который часто применялся для подобной фильтрации. Один из недост Пользовательские критерии даже не обязаны быть простыми выражениями. Например, следующий вызов инициализирует переменную $id номером пользователя nobody и затем использует ее в выражении: % psgrep 'no strict "vars"; BEGIN { $id = getpwnamC'nobody") } uid == $id Но как использовать эти слова, uid, command и size, даже не снабжая их символом $ для представления соответствующих полей входных записей? Мы напрямую манипулируем с таблицей символов, присваивая замыкания (closures) неявным тип-глобам (typeglobs), которы Однако в psgrep встречается нюанс, отсутствующий в этих рецептах, - речь идет о пустых скобках в замыкании. Благодаря скобкам функция может использоваться в выражениях везде, где допускается отдельная величина (например, строка или числовая константа). В Показанная версия psgrep получает входные данные от команды ps в формате Red Hat Linux. Чтобы перенести ее в другую систему, посмотрите, в каких столбцах начинаются заголовки. Такой подход не ограничивается спецификой ps или системы UNIX. Это общая методи После небольшого изменения в функциях отбора программа даже подойдет для работы с пользовательской базой данных. Если у вас имеется массив записей (см. рецепт 11.9), пользователь может указать произвольный критерий отбора:
sub id() { $_->{ID} }
sub title() { $_->{TITLE} }
sub executive { title ='/(?: vice-)?president/i }
# Критерии отбора указываются при вызове дгер @slowburners = дгер { id < 10 && !executive } ©employees; По причинам, связанным с безопасностью и быстродействием, такой подход редко встречается в реальных механизмах, описанных в главе 14 "Базы данных". В частности, он не поддерживается в sql, но имея в своем распоряжении peri и некоторую долю изобретательнос
© copyright 2000 Soft group
v

Используются технологии uCoz