Powershell и объектный pipe

Я задумался над тем, чтобы показать как могла бы выглядеть обработка логов на PowerShell. Как я уже говорил, pipe в PowerShell объектный, то есть по нему идёт не монолитный текст, а набор объектов. Вот я и попробую на примере показать что это.

Для начала настроим лог Apache так, чтобы его было удобнее парсить. К примеру, можно ввести символы табуляций — в логе они не встречаются и вполне подходят на роль разделителя:

LogFormat "%h\t%l\t%u\t%t\t\"%r\"\t%>s\t%b" common
CustomLog logs/access.log common

В логе у нас есть: хост клиента, его ident, имя пользователя (если была произведена HTTP-авторизация), дата и время запроса, метод-URL-версия-HTTP (одним полем), код ответа и длина отданного клиенту контента:

127.0.0.1   -   -   [03/Jan/2010:16:35:30 +0300]    "GET /favicon.ico HTTP/1.1" 404 1976
127.0.0.1   -   -   [03/Jan/2010:16:35:32 +0300]    "GET /a.html HTTP/1.1"  200 521
127.0.0.1   -   -   [03/Jan/2010:16:35:33 +0300]    "GET /favicon.ico HTTP/1.1" 404 1976

Предположим, наша задача — разобрать лог, отобрать только те запросы, у которых код ответа 200, отсортировать по размеру контента и вывести в табличном виде название метода, URL и размер контента.

cat access.log | %`
{
    $o = "" | Select-Object host, logname, user, date, url, code, len, method
    $o.host, $o.logname, $o.user, $o.date, $o.url, $o.code, $o.len = $_.Split("`t")

    [datetime]$o.date = $o.date -replace '^.([^:]+):(.+).$', '$1 $2'
    
    "logname", "user", "len" | %{ If ($o.$_ -eq '-') { $o.$_ = $false } }

    $o.method, $o.url = $o.url.SubString(1).Split()[0..1]

    $o
} | ?{ $_.code -eq 200 } | Sort-Object -property len | Format-Table method, url, len

Итак, что здесь происходит. Читаем лог, проходимся по каждой его строке (%{ }), внутри цикла странным хаком с Select-Object создаём объект с заданными полями, разбиваем входную строку по символу табуляция, убираем у поля «date» первый и последний символы (там квадратные скобки) и двоеточие между датой и временем (такой формат понимает PowerShell), сразу преобразуем это значение в тип «datetime», у поле «logname», «user», «len» заменяем значение на False, если там «-» («пусто» в терминах лога Apache), поле «url» разделяем пробелу (там три значения — метод, URL и версия протокола), избавляемся от окаймляющих кавычек и записываем в соответствующие поля.

Последней строкой цикла выводим получившийся объект. До цикла по pipe шли объекты попроще — всего лишь наборы строк («System.String»), теперь идут более интересные объекты («System.Management.Automation.PSCustomObject» с заданными нами полями).

Из этих объектов мы вибираем только те, у которых поле «code» содержит значение 200 («?{}» — это короткий алиас «Where-Object»), сортируем по полю длины контента и выводим, форматируя в таблицу, три поля: метод запрос, URL и длину контента. На экране будет что-то подобное:

method                     url                        len
------                     ---                        ---
GET                        /                          4701
GET                        /                          4701
GET                        /                          4701
GET                        /                          4701
GET                        /a.html                    521
GET                        /a.html                    521

Надо будет после праздников взглянуть на PowerShell 2.0, говорят, там очень много нового и интересного добавили.

Поделиться
Отправить
26 комментариев
A.I. (iskin.myopenid.com)

Надеюсь небольшой holly war тут разрешён. А не кажется ли тебе, что PowerShell тяжелее bash и менее функционален и удобен интерактивных консолей Python или Ruby — то есть, ни рыба, ни мясо. Например, для Ruby (где само собой любое значение — набор объектов) даже есть специальная библиотека для замена консоли (с несколькими интересными идеями, например, отсутствием модальности текущей директории).

