За время работы администратором появляются вопросы, решение которых периодически откладывается за кажущейся малозначительностью, но иногда восхищают неожиданно найденные ответы. Спешу поделиться одним таким вопросом с простым ответом (файлы 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". Пример его содержимого:
Машин в домене не одна сотня и количество файлов быстро разрослось. Имея такой набор логов хочется иногда составлять «на лету» некоторую картину о содержимом файлов примерно в таком виде:

Но оказалось, что если файлы логов раскиданы по подкаталогам, то выполнить над ними команду (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/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):
Обращаю внимание, что для вывода имени конвертируемого файла в консоль нужно ключ -exec использовать два раза. Объединения команд методом && в одном ключе -exec не получится. Ключ -exec воспринимает только одну команду.
Вопрос был такой: обойти все текстовые файлы в подкаталогах и вывести значения текстовых строк по регулярному выражению. (Понятно, что никакой проводник или 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 воспринимает только одну команду.