Пишу, по большей части, про историю, свою жизнь и немного про программирование.

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) 2010

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

jankkhvej (jankkhvej.blogspot.com) 2010

Тут получается так: на всех виндах у меня давно стоит С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) 2010

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

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

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

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

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

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

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

Комментарий для 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) 2010

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

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

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

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

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

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

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

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

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

A.I. (iskin.myopenid.com) 2010

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

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

A.I. (iskin.myopenid.com) 2010

Собственно, стандартными библиотеками решение задачи с логами
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) 2010

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

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

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

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

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

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

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

baka.name 2010

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

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

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

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

A.I. (iskin.myopenid.com) 2010

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

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

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

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

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

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

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

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

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

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

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

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

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

A.I. (iskin.myopenid.com) 2010

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

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

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

ELV1S (elv1s.ru) 2010

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

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

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

A.I. (iskin.myopenid.com) 2010

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

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

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

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

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

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

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

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

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

jankkhvej (jankkhvej.blogspot.com) 2010

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

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

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

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

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

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

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

jankkhvej (jankkhvej.blogspot.com) 2010

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

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

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

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

jankkhvej (jankkhvej.blogspot.com) 2010

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

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

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

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

jankkhvej (jankkhvej.blogspot.com) 2010

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

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

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

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