jankkhvej (jankkhvej.blogspot.com)

Тут получается так: на всех виндах у меня давно стоит Сygwin, bash/awk/perl/etc, и мне не нужен там никакой PowerShell. С другой стороны, если теперь в поставке есть PowerShell, то почему бы и нет?

Когда-то для ДОСа я активно использовал 4DOS, потом пересел на OS/2 и, пока не открыл REXX, юзал 4OS/2. Как только я проникся REXX и его полной объектной интеграцией в систему, 4OS/2 отпал как старая грязь с первым снегом.
Потом я пересел на Unix, и вообще забыл о проблеме качественного скриптинга. Но иногда хочется как раз интеграции с GUI винды, типа там видео проиграть, получить свойства объектов, кодеки там у видео, и всё такое. А в Cygwin c этим никак. И. насколько я понимаю, в винде с этим вообще никак было до появления PowerShell...
С другой стороны, нафиг мне эта винда, когда везде или Unix c его перлами/башами, либо MacOS с AppleScipt?..

jankkhvej (jankkhvej.blogspot.com)

Вообще, я тут подумал — если уж гонять апач на винде, то тогда можно и логи его попарсить павершеллом... Но такая извращённая жизнь обязательно выйдет боком — или болячкой, или семейными неурядицами :)
Надо делать то, что надо, а что не надо — делать не надо.

Евгений Степанищев (bolknote.ru)

Комментарий для iskin.myopenid.com:

Что значит «тяжелее» bash? :) По мне так PS проще и более продуманный. И PS как shell для Windows уж точно функциональнее Python/Ruby — у него есть прямой и естественный доступ к COM/WMI/ADSI, то есть ко всем кишкам системы, а во второй версии аж транзакции.

Я не знаю Ruby, но около года попрограммировал на Python и пару месяцев на PS (плюс прочитал две книги), на мой взгляд, сделать из Python shell очень тяжело, надо делать обвязку. Ну попробуйте, для пример, скопировать там файлы по маске в другой каталог. Сделать это возможно, конечно, но так же просто как в каком-то shell?

Shell это ведь не только возможности как таковые, это определённая область, под которую язык надо затачивать.

Евгений Степанищев (bolknote.ru)

Комментарий для jankkhvej.blogspot.com:

Тут получается так: на всех виндах у меня давно стоит Сygwin, bash/awk/perl/etc, и мне не нужен там никакой PowerShell.

Сравнивать PS и bash некорректно, PS более молодая и продуманная вещь, сравнение будет не в пользу bash, конечно же. Как shell Perl тоже хуже, чем PS.

типа там видео проиграть, получить свойства объектов, кодеки там у видео, и всё такое. А в Cygwin c этим никак. И. насколько я понимаю, в винде с этим вообще никак было до появления PowerShell…

Ну как же никак? Можно было сходить из Windows Script Host из реестр и WMI и там всё выяснить, просто это не так удобно как сейчас.

если уж гонять апач на винде, то тогда можно и логи его попарсить павершеллом... Но такая извращённая жизнь обязательно выйдет боком — или болячкой, или семейными неурядицами :)

На винде можно логи IIS запрашивать. Парсить их не надо, они уже придут в объектном виде, а логи Apache можно парсить и на других ОС:  http://en.wikipedia.org/wiki/Pash_%28software%29

jankkhvej (jankkhvej.blogspot.com)

Ну как же никак? Можно было сходить из Windows Script Host из реестр и WMI и там всё выяснить, просто это не так удобно как сейчас.

Именно это и ужасало.

Что до сравнений, очевидно же, если я на перле потрачу 2 минуты на написание нужного мне скрипта, то на изучение PS...

Кстати, неужели ещё где-то необходимо использовать IIS?

Евгений Степанищев (bolknote.ru)

Комментарий для jankkhvej.blogspot.com:

Ну, PS изучается очень быстро. Не две минуты, конечно, но за две минуты на нём уже можно что-то писать.

