Pull to refresh

Поиск текста по подкаталогам

Reading time4 min
Views4.6K
За время работы администратором появляются вопросы, решение которых периодически откладывается за кажущейся малозначительностью, но иногда восхищают неожиданно найденные ответы. Спешу поделиться одним таким вопросом с простым ответом (файлы windows, решение — linux, поэтому уклон больше в сторону linux).

Вопрос был такой: обойти все текстовые файлы в подкаталогах и вывести значения текстовых строк по регулярному выражению. (Понятно, что никакой проводник или windows-commander здесь не поможет).

Обстоятельства:
Много логов в текстовых файлах. Значения логов, в основном, представляют собой кусты реестра FireFox, FlashPlayer, office и пр. в формате JSON. Скрипты были написаны на языке JavaScript+WMI и помещены в Active Directory в автозагрузку компьютера и пользователя. Вот некоторые ключи реестра, которые представляли основной интерес:

HKLM\Software\Macromedia\FlashPlayer
HKLM\Software\Macromedia\FlashPlayerActiveX
HKLM\Software\Macromedia\FlashPlayerPlugin
HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall
HKLM\Software\Mozilla.org
HKLM\Software\Mozilla
HKLM\Software\MozillaPlugins


Логи создавались в текстовые файлы в следующем формате \\serverlog\logs$\[Дата]\[имя компьютера]\[путь к кусту реестра без запрещённых спецсимволов].txt. Пример имени такого файла "\\serverlegs\logs$\regToFile.ANSI\2011-09-13\regToFile-[12-143057][2011-09-03]\[HKCU][SOFTWARE][Macromedia][FlashPlayer].txt". Пример его содержимого:

[
 {"path":"HKLM\\SOFTWARE\\Macromedia\\FlashPlayer","type":"folder"},
 {"path":"HKLM\\SOFTWARE\\Macromedia\\FlashPlayer","type":"REG_SZ","name":"CurrentVersion","value":"9,0,45,0"},
 {"path":"HKLM\\SOFTWARE\\Macromedia\\FlashPlayer\\SafeVersions","type":"folder"},
 {"path":"HKLM\\SOFTWARE\\Macromedia\\FlashPlayer\\SafeVersions","type":"REG_DWORD","name":"6.0","value": 88},
 {"path":"HKLM\\SOFTWARE\\Macromedia\\FlashPlayer\\SafeVersions","type":"REG_DWORD","name":"7.0","value": 65},
 {"path":"HKLM\\SOFTWARE\\Macromedia\\FlashPlayer\\SafeVersions","type":"REG_DWORD","name":"8.0","value": 33},
 {"path":"HKLM\\SOFTWARE\\Macromedia\\FlashPlayer\\SafeVersions","type":"REG_DWORD","name":"9.0","value": 45}
]


Машин в домене не одна сотня и количество файлов быстро разрослось. Имея такой набор логов хочется иногда составлять «на лету» некоторую картину о содержимом файлов примерно в таком виде:



Но оказалось, что если файлы логов раскиданы по подкаталогам, то выполнить над ними команду (windows) find, не представляется возможным — не ищет она по подкаталогам. Монтируем сетевой каталог с логами в Ubuntu (sudo mount -t cifs -o user=<domain\\username>,password=<domain_password>,iocharset=utf8 //serverlogs/logs$/ /media/serverlogs/). Попытка в linux поначалу не принесла успеха. Команда find имеет те же проблемы и там! Но linux-то тем и хорош, что его-то консоль является админ-ориентированной, хоть и интерфейс совсем не дружественный. В man написано, что команда find имеет опцию -exec. Это просто супер опция. Казалось бы остаётся только подставить в этот ключ команду grep и мы получаем заветный результат… Но тут нас ждёт небольшое разочарование! Файлы логов-то писались в UNICODE (может моя архитектурная ошибка?), а grep в упор не понимает UNICODE (но UTF-8 понимает). Развиваем мысль дальше: есть команда iconv, которая может на лету конвертировать кодировки. Вот тут эта её возможность и пригодилась. Дополнительно используем «трубопровод» и получаем команду такого вида:

time find /media/serverlogs/regToFile.ANSI/ -name "*.txt" -exec iconv -f UNICODE -t UTF-8 {} \; | grep 'Macromedia\\\\FlashPlayer.*CurrentVersion'

Немного пояснений:
[time] — выводит время, потраченное на выполнение команды.
[find /media/server03-logs/regToFile.ANSI/ -name "*.txt"] — вывести все файлы типа *.txt, которые есть в подкаталогах [/media/serverlogs/regToFile.ANSI/]
[-exec iconv -f UNICODE -t UTF-8 {} \;] — конвертировать содержимое найденного файла (одного за раз) из кодировки UNICODE в UTF-8
[| grep 'Macromedia\\\\FlashPlayer.*CurrentVersion'] — найти в конвертированном тексте строку Macromedia\\\\FlashPlayer.*CurrentVersion

Желаемый результат достигнут и выглядит как картинка выше. Думаю, что я не единственный у кого возникала такая проблема. Если кому-то пригодится, буду рад.

P.S.
Проанализировав комментарии, man grep -r и help по «System.FileSystemObject».OpenAsTextStream() пришёл к выводу, что проблема изначально «скрывалась» именно в этом методе OpenAsTextStream(). У него есть параметр format. Если он -1, то файл открывается в режиме UNICODE, а если 0, то в режиме ASCII (но не ANSI!, а utf-8). У меня был -1. Вот это и было корнем проблемы. Установил его в 0 и стало работать и grep -r (в linux) и findstr в windows. Странно, конечно, что они UNICODE не понимают. Ну а вдруг, если мне захочется что-то сделать с найденной строкой перед тем, как вывести её на экран, то буду использовать find -exec.

Для вывода найденных строк:

JavaScript->«System.FileSystemObject».OpenAsTextStream(ForAppending, TristateFalse); (TristateFalse для UTF-8!!!!)
Windows:
cd <rootPath>
findstr /s «text» *.txt

Linux:
grep -r «text» <rootPath>

Продолжая тему поиска выполнил конвертирование файлов с логами формата UNICODE в формат UTF-8 (в консоли linux/bash):

time find /media/serverlogs/ -name "*.txt" -exec iconv -f=UNICODE -t=UTF-8 {} -o {}.utf8 \; -exec echo {} \;

Обращаю внимание, что для вывода имени конвертируемого файла в консоль нужно ключ -exec использовать два раза. Объединения команд методом && в одном ключе -exec не получится. Ключ -exec воспринимает только одну команду.
Tags:
Hubs:
Total votes 5: ↑4 and ↓1+3
Comments20

Articles