lilalo
changeset 155:8ee5e59f1bd3 lilalo-sqlite
Локальное хранение и анализ данных с помощью SQlite
Очень много изменений, касающихся работы с sqlite
и локального использования результатов записи.
Подробнее:
README.l3text
Очень много изменений, касающихся работы с sqlite
и локального использования результатов записи.
Подробнее:
README.l3text
author | Igor Chubin <igor@chub.in> |
---|---|
date | Tue Mar 16 20:05:30 2010 +0200 (2010-03-16) |
parents | 71d6b2a6b8eb |
children | a0daf0c3fa52 |
files | README README.l3text l3-agent l3bashrc l3config.pm l3text |
line diff
1.1 --- a/README Mon Dec 28 22:00:37 2009 +0300 1.2 +++ b/README Tue Mar 16 20:05:30 2010 +0200 1.3 @@ -13,3 +13,4 @@ 1.4 1.5 http://xgu.ru/wiki/LiLaLo 1.6 1.7 +
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/README.l3text Tue Mar 16 20:05:30 2010 +0200 2.3 @@ -0,0 +1,380 @@ 2.4 + 2.5 + 2.6 +l3text (или l3) это программа, которая предоставляет интерфейс 2.7 +к информации, записанной LiLaLo, прямо в командной строке. 2.8 + 2.9 +Результаты распознавания записываются с помощью l3-agent 2.10 +в базу данных SQLite, и они становятся сразу же доступными для 2.11 +использования с помощью l3text. 2.12 + 2.13 +l3text — это набор инструментов, для работы с транскриптами сеансов, 2.14 +сгруппированные под одной крышей. 2.15 + 2.16 +Некоторые примеры использования. 2.17 + 2.18 +Показать весь журнал команд в текущем контексте 2.19 +(начиная с текущего положения). 2.20 + $ l3text cat 2.21 + 2.22 +Показать только командные строки или только вывод: 2.23 + $ l3text cat -c 2.24 + $ l3text cat -o 2.25 + 2.26 +Показать последние 10 команд: 2.27 + $ l3text tail 2.28 + 2.29 +Показать последние 20 команд: 2.30 + $ l3text tail -20 2.31 + 2.32 +Следить за текущим сеансом, то есть показывать все команды, 2.33 +выполняющиеся в текущем контексте: 2.34 + $ l3text tail -f 2.35 + 2.36 +По умолчанию показываются только команды и результаты их выполнения. 2.37 +Вывод можно настроить: 2.38 + $ l3 cat -o time,cline,output 2.39 + 2.40 +== Контекст == 2.41 + 2.42 +Все команды, которые вы набираете, и результаты их выполнения 2.43 +не повисают в воздухе, а дописываются в конец листов, 2.44 +находящихся в определённых местах. 2.45 + 2.46 +Лист, на который попадают команды называется ''контекстом записи''. 2.47 + 2.48 +Вы можете набирать команды в разных окнах, 2.49 +но если они находятся в одном контексте, то они запишутся 2.50 +на один лист, одна под другой. 2.51 + 2.52 +С другой стороны, в одном окне вы можете поменять контекст несколько раз, 2.53 +и команды, набиравшиеся в этих контекстах, попадут на разные листы. 2.54 + 2.55 +По умолчанию контекст равен /default , 2.56 +но для удобства дальнейшей работы с записями рекомендуется 2.57 +задавать нужный контекст, которому соответствуют выполняемые действия. 2.58 + 2.59 +Сменить текущий контекст записи можно так: 2.60 + l3 context /path/to/new/context 2.61 + 2.62 +Просмотреть контекст записи можно так: 2.63 + l3 context 2.64 + 2.65 +Итак, контекст записи (контекст агента) определяет, 2.66 +куда попадают команды при записи. 2.67 + 2.68 +Мы записывали команды не просто так. 2.69 +Мы хотим поработать с тем, что мы записали. 2.70 +Лист, на котором записаны команды, с которыми мы хотим 2.71 +поработать, определяется ''контекстом чтения''. 2.72 + 2.73 +То есть он определяет, какие именно команды 2.74 +из записанных ранее мы имеем в виду. 2.75 + 2.76 +По умолчанию текущий контекст чтения становится равным 2.77 +текущему контексту записи. 2.78 + 2.79 +Смена текущего контекста чтения: 2.80 + $ l3 cd /path/to/new/context 2.81 +или 2.82 + $ l3 go /path/to/new/context 2.83 + 2.84 +Просмотр доступных контекстов: 2.85 + $ l3 ls 2.86 + 2.87 +Просмотреть имя текущего контекста чтения: 2.88 + $ l3 pwd 2.89 + 2.90 +Символы : и / являются в имени контекста специальными. 2.91 + 2.92 +Символ / формирует иерархию контекстов, 2.93 +а символ : указывает координаты строки в контексте. 2.94 + 2.95 +Перейти на 3 команды назад: 2.96 + $ l3 go :-3 2.97 + 2.98 +Перейти в контекст /adm/lab1, в его самый конец (и находиться 2.99 +всё время в конце, если он растёт, то есть делаются новые записи): 2.100 + $ l3 go /adm/lab1 2.101 + 2.102 +Перейти в контекст /adm/lab1, на первую команду grep: 2.103 + $ l3 go /adm/lab1:/grep/ 2.104 + 2.105 +О том, как указывать координаты интересующих нас строк, 2.106 +написано ниже, в разделе «Интервалы». 2.107 + 2.108 + 2.109 +== Команда == 2.110 + 2.111 + l3 [ОПЦИИ1] [ИНТЕРВАЛ] [filter ВЫРАЖЕНИЕ] [grep РЕГВЫР] [КОМАНДА] [ОПЦИИ2] 2.112 + 2.113 +== Интервалы == 2.114 + 2.115 +По умолчанию операция выполняется над всеми командами, 2.116 +находящимися в текущем контексте, начиная с текущей точки и до его конца. 2.117 + 2.118 +Можно ограничить команды с помощью интервала: 2.119 + 2.120 + выражение 2.121 + выражение,выражение 2.122 + 2.123 +Примеры: 2.124 + 2.125 + -10,. последние 10 команд 2.126 + .,+3 от текущей команды три вниз 2.127 + /grep/ последняя команда grep 2.128 + o/grep/ последняя команда, содержащая grep в выводе (output) 2.129 + . текущая строка 2.130 + @12:23 команда, выполненная в 12:23 (12:23 текущего часа) 2.131 + @08:12:00 команда, выполненная в 8:12:00 (или ближайшая после этого времени) 2.132 + 1 первая команда в контексте 2.133 + $ последняя команда в контексте 2.134 + % весь контекст 2.135 + 2.136 + 2.137 +Интервал указывается перед командой, работу которой он ограничивает: 2.138 + 2.139 + l3 -10,. cat 2.140 +напечатать последние 10 команд 2.141 + 2.142 +равносильно этому: 2.143 + l3 tail -10 2.144 + 2.145 +интервал = строка 2.146 +интервал = строка,строка 2.147 +интервал = % 2.148 +строка = число 2.149 + = -число 2.150 + = +число 2.151 + = @время 2.152 + = /регвыр/ 2.153 + = . 2.154 + = $ 2.155 + 2.156 +== Фильтрация == 2.157 + 2.158 +Можно указать дополнительный фильтр, 2.159 +который оставляет команды, которые нас интересуют. 2.160 + 2.161 +Есть два вида фильтров: 2.162 + * по регулярным выражениям 2.163 + * по вычисляемому выражению (синтаксис perl с некоторыми отличиями) 2.164 + 2.165 +grep -v regexp инверсия 2.166 +grep regexp прямой поиск 2.167 + 2.168 +-f полный 2.169 +-o только в выводе 2.170 +-c только в команде (по умолчанию) 2.171 + 2.172 +filter выражение 2.173 + 2.174 +== Команды == 2.175 + 2.176 +cat 2.177 +tail 2.178 +head 2.179 +history 2.180 + 2.181 +Все эти команды являются сокращением для одной команды. 2.182 + 2.183 +== Зачем всё это нужно == 2.184 + 2.185 +* использование данных вывода команд при автоматическом продолжении команд 2.186 +* быстрое выделение и копирование команд 2.187 +* поиск команд 2.188 +* привязка ко времени 2.189 +* подключение к журналу 2.190 + 2.191 +=== Tab-completion: Данные вывода команд при наборе других команд === 2.192 + 2.193 +Вы выполнили одну команду, 2.194 +и хотите использовать результаты её исполнения 2.195 +в качестве аргумента другой. 2.196 + 2.197 +Например, вы просканировали wifi-сети: 2.198 + # iwlist scanning 2.199 +В выводе команды присутствовали названия SSID, один из которых мы будем использовать дальше. 2.200 +Было бы классно, если бы дальше, когда мы вводим команду 2.201 + # iwconfig wlan0 essid ____ 2.202 +мы могли использовать табуляцию при наборе essid. 2.203 + 2.204 +В качестве списка возможных продолжений 2.205 +должны использоваться сети, которые выдала предыдущая команда. 2.206 + 2.207 +Это становится возможным с использованием l3text 2.208 +(при написании соответствующих расширения для bash_completion). 2.209 + 2.210 +=== Быстрое выделение и копирование команд === 2.211 + 2.212 +Ваш друг помогает вам настроить систему по джабберу. 2.213 +Он присылает вам команды, которые вы творчески перерабатываете 2.214 +и вводите в консоль. 2.215 + 2.216 +Потом, для того чтобы он видел, что получается, вы копируете результаты 2.217 +исполнения ему в джаббер. 2.218 + 2.219 +Например, вы хотите показать результат пяти последних команд: 2.220 + 2.221 +В самом простейшем случае (если вы используете графический-джаббер клиент) 2.222 + l3 tail -5 | xsel 2.223 + 2.224 +Потом идёте в джаббер-клиент и делаете: 2.225 + shift-insert 2.226 + 2.227 +Можно отправить ему код прямо из консоли: 2.228 + l3 tail -5 | sendxmpp igor@chub.in 2.229 + 2.230 +Или даже ещё проще, всё, что вы делаете будет копироваться ему: 2.231 + l3 tail -f | sendxmpp igor@chub.in 2.232 + 2.233 +Или, если вы не хотите, чтобы копировались команды, набранные неверно: 2.234 + l3 filter err!=128 tail -f | sendxmpp igor@chub.in 2.235 + 2.236 + 2.237 +Команды l3 не обязательно вызывать в той сессии, где идёт настройка. 2.238 +Вы можете их вызывать в соседнем терминале, где вообще не идёт запись. 2.239 +Конечно, вы должны будете указать соответствующий контекст. 2.240 + 2.241 + l3 cd /adm/lab1 2.242 + 2.243 +(предполагается, что настройка идёт в /adm/lab1 ) 2.244 + 2.245 +=== Поиск команд === 2.246 + 2.247 +Вы выполняете настройку сервера, 2.248 +в ходе которой сделали много инсталляций пакетов. 2.249 + 2.250 +Вы делали их в разных окнах, уже сами не помните в каких, 2.251 +но запись всё время шла. 2.252 + 2.253 +Вы хотите увидеть имена пакетов, которые были установлены. 2.254 + 2.255 +Простейший вариант: 2.256 + 2.257 + l3 grep apt-get 2.258 + 2.259 +Если вы помните, что начали работу по инсталляции после обеда, 2.260 +а всё, что было до этого, к делу не относится: 2.261 + l3 @14:00:00,$ grep apt-get 2.262 + 2.263 +Если вы помните, что было много инсталляций, но в некоторых из них 2.264 +вы неправильно указывали имена пакетов, и вы хотели бы оставить 2.265 +только те из них, которые выполнились успешно: 2.266 + l3 grep err=0 grep apt-get 2.267 +или 2.268 + l3 filter err=0&&/apt-get/ 2.269 +или 2.270 + l3 filter err=0 grep apt-get 2.271 + 2.272 +=== Привязка ко времени === 2.273 + 2.274 +Вы рассказываете, как настроить какую-то вещь. 2.275 +Вы хотели бы, чтобы всё, что вы рассказываете, 2.276 +можно было использовать как основу для составления 2.277 +более подробного документа. 2.278 + 2.279 +Вы используете доску (или графический планшет), 2.280 +некоторые вещи делаются не в командной строке, 2.281 +а на экране с графическим интерфейсом, 2.282 +кроме того ваш голос записывается. 2.283 + 2.284 +Вы не можете тратить время на то, чтобы положить скриншоты 2.285 +в нужное место, но вы можете их сделать. 2.286 +Аналогично и со скринкастами. Вы можете записывать то, 2.287 +что вы делаете при подключении к какой-то системе, 2.288 +но вы не можете выполнять никаких других операций 2.289 +по обработке записи. Потому что на это просто нет времени. 2.290 + 2.291 +Ход вашей работы фиксируется, операции запоминаются, 2.292 +скриншоты помечаются временными метками. 2.293 +Аналогично происходит со схемами на доске, 2.294 +которые фотографируются и представляются в виде помеченных 2.295 +временными метками файлов. 2.296 + 2.297 +После того, как сеанс окончен, 2.298 +вы получаете его транскрипт, который можно дальше редактировать. 2.299 +Команды в этом транскрипте сопровождаются специальными якорями, 2.300 +с помощью которых они привязываются к записанному ранее сеансу. 2.301 + 2.302 +В транскрипте вы удаляете лишние команды, 2.303 +добавляете текст и получаете документ, 2.304 +который подробно описывает происходившее, 2.305 +причём при желании вы можете получить недостающую информацию 2.306 +из архива сделанных ранее операций. 2.307 + 2.308 +Туда же, в нужные (соответствующие времени) места, 2.309 +автоматически попадают сделанные скриншоты, 2.310 +скринкасты, фотографии, запись звука. 2.311 + 2.312 + 2.313 +=== Подключение к журналу === 2.314 + 2.315 +Продолжим предыдущий пример. 2.316 + 2.317 +Вы прочитали документацию, составленную по описанной выше методике, 2.318 +и теперь хотите выполнить такую же настройку. 2.319 + 2.320 +В обычном случае вы просто копируете команды в свою командную строку 2.321 +или набираете их заново, с учётом ваших собственных условий. 2.322 + 2.323 +Можно значительно ускорить и упростить этот процесс, 2.324 +а так же уменьшить вероятность ошибки возможной в ходе его выполнения. 2.325 + 2.326 +Вы подключаетесь к сделанной записи: 2.327 + l3 go http://xgu.ru/l3/option-82 2.328 + 2.329 +Все рассмотренные ранее команды становятся доступным. 2.330 + 2.331 +Становится доступным продолжение команд (tab completion) 2.332 +на основе информации, набранной в той работе 2.333 +и даже больше: вы можете добавить команды 2.334 +из контекста в историю текущего командного интерпретатора, 2.335 +как будто вы их сами уже выполняли. 2.336 + 2.337 +Автоматически они не добавляются, чтобы не создавать путаницы. 2.338 +Но вы можете их загрузить: 2.339 + l3 % | l3hist 2.340 +или, если вас интересуют только определённые команды, то с использование интервала, 2.341 +например: 2.342 + l3 @14:00,$ | l3hist 2.343 + 2.344 +Вам тогда не нужно будет их заново набирать. 2.345 +Достаточно прокрутить историю вверх, а потом просто выполнять 2.346 +по одной, нажимая ctrl-o, при этом адаптируя к своим условиям. 2.347 + 2.348 +Чтобы ещё сильнее сократить себе объём работы, 2.349 +можно применить процедуру адаптации, которая позволяет 2.350 +автоматически заменять специфические для процедуры параметры. 2.351 + 2.352 +Для этого вы вызываете: 2.353 + l3 parameterize 2.354 + l3 param (сокращённо) 2.355 +которая открывает текстовый редактор, где указывается два столбика параметров: 2.356 +что заменить и на что заменить. 2.357 + 2.358 +Например, пусть в описанном документе используются адреса: 2.359 + 192.168.15.1 2.360 + 192.168.15.254 2.361 + example.com 2.362 + 2.363 +Причём составитель этого документа мог указать, что эти параметры являются специфичными, 2.364 +а мог не указывать. Если он указал, то совсем хорошо — тогда вам нужно просто пройтись 2.365 +и добавить справа, на что они должны заменяться в вашем случае. 2.366 +Если же он не указал, то вам нужно придумать список замен по своему вкусу. 2.367 + 2.368 +Вы вводите локализованную версию: 2.369 + 192.168.15.1 10.0.35.1 2.370 + 192.168.15.254 10.0.35.254 2.371 + example.com xgu.ru 2.372 + 2.373 +Теперь, во всех выводах команды l3 будут исправленные команды 2.374 +и исправленные результаты их выполнения. 2.375 +Автопродолжение (tab completion) в шелле уже будет показывать исправленные аргументы. 2.376 +Историю командного интерпретатора вы тоже можете обновить, 2.377 +и в ней тогда будут изменённые команды. 2.378 + 2.379 +Использовать параметризацию с загрузкой удалённого контекста 2.380 +настолько логично, что вы можете это сделать одной командой: 2.381 + l3 go -p http://xgu.ru/l3/option-82 2.382 + 2.383 +
3.1 --- a/l3-agent Mon Dec 28 22:00:37 2009 +0300 3.2 +++ b/l3-agent Tue Mar 16 20:05:30 2010 +0200 3.3 @@ -10,6 +10,7 @@ 3.4 use Text::Iconv; 3.5 use Time::Local 'timelocal_nocheck'; 3.6 use IO::Socket; 3.7 +use DBI; 3.8 3.9 use lib "/etc/lilalo"; 3.10 use l3config; 3.11 @@ -176,8 +177,7 @@ 3.12 { 3.13 my $cline = $_[0]; 3.14 my @lists = split /\;/, $cline; 3.15 - 3.16 - 3.17 + 3.18 my @commands = (); 3.19 for my $list (@lists) { 3.20 push @commands, split /\|/, $list; 3.21 @@ -344,7 +344,7 @@ 3.22 my $tty = $1; 3.23 my %cl; 3.24 my $last_output_length=0; 3.25 - my $saved_output; 3.26 + my $saved_output; 3.27 while (<FILE>) { 3.28 $commandlines_processed++; 3.29 3.30 @@ -357,7 +357,8 @@ 3.31 $commandlines_loaded++; 3.32 $last_output_length=0; 3.33 3.34 - # Previous command 3.35 + # Сохраняем часть выполненного ранее разбора в переменной last_cl, 3.36 + # которую мы дополним некоторой информацией и сохраним позже 3.37 my %last_cl = %cl; 3.38 my $this_line = $1; 3.39 my $err = $2 || ""; 3.40 @@ -425,7 +426,6 @@ 3.41 $last_cl{"err"}=$err; 3.42 $last_cl{"err"}=130 if $err eq "^C"; 3.43 3.44 - 3.45 # Output 3.46 if (!$last_cl{"suppress_output"} || $last_cl{"err"}) { 3.47 for (my $i=0; $i<$Config{"terminal_height"}; $i++) { 3.48 @@ -570,8 +570,19 @@ 3.49 } 3.50 3.51 $vt{$local_session_id}->reset(); 3.52 - $saved_output=""; 3.53 + $saved_output=""; 3.54 3.55 +# Обработка команд с одинаковым временем 3.56 +# Скорее всего они набраны с помощью tab-completion 3.57 + if ((defined($last_cl{time}) 3.58 + && defined($cl{time}) 3.59 + && $last_cl{time} == $cl{time}) 3.60 + && 3.61 + (defined($last_cl{nonce}) 3.62 + && defined($cl{nonce}) 3.63 + && $last_cl{nonce} == $cl{nonce})) { 3.64 + $last_cl{"tab"}=1; 3.65 + }; 3.66 3.67 # Changing encoding 3.68 for (keys %last_cl) { 3.69 @@ -675,21 +686,109 @@ 3.70 $text =~ s/&/&/g; 3.71 $text =~ s/</</g; 3.72 $text =~ s/>/>/g; 3.73 + $text =~ s/"/"/g; 3.74 print $TO $text; 3.75 } 3.76 3.77 +sub strq 3.78 +{ 3.79 + my $text = join "", @_; 3.80 + $text =~ s/&/&/g; 3.81 + $text =~ s/</</g; 3.82 + $text =~ s/>/>/g; 3.83 + $text =~ s/"/"/g; 3.84 + return $text; 3.85 +} 3.86 3.87 =cut 3.88 Вывести результат обработки журнала. 3.89 =cut 3.90 3.91 +sub print_commands_to_sqlite_cache 3.92 +{ 3.93 + my $output_filename=$_[0]; 3.94 + my $db = DBI->connect("dbi:SQLite:$output_filename", "", "", 3.95 + {RaiseError => 1, AutoCommit => 1}); 3.96 + 3.97 + my $cl; 3.98 + for my $i (@Command_Lines_Index) { 3.99 + my $val; 3.100 + my $var_list; 3.101 + my $values_list; 3.102 + my $create_var_list; 3.103 + my $sql; 3.104 + $cl = $Command_Lines[$i]; 3.105 + 3.106 + $cl->{l3cd}=$Config{l3cd} if $Config{"l3cd"}; 3.107 + for my $element (qw( 3.108 + local_session_id 3.109 + history 3.110 + uid 3.111 + pid 3.112 + time 3.113 + pwd 3.114 + raw_start 3.115 + raw_output_start 3.116 + raw_end 3.117 + raw_file 3.118 + tty 3.119 + err 3.120 + last_command 3.121 + nonce 3.122 + l3cd 3.123 + tab 3.124 + )) { 3.125 + if (defined($cl->{"$element"})) { 3.126 + $val = $cl->{"$element"}; 3.127 + } 3.128 + else { 3.129 + $val = "" 3.130 + }; 3.131 + $var_list .= "$element,"; 3.132 + $values_list .= '"'.$val."\","; 3.133 + $create_var_list .= ", $element TEXT"; 3.134 + } 3.135 + for my $element (qw( 3.136 + prompt 3.137 + cline 3.138 + output 3.139 + )) { 3.140 + if (defined($cl->{"$element"})) { 3.141 + $val = $cl->{"$element"}; 3.142 + } 3.143 + else { 3.144 + $val = "" 3.145 + }; 3.146 + $var_list .= "$element,"; 3.147 + $values_list .= '"'.strq($val)."\","; 3.148 + $create_var_list .= ", $element TEXT"; 3.149 + } 3.150 + if ($cl->{"diff"}) { 3.151 + $val = strq(${$Diffs{$cl->{"diff"}}}{"text"}); 3.152 + } 3.153 + else { 3.154 + $val=""; 3.155 + } 3.156 + $var_list .= "diff,"; 3.157 + $create_var_list .= ", diff TEXT"; 3.158 + $values_list .= '"'.$val."\","; 3.159 + 3.160 + $db->do("CREATE TABLE IF NOT EXISTS commands (id INTEGER PRIMARY KEY $create_var_list)"); 3.161 + #print "CREATE TABLE commands (id INTEGER PRIMARY KEY $create_var_list)\n"; 3.162 + $values_list =~ s/,$//; 3.163 + $var_list =~ s/,$//; 3.164 + $sql = "INSERT INTO commands ($var_list) VALUES($values_list) "; 3.165 + print "$sql\n"; 3.166 + $db->do($sql); 3.167 + } 3.168 +} 3.169 + 3.170 sub print_command_lines 3.171 { 3.172 my $output_filename=$_[0]; 3.173 open(OUT, ">>", $output_filename) 3.174 or die "Can't open $output_filename for writing\n"; 3.175 3.176 - 3.177 my $cl; 3.178 my $in_range=0; 3.179 for my $i (@Command_Lines_Index) { 3.180 @@ -704,17 +803,12 @@ 3.181 next; 3.182 } 3.183 next if ($Config{"from"} && $Config{"to"} && !$in_range) 3.184 - || 3.185 - ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) 3.186 - || 3.187 - ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) 3.188 - || 3.189 - ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); 3.190 - 3.191 + || ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) 3.192 + || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) 3.193 + || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); 3.194 + 3.195 # Вырезаем из вывода только нужное количество строк 3.196 - 3.197 my $output=""; 3.198 - 3.199 if (!grep ($_ eq $cl->{"last_command"}, @{$Config{"full_output_commands"}}) 3.200 && ($Config{"head_lines"} 3.201 || $Config{"tail_lines"})) { 3.202 @@ -730,20 +824,20 @@ 3.203 if ($start < 0) { 3.204 $start=0; 3.205 $mark=0; 3.206 - } 3.207 + } 3.208 if ($start < $Config{"cache_head_lines"}) { 3.209 $start=$Config{"cache_head_lines"}; 3.210 $mark=0; 3.211 - } 3.212 + } 3.213 $output .= $Config{"skip_text"}."\n" if $mark; 3.214 for ($i=$start; $i<= $#lines; $i++) { 3.215 $output .= $lines[$i]."\n"; 3.216 } 3.217 - } 3.218 + } 3.219 else { 3.220 # Full output 3.221 $output .= $cl->{"output"}; 3.222 - } 3.223 + } 3.224 3.225 # Совместимость с labmaker 3.226 3.227 @@ -759,7 +853,6 @@ 3.228 # timelocal( $sec, $min, $hour, $mday,$mon,$year); 3.229 $cl->{time} ||= timelocal_nocheck($cl->{sec},$cl->{min},$cl->{hour},$cl->{day},0,$year); 3.230 3.231 - 3.232 # Начинаем вывод команды 3.233 print OUT "<command>\n"; 3.234 print OUT "<l3cd>$Config{l3cd}</l3cd>\n" if $Config{"l3cd"}; 3.235 @@ -917,6 +1010,7 @@ 3.236 load_command_lines($Config{"input"}, $Config{"input_mask"}); 3.237 sort_command_lines; 3.238 #process_command_lines; 3.239 + print_commands_to_sqlite_cache($Config{"cache_sqlite"}); 3.240 print_command_lines($Config{"cache"}); 3.241 } 3.242 else { 3.243 @@ -975,6 +1069,7 @@ 3.244 if (@Command_Lines) { 3.245 sort_command_lines; 3.246 #process_command_lines; 3.247 + print_commands_to_sqlite_cache($Config{"cache_sqlite"}); 3.248 print_command_lines($Config{"cache"}); 3.249 } 3.250 save_cache_stat();
4.1 --- a/l3bashrc Mon Dec 28 22:00:37 2009 +0300 4.2 +++ b/l3bashrc Tue Mar 16 20:05:30 2010 +0200 4.3 @@ -57,6 +57,7 @@ 4.4 { 4.5 export L3_SESSION_ID=${RANDOM}${RANDOM}${RANDOM}${RANDOM}-`date +%s` 4.6 export L3_HOME=~/.lilalo/ 4.7 + L3_SQLITE=$L3_HOME/report.sqlite 4.8 mkdir -p $L3_HOME 4.9 4.10 tty=`/usr/bin/tty` 4.11 @@ -70,6 +71,21 @@ 4.12 hostname=`hostname -f 2> /dev/null` 4.13 [ -n "$bsd" ] && hostname=`hostname` 4.14 4.15 + cat <<INFO | perl 4.16 +use DBI; 4.17 +use strict; 4.18 +my \$db = DBI->connect("dbi:SQLite:$L3_SQLITE", "", "", 4.19 +{RaiseError => 1, AutoCommit => 1}); 4.20 + 4.21 +\$db->do("CREATE TABLE IF NOT EXISTS sessions (id INTEGER PRIMARY KEY, 4.22 + session TEXT, hostname TEXT, user TEXT, uid TEXT, login_from TEXT, 4.23 + tty TEXT, system TEXT, parent TEXT, ppid TEXT, pid TEXT, start_time TEXT, lang TEXT)"); 4.24 +\$db->do("INSERT INTO sessions VALUES (NULL, '$L3_SESSION_ID', '$hostname', '$USER', '$UID', '$login_from', 4.25 + '$tty', '$system', '$parent', '$PPID', '$$', 4.26 + '$start_time', '$LANG')"); 4.27 +INFO 4.28 + #perl $temp_l3_name #; rm $temp_l3_name; unset temp_l3_name 4.29 + 4.30 cat <<INFO > $L3_HOME/$L3_SESSION_ID.info 4.31 <session> 4.32 <local_session_id>$L3_SESSION_ID</local_session_id> 4.33 @@ -258,11 +274,14 @@ 4.34 l3 () 4.35 { 4.36 case "$1" in 4.37 - on) 4.38 - echo switching on 4.39 - ;; 4.40 - off) 4.41 - echo switcing off 4.42 + context) 4.43 + if [ -z "$2" ] 4.44 + then 4.45 + echo "$L3_CONTEXT" 4.46 + else 4.47 + echo $2 | grep -q ^/ && L3_CONTEXT="$2" || L3_CONTEXT="$L3_CONTEXT/$2" 4.48 + export L3_CONTEXT="`echo $L3_CONTEXT | perl -e '$_=<>; 1 while s@/[^/]*/\.\.@@; print;'`" 4.49 + fi 4.50 ;; 4.51 cd) 4.52 echo l3cd="$2" > ~/.l3rc 4.53 @@ -271,26 +290,10 @@ 4.54 grep ^l3cd= ~/.l3rc | sed s/[^=]*=// 4.55 ;; 4.56 *) 4.57 - cat <<EOF 4.58 -l3 [command]: 4.59 - 4.60 - cd new_context - change current lilalo context 4.61 - pwd - show current lilalo context 4.62 - on - switch writing on (NOT IMPLEMENTED YET) 4.63 - off - switch writing off (NOT IMPLEMENTED YET) 4.64 - 4.65 -EOF 4.66 + l3text "$@" 4.67 ;; 4.68 esac 4.69 } 4.70 4.71 -l3cd() 4.72 -{ 4.73 - l3 cd "$@" 4.74 -} 4.75 4.76 -l3pwd() 4.77 -{ 4.78 - l3 pwd 4.79 -} 4.80
5.1 --- a/l3config.pm Mon Dec 28 22:00:37 2009 +0300 5.2 +++ b/l3config.pm Tue Mar 16 20:05:30 2010 +0200 5.3 @@ -6,7 +6,7 @@ 5.4 use Getopt::Long; 5.5 5.6 @ISA = ('Exporter'); 5.7 -@EXPORT = qw(%Config &init_config); 5.8 +@EXPORT = qw(%Config &init_config &init_config_without_command_line_processing); 5.9 5.10 our $System_Config_File = "/etc/lilalo.conf"; 5.11 our $User_Config_File = "$ENV{HOME}/.l3rc"; 5.12 @@ -49,6 +49,7 @@ 5.13 "encoding" => "utf-8", 5.14 5.15 "cache" => "$ENV{HOME}/.lilalo/report.xml", 5.16 + "cache_sqlite" => "$ENV{HOME}/.lilalo/report.sqlite", 5.17 "cache_stat" => "$ENV{HOME}/.lilalo/.report.dat", 5.18 5.19 "output" => "/tmp/report.html", 5.20 @@ -152,32 +153,44 @@ 5.21 5.22 sub read_config_file 5.23 { 5.24 - my $config = $_[0]; 5.25 - my $filename = $_[1]; 5.26 - open(CONFIG, "$filename") 5.27 - or return; 5.28 - while (<CONFIG>) { 5.29 + my $config = $_[0]; 5.30 + my $filename = $_[1]; 5.31 + open(CONFIG, "$filename") 5.32 + or return; 5.33 + while (<CONFIG>) { 5.34 chomp; 5.35 - s/#.*//; 5.36 - next if /^\s*$/; 5.37 - my ($var, $val) = split /\s*=\s*/, $_, 2; 5.38 - $var =~ s/\s*//; 5.39 - $config->{$var} = $val; 5.40 - } 5.41 - close(CONFIG); 5.42 + s/#.*//; 5.43 + next if /^\s*$/; 5.44 + my ($var, $val) = split /\s*=\s*/, $_, 2; 5.45 + $var =~ s/\s*//; 5.46 + $config->{$var} = $val; 5.47 + } 5.48 + close(CONFIG); 5.49 } 5.50 5.51 5.52 sub init_config 5.53 { 5.54 - my %argv_config; 5.55 - my %file_config; 5.56 - read_config_file(\%file_config, $System_Config_File); 5.57 - read_config_file(\%file_config, $User_Config_File); 5.58 - GetOptions(\%argv_config, map "$_=s", keys %Config); 5.59 - %Config = (%Config, %file_config, %argv_config); 5.60 + my %argv_config; 5.61 + my %file_config; 5.62 + read_config_file(\%file_config, $System_Config_File); 5.63 + read_config_file(\%file_config, $User_Config_File); 5.64 + GetOptions(\%argv_config, map "$_=s", keys %Config); 5.65 + %Config = (%Config, %file_config, %argv_config); 5.66 for my $key (keys %Config) { 5.67 utf8::decode($Config{$key}); 5.68 } 5.69 } 5.70 5.71 +sub init_config_without_command_line_processing 5.72 +{ 5.73 + my %argv_config; 5.74 + my %file_config; 5.75 + read_config_file(\%file_config, $System_Config_File); 5.76 + read_config_file(\%file_config, $User_Config_File); 5.77 + %Config = (%Config, %file_config); 5.78 + for my $key (keys %Config) { 5.79 + utf8::decode($Config{$key}); 5.80 + } 5.81 +} 5.82 +
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/l3text Tue Mar 16 20:05:30 2010 +0200 6.3 @@ -0,0 +1,365 @@ 6.4 +#!/usr/bin/perl -w 6.5 + 6.6 +use POSIX qw(strftime); 6.7 +use lib '/etc/lilalo'; 6.8 +use l3config; 6.9 +use utf8; 6.10 +use DBI; 6.11 +#no warnings; 6.12 + 6.13 +our @Fields=qw/id time prompt cline output err last_command tab diff l3cd/; 6.14 +our $Fields=join(", ",@Fields); 6.15 +our $db; 6.16 + 6.17 +=cut 6.18 +Множество команд, с которыми выполняется операция, 6.19 +определяется текущим контекстом и текущей строкой, 6.20 +а также параметрами: 6.21 + * фильтр; 6.22 + * регулярное выражение; 6.23 + * интервал. 6.24 +=cut 6.25 +our $Filter; 6.26 +our $Grep; 6.27 +our $Interval; 6.28 +our $Command; 6.29 + 6.30 +# Если мы используем режим -f, то в этой переменной запоминается 6.31 +# до какой точки мы дошли 6.32 +our $Follow=0; 6.33 + 6.34 +our $Context; 6.35 + 6.36 +our @Commands = qw/head tail cat history/; 6.37 + 6.38 +sub main(); 6.39 +sub connect_database(); 6.40 + 6.41 +main(); 6.42 + 6.43 +sub main() 6.44 +{ 6.45 + $| = 1; 6.46 + 6.47 + init_config_without_command_line_processing(); 6.48 + $Context=$Config{l3cd}; 6.49 + connect_database(); 6.50 +# Обработка всех grep'ов 6.51 + $Grep=""; 6.52 + my $i=0; 6.53 + my $arg; 6.54 + my @argv=(); 6.55 + while ($i<=$#ARGV) { 6.56 + $arg=$ARGV[$i]; 6.57 + #print "arg=$arg\n"; 6.58 + if ($arg eq "grep") { 6.59 + my $grep_keys=""; 6.60 + my $grep_regexp=""; 6.61 + $i++; 6.62 + while(defined ($ARGV[$i]) and $ARGV[$i] =~ /^-/) { 6.63 + $grep_keys .= " ".$ARGV[$i]; 6.64 + $i++; 6.65 + } 6.66 + if (defined($ARGV[$i])) { 6.67 + $grep_regexp = $ARGV[$i]; 6.68 + $i++; 6.69 + } 6.70 + else { 6.71 + die "ERROR: grep argument is not specified"; 6.72 + } 6.73 + $Grep .= grep_options($grep_regexp,$grep_keys); 6.74 + } 6.75 + else { 6.76 + $i++; 6.77 + push(@new_argv,$arg); 6.78 + } 6.79 + } 6.80 + @argv=@new_argv; 6.81 + 6.82 + if (grep(/^-f$/, @argv)) { 6.83 + $follow=1; 6.84 + } 6.85 + if (grep(/^history$/, @argv)) { 6.86 + my $interval=$argv[0]||"%"; 6.87 + $interval="%" if $interval eq "history"; 6.88 + my $exit=0; 6.89 + while (not $exit) { 6.90 + print_commands(select_interval($interval, $Grep)); 6.91 + if (not $follow) { 6.92 + $exit=1; 6.93 + } else { 6.94 + sleep(1); 6.95 + } 6.96 + } 6.97 + } else { 6.98 + my $exit=0; 6.99 + while (not $exit) { 6.100 + print_all(select_interval($argv[0]||"%", $Grep)); 6.101 + if (not $follow) { 6.102 + $exit=1; 6.103 + } else { 6.104 + sleep(1); 6.105 + } 6.106 + } 6.107 + } 6.108 + #print_all(select_by_cline("*privet*")); 6.109 +} 6.110 + 6.111 +sub connect_database() 6.112 +{ 6.113 + $db = DBI->connect("dbi:SQLite:".$Config{cache_sqlite}, "", "", 6.114 + {RaiseError => 1, AutoCommit => 1}); 6.115 + $db->func('regexp', 2, sub { 6.116 + my ($regex, $string) = @_; 6.117 + return $string =~ /$regex/; 6.118 + }, 'create_function'); 6.119 +} 6.120 + 6.121 +sub select_all() { 6.122 + $l3cd=$Config{l3cd}; 6.123 + return $db->selectall_arrayref("SELECT $Fields FROM commands WHERE l3cd='$l3cd'"); 6.124 +} 6.125 + 6.126 +sub select_last_n($) { 6.127 + my $last=$_[0]; 6.128 + $count = $db->selectall_arrayref("SELECT COUNT(*) FROM commands"); 6.129 + my $offset=$$count[0][0]-$last; 6.130 + return $db->selectall_arrayref("SELECT $Fields FROM commands LIMIT $last OFFSET $offset"); 6.131 +} 6.132 + 6.133 +sub select_interval($) { 6.134 + my $interval = shift; 6.135 + my $grep = shift; 6.136 + 6.137 + my $q; # Рабочая переменная для запросов 6.138 +# % 6.139 + $interval='1,$' if ($interval eq "%"); 6.140 +# return $db->selectall_arrayref( 6.141 +# "SELECT $Fields FROM commands WHERE l3cd='$Context'"); 6.142 + 6.143 +# Вычисляем номера начальной и конечной строки 6.144 +# на основе интервала 6.145 + my ($start,$stop); 6.146 + my ($start_n, $stop_n, $curr_n) = ("","",""); # номера начальной и конечной строк 6.147 + if ($interval =~ /,/) { 6.148 + ($start,$stop) = split(/,/,$interval); 6.149 + } 6.150 + else { 6.151 + $start = $interval; 6.152 + $stop = $interval; 6.153 + } 6.154 + 6.155 + $q = $db->selectall_arrayref("SELECT COUNT(*) FROM commands"); 6.156 + my $count = $$q[0][0]; 6.157 + 6.158 +# Номер текущей строки 6.159 +# По умолчанию текущая строка указывает на последнюю 6.160 + $curr_n = $count; 6.161 + #$curr_n = 2; 6.162 + 6.163 + if ($start =~ /^[0-9]*$/) { $start_n = $start; } 6.164 + if ($stop =~ /^[0-9]*$/) { $stop_n = $stop; } 6.165 + if ($start =~ /^-[0-9]*$/) { $start_n = $curr_n + $start; } 6.166 + if ($stop =~ /^-[0-9]*$/) { $stop_n = $curr_n + $stop; } 6.167 + if ($start =~ /^\+[0-9]*$/) { $start_n = $curr_n + $start; } 6.168 + if ($stop =~ /^\+[0-9]*$/) { $stop_n = $curr_n + $stop; } 6.169 + if ($start eq '.') { $start_n = $curr_n; } 6.170 + if ($stop eq '.') { $stop_n = $curr_n; } 6.171 + if ($start eq '$') { $start_n = $count; } 6.172 + if ($stop eq '$') { $stop_n = $count; } 6.173 + if ($start =~ m@^/(.*)/@ ) { 6.174 + $q = $db->selectall_arrayref( 6.175 + "SELECT id FROM commands WHERE cline REGEXP '$1' LIMIT 1"); 6.176 + $start_n=$$q[0][0]; 6.177 + } 6.178 + if ($stop =~ m@^/(.*)/@ ) { 6.179 + $q = $db->selectall_arrayref( 6.180 + "SELECT id FROM commands 6.181 + WHERE cline REGEXP '$1' 6.182 + AND (id >= $start_n) LIMIT 1"); 6.183 + $stop_n=$$q[0][0]; 6.184 + } 6.185 + if ($start =~ m@^o/(.*)/@ ) { 6.186 + $q = $db->selectall_arrayref( 6.187 + "SELECT id FROM commands WHERE output REGEXP '$1' LIMIT 1"); 6.188 + $start_n=$$q[0][0]; 6.189 + } 6.190 + if ($stop =~ m@^o/(.*)/@ && $start_n) { 6.191 + $q = $db->selectall_arrayref( 6.192 + "SELECT id FROM commands 6.193 + WHERE output REGEXP '$1' AND (id >= $start_n) LIMIT 1"); 6.194 + $stop_n=$$q[0][0]; 6.195 + } 6.196 + 6.197 + if (not $start_n or not $stop_n) {return ""}; 6.198 + 6.199 + $stop_n = $count if $count < $stop_n; 6.200 + $start_n =1 if 1 > $start_n; 6.201 + 6.202 + my $offset=$start_n-1; 6.203 + my $limit = $stop_n - $offset; 6.204 + 6.205 + my $pre_follow=$Follow; 6.206 + $q = $db->selectall_arrayref( 6.207 + "SELECT id FROM commands 6.208 + WHERE ((id>=$start_n AND id<=$stop_n AND id>$pre_follow) $grep) 6.209 + ORDER BY id DESC"); 6.210 + $Follow = $$q[0][0] if $$q[0][0]; 6.211 + return $db->selectall_arrayref( 6.212 + "SELECT $Fields FROM commands 6.213 + WHERE ((id>=$start_n AND id<=$stop_n AND id>$pre_follow) $grep)"); 6.214 +} 6.215 + 6.216 +sub grep_options($$) { 6.217 +=cut 6.218 + -c ищем только в комадной строке # по умолчанию 6.219 + -o ищем только в выводе 6.220 + -f ищем и там, и там 6.221 + -v инверсия 6.222 +=cut 6.223 + my $regexp=shift; return "" if $regexp eq ''; 6.224 + my $options=shift; 6.225 + my @options = split //, $options; 6.226 + my $not = ""; 6.227 + $not = "NOT" if grep(/v/,@options); 6.228 + 6.229 + my $grep =""; 6.230 + if (grep(/f/,@options)) { 6.231 + $grep = "AND $not ((cline REGEXP '$regexp') OR (output REGEXP '$regexp'))"; 6.232 + } 6.233 + elsif (grep(/o/,@options)) { 6.234 + $grep = "AND $not (output REGEXP '$regexp')"; 6.235 + } 6.236 + elsif (1 || grep(/c/,@options)) { 6.237 + $grep = "AND $not (cline REGEXP '$regexp')"; 6.238 + } 6.239 + 6.240 + return $grep; 6.241 +} 6.242 + 6.243 +sub select_by_cline($) { 6.244 + my $cline = $_[0]; 6.245 + return $db->selectall_arrayref("SELECT $Fields FROM commands WHERE cline GLOB '$cline'"); 6.246 +} 6.247 + 6.248 +sub print_commands($) 6.249 +{ 6.250 + my @fields=@Fields; 6.251 + my $fields=join(", ",@fields); 6.252 + my $all=$_[0]; 6.253 + foreach my $row (@$all) { 6.254 + my $cl; #Каждая строка 6.255 + my $i=0; 6.256 + for my $f (@fields) { 6.257 + $cl->{$fields[$i]}=$$row[$i]; 6.258 + $i++; 6.259 + } 6.260 + print $cl->{id}." ".strdequot($cl->{cline})."\n"; 6.261 + } 6.262 +} 6.263 +sub print_all($) { 6.264 + my @fields=@Fields; 6.265 + my $fields=join(", ",@fields); 6.266 + my $all=$_[0]; 6.267 + my $prev_cl; # Предыдущая строка 6.268 + my $tab_seq; # Номер в табуляционной последовательности 6.269 + foreach my $row (@$all) { 6.270 + my $cl; #Каждая строка 6.271 + my @anchor; #Начальная строка, якорь, по которому привязывается команда 6.272 + my $res=""; #Буфер, в который выводится результат вывода каждой строки 6.273 + $i=0; 6.274 + for my $f (@fields) { 6.275 + $cl->{$fields[$i]}=$$row[$i]; 6.276 + $i++; 6.277 + } 6.278 + next if ($Config{"skip_empty"} =~ /^y/i && $cl->{"cline"} =~ /^\s*$/ ) 6.279 + || ($Config{"skip_wrong"} =~ /^y/i && $cl->{"err"} != 0) 6.280 + || ($Config{"skip_interrupted"} =~ /^y/i && $cl->{"err"} == 130); 6.281 + 6.282 + # Время 6.283 + # Добавляем спереди 0 для удобочитаемости 6.284 + my ($sec,$min,$hour,$day,$mon,$year,$wday,$yday,$isdst) = localtime($cl->{time}); 6.285 + $min = "0".$min if $min =~ /^.$/; 6.286 + $hour = "0".$hour if $hour =~ /^.$/; 6.287 + $sec = "0".$sec if $sec =~ /^.$/; 6.288 + 6.289 + if ($Config{"show_time"} =~ /^y/i) { 6.290 + push @anchor,"$hour:$min:$sec "; 6.291 + } 6.292 + 6.293 + if ($cl->{"err"}) { 6.294 + push @anchor, "err=".$cl->{'err'}; 6.295 + } 6.296 + if ($cl->{"tab"}) { 6.297 + push @anchor, "tab=".$cl->{'tab'}; 6.298 + } 6.299 + $res.="#=".join("",@anchor)."\n"; 6.300 + $res.=$cl->{prompt}.strdequot($cl->{cline})."\n"; 6.301 + 6.302 + my $output = ""; # фомируем вывод команды 6.303 + my $last_command = $cl->{"last_command"}; 6.304 + if (!( $Config{"suppress_editors"} =~ /^y/i 6.305 + && grep ($_ eq $last_command, @{$Config{"editors"}}) 6.306 + || $Config{"suppress_pagers"} =~ /^y/i 6.307 + && grep ($_ eq $last_command, @{$Config{"pagers"}}) 6.308 + || $Config{"suppress_terminal"}=~ /^y/i 6.309 + && grep ($_ eq $last_command, @{$Config{"terminal"}}) 6.310 + )) { 6.311 + $output = strdequot($cl->{output}); 6.312 + 6.313 + # Вырезаем слишком длинные выводы 6.314 + my @lines = split '\n', $output; 6.315 + if (!$Config{"command_id"} 6.316 + && ( $Config{"head_lines"} || $Config{"tail_lines"}) 6.317 + && $#lines > $Config{"head_lines"} + $Config{"tail_lines"}) { 6.318 + $output=""; 6.319 + for (my $i=0; $i<= $#lines && $i < $Config{"head_lines"}; $i++) { 6.320 + $output .= $lines[$i]."\n"; 6.321 + } 6.322 + $output .= $Config{"skip_text"}."\n"; 6.323 + 6.324 + my $start_line=$#lines-$Config{"tail_lines"}+1; 6.325 + for (my $i=$start_line; $i<= $#lines; $i++) { 6.326 + $output .= $lines[$i]."\n"; 6.327 + } 6.328 + } 6.329 + } 6.330 + $res.=$output; 6.331 + if ( $Config{"show_diffs"} =~ /^y/i && $cl->{"diff"}) { 6.332 + $res.= $cl->{"diff"} 6.333 + } 6.334 + #open(OUTPUT, '|sendxmpp nata_samoylenko@jabber.ru'); 6.335 + #print OUTPUT $res; 6.336 + #close(OUTPUT); 6.337 + print $res; 6.338 + $res=""; 6.339 + $prev_cl=$cl; 6.340 + } 6.341 +} 6.342 + 6.343 +sub strdequot($) 6.344 +{ 6.345 + my $text = join "", @_; 6.346 + $text =~ s/&/&/g; 6.347 + $text =~ s/</</g; 6.348 + $text =~ s/>/>/g; 6.349 + $text =~ s/"/"/g; 6.350 + return $text; 6.351 +} 6.352 + 6.353 +sub show_usage() 6.354 +{ 6.355 + print <<EOF; 6.356 +Usage: 6.357 + l3 [INTERVAL] [grep [KEYS] REGEXP] [filter [KEYS] FILTER] [COMMAND [KEYS]] 6.358 + 6.359 +Commands: 6.360 + history 6.361 + tail 6.362 + head 6.363 + cat (default) 6.364 + 6.365 + If COMMAND is not specified, cat assumed. 6.366 + 6.367 +EOF 6.368 +}