Кстати, неужели ещё где-то необходимо использовать IIS?

А что с ним не так? Раньше это было дырявое ведро, но вот уже несколько последних лет — очень производительный веб-сервер.

A.I. (iskin.myopenid.com)

Комментарий для Евгения Степанищева:

PS тяжелее bash так как тянет весь .Net framework. А bash есть на дистрибутивах «на дискетку».
Интерактивная консоль в IronRuby (Ruby внутри .Net) так же имеет доступ ко всем Windows API.
Я не знаю Python, но Ruby очень гибкий и вопрос удобства — просто в наборе удобных методов.

A.I. (iskin.myopenid.com)

Собственно, стандартными библиотеками решение задачи с логами
File.readlines(’access.log’).map { |i| i.split(«\t») }.reject { |i| ’200’ != i[5] }.sort { |a, b| a[6] <=> b[6] }.each { |i| request = i[4].split(’ ’); puts «#{request[0]} #{request[1]} #{i[6]}» }
И всё решение в одну строчку с помощью удобных механизмов работы с массивами из функциональных ЯП (в Python Они тоже есть, но синтаксис больно некрасивый).

Евгений Степанищев (bolknote.ru)

Комментарий для iskin.myopenid.com:

Ruby очень гибкий и вопрос удобства — просто в наборе удобных методов

Ну, в общем, про Ruby ничего сказать не могу. Я его даже чуть-чуть не знаю.

PS тяжелее bash так как тянет весь .Net framework. А bash есть на дистрибутивах «на дискетку».

dotNet framework, вроде как, не слишком много занимает. Не «на дискетку» (кому нужны сейчас дискетки?:), но по нынешним временам не кажется огромным. Да и в новых виндах он встроен, впрочем, как и PowerShell.

в Python Они тоже есть, но синтаксис больно некрасивый

Мне не кажется некрасивым. Вполне себе сопоставимый синтаксис. Выглядеть будет примерно так же.

baka.name

Комментарий для Евгения Степанищева:

dotNet framework, вроде как, не слишком много занимает
Да и в новых виндах он встроен, впрочем, как и PowerShell.

Если на компьютере нет «новых виндов», то для этого, наверное, есть причины.
Возможно, те же самые, по которым «не слишком много» может оказываться многовато.
(Да и не все верят, что от перехода к новой его версии не сломается то, что раньше работало.)

А «на дискетку» — это newLISP, да.
Но его тоже придётся изучать.

A.I. (iskin.myopenid.com)

Комментарий для Евгения Степанищева:

«На дискетку» — имеется в виду роутеры и другие мелкие железки. То есть PS нельзя заложить в основу ОС, как sh в UNIX. Те же низкоуровневые скрипты включения ОС или куча мелких системных скриптов — тут точно не место виртуальной машине.

Евгений Степанищев (bolknote.ru)

Комментарий для iskin.myopenid.com:

«На дискетку» — имеется в виду роутеры и другие мелкие железки.

Пройдёт 2-3 года и в роутерах станет больше памяти, не проблема, правда же?

То есть PS нельзя заложить в основу ОС, как sh в UNIX

Как это «в основу OС»?

Евгений Степанищев (bolknote.ru)

Комментарий для baka.name:

Если на компьютере нет «новых виндов», то для этого, наверное, есть причины.

Ну, старые машины, бог с ними. Когда-нибудь они вымрут, это не причина задерживать прогресс. Один из компьютеров у меня — довольно старый Pentium-III, на нём стоит Windows XP, много лет без переустановки.

На этой XP, соответственно, уже может работать PS (Windows 7 там не заработает — памяти всего 512МБ).

A.I. (iskin.myopenid.com)

Комментарий для Евгения Степанищева:

bash является интерфейсом по умолчанию, следовательно должен работать на любых устройствах — вырастет память у роутеров (не будем думать зачем там .Net ;) ), так Linux будут ставить на мелкие датчики, которые будут разбрасывать по лесу для контроля погоды («умная пыль») и там тоже понадобится bash для сервисных нужд.

В основе ОС — это значит, что bash используется в скриптах запуска ОС (/etc/init.d/) в куче мелких системных утилит, которые часто запускаются. Грузить для этого всю CLR-VM со стеком библиотек .Net — неправильно.

ELV1S (elv1s.ru)

Комментарий для iskin.myopenid.com:

... Например, для Ruby (где само собой любое значение — набор объектов) даже есть специальная библиотека для замена консоли (с несколькими интересными идеями, например, отсутствием модальности текущей директории).

А как эта библиотека называется?

A.I. (iskin.myopenid.com)

Комментарий для elv1s.ru:

Вспомнил Ruby-библиотеку для консоли — rush,  http://rush.heroku.com/

Евгений Степанищев (bolknote.ru)

Комментарий для iskin.myopenid.com:

Грузить для этого всю CLR-VM со стеком библиотек .Net — неправильно.

Когда-то, много лет назад, когда я активно писал на ассемблере, а пучок игр помещался на дискету, вышла игра «Prince II». Как сейчас помню — она занимала пять мегабайт. Тогда для меня это было невероятным расходом. Я недоумевал и переживал (куда катится мир), что какая-то игрушка занимает целых пять мегабайт.

Мораль такова — что сейчас кажется нехеровым расходом, завтра окажется обыденностью.

В основе ОС — это значит, что bash используется в скриптах запуска ОС (/etc/init.d/) в куче мелких системных утилит, которые часто запускаются.

Я смотрю куча мелких системных утилит, чем дальше, тем больше, переползает на Python. Да и в init.d никто не мешает использовать другой язык.

jankkhvej (jankkhvej.blogspot.com)

Всё-таки в «основу ОС» в Unix встроена концепция «всё — файл», а вот там, где PowerShell — его концепции «всё — объект» в ОС нету. В этом собственно и есть фейл PowerShell — он надстройка, а не оболочка к функциям ОС. И это реально бесит, если вспоминать то, что концепция «всё — объект» была ещё в 1997-ом в OS/2.

Евгений Степанищев (bolknote.ru)

Комментарий для jankkhvej.blogspot.com:

а вот там, где PowerShell — его концепции «всё — объект» в ОС нету

А что вам даёт основание это утверждать?

del a.txt — удаление файла a.txt
del variable:a — удаление переменной «a»
del hkcu:AppEvents — удаление ветки реестра HKCU/AppEvent

Это к концепции «всё файл» (хотя это объекты, конечно). Добраться до WMI не настолько тривиально, но тоже довольно просто.

jankkhvej (jankkhvej.blogspot.com)

Можно ли из произвольной программы с использованием стандартной функции из стандартной библиотеки найти и открыть объект N, который, к примеру документ ворда на рабочем столе и получить его автора? В OS/2 можно.
Можно ли прочитать данные из произвольного устройства, типа флешки или сети, простой функцией из стандартной библиотеки? В Unix можно.
А вот в Windows надо вызывать PowerShell. Вот и вся разница.

Евгений Степанищев (bolknote.ru)

Комментарий для jankkhvej.blogspot.com:

А… мы на недостатки Windows и отдельные достоинства других ОС перешли? Не, я не хочу об этом спорить :) Я про PowerShell и исключительно про него.

jankkhvej (jankkhvej.blogspot.com)

это был ответ на вопрос «А что вам даёт основание это утверждать?». Концепции PowerShell на Windows не распостраняются же.

Евгений Степанищев (bolknote.ru)

Комментарий для jankkhvej.blogspot.com:

В DOS, как не странно, эта концепция присутствовала. Например, принтер там был prn, COM1 — com1, а с HIMEM можно было общаться через файл XMSXXXX0.

jankkhvej (jankkhvej.blogspot.com)

в DOS? Принтер да, а, к примеру, сканер?
То-то же.

Евгений Степанищев (bolknote.ru)

Комментарий для jankkhvej.blogspot.com:

А во времена DOS были сканеры?

Популярное