20080728

less: автоматический просмотр архивов *.gz и других типов файлов

Популярную листалку для командной строки less легко научить просматривать не только текстовые файлы, но и многие другие. Например, ей можно смотреть сжатые файлы или файлы PDF не задумываясь о вызове архиватора или нужного конвертера. Примерно так:
$ less /usr/share/doc/less/changelog.gz
Собственно, во многих дистрибутивах так уже настроено по умолчанию. В Debian эта возможность по умолчанию выключена. Чтобы она заработала нужно в ~/.bashrc (если пользуетесь bash) добавить eval $(lesspipe) или eval $(lessfile). Это установит переменные окружения LESSOPEN и LESSCLOSE, говорящие less пропускать файлы через указанный фильтр. Фильтр /usr/bin/lesspipe смотрит на расширение файла и запускает нужный конвертрер, если необходимо. Разница между lesspipe и lessfile в том, что первый конвертирует файлы на лету, сразу отдавая результат в less, а lessfile вначале полностью конвертирует файл, и только потом вызывает less.

Понятно, что очень просто можно настроить пользовательские фильтры. Добавлять их нужно в пользовательский скрипт ~/.lessfilter. Например, чтобы просматривать с помощью less файлы OpenOffice, я добавляю фильтр, вызывающий antiodt, а чтобы просматривать файлы MSWord, фильтр, вызывающий antiword. Файл ~/.lessfilter выглядит при этом так:
#!/bin/sh
(
case `echo "$1" | tr '[:upper:]' '[:lower:]'` in
    *.odt)
        if [ -x "`which antiodt`" ]; then antiodt "$1" ;
        else cat "$1"; fi ;; # No antiodt available
    *.doc)
        if [ -x "`which antiword`" ]; then antiword "$1" ;
        else cat "$1"; fi ;; # No antiword available
esac
) 2>/dev/null
P.S. Код раскрасил с помощью плагина toblog к vim, создающего цветной HTML согласно текущей цветовой схеме редактора.

20080725

WiFi карточка D-Link DWL-G630 (RaLink RT2561/RT61 rev B 802.11g)

Достал из коробки лежащую уже с год без дела WiFi-карточку D-Link DWL-G630. lspci опознаёт её так:
07:00.0 Network controller: RaLink RT2561/RT61 rev B 802.11g
Вспомнил, что год назад нужно было скачивать драйвера с сайта Ralink и настраивать их весьма нестандартым способом. Детали, впрочем, забыл. За прошедший год ситуация несколько изменилась. Теперь (дистрибутивное ядро 2.6.24-1-686) при втыкании карточки автоматически подгружается модуль rt61pci, создаётся почему-то сразу два интерфейса ra0 (почему-то без wireless-extensions) и wlan0_rename. Честно говоря, я так и не понял, поддерживает ли этот драйвер rt61pci мою карточку в варианте «rev B» или нет…

В общем, скачал то, что называется улучшенный legacy driver, rt61, CVS-срез. В отличие от идущего с ядром rt61pci он не умеет работать с wpa_supplicant, но по моему прошлому опыту он точно поддерживает эту карточку. После распаковки архива с драйвером, перехода в rt61-cvs-2008072016/Module/, make и make install через минуту меня стал доступен новый драйвер rt61.

Поскольку в отличие от ситуации год назад настраивается он теперь вполне стандартными инструментами iwconfig и iwpriv, я удалил оставшийся от прошлогодних экспериментов каталог с его настройками /etc/Wireless.

Для работы карточки требуется установить также прошивки. Я просто поставил пакет firmware-ralink, и нужные прошивки легли куда надо (в /lib/firmware). Прошивки можно взять также отдельно с сайта Ralink.

После этого при втыкании карточки iwconfig стал показывать только один интерфейс ra0, а lsmod подтвердил, что автоматически грузиться rt61.

Дальнейшая настройка достаточно проста. Можно после втыкания карточки просто запускать графическую утилиту RutilT (есть в дистрибутиве), и в ней выбирать нужную сеть и вбивать пароль.

Можно и традиционно, из коммандной строки, используя iwconfig. Для настроек шифрования вместо wpa_supplicant с этим драйвером можно установить нужные параметры утилитой iwpriv (примеры есть в README к драйверу). Мне же хотелось, чтобы настройки определялись в традиционном для Debian /etc/network/interfaces, поэтому все нужные команды прописал туда. Моя точка доступа настроена на использование WPA2PSK с шифрованием AES (для WPAPSK/TKIP заменить на WPAPSK и TKIP соответственно). В этом случае настройки такие::
auto ra0
iface ra0 inet dhcp
pre-up iwconfig ra0 mode managed
pre-up iwpriv ra0 set AuthMode=WPA2PSK
pre-up iwpriv ra0 set EncrypType=AES
pre-up iwconfig ra0 essid "MYESSID"
pre-up iwpriv ra0 set WPAPSK="mypassword"
После этого беспроводная сеть стала подключаться автоматически, а также отключаться и подключаться по ifdown/ifup ra0. Мне бо́льшего и не надо :)

Если что, вот ветка форума, посвящённая обсуждению legacy-драйвера rt61 (pci).

20080724

Возврат денег за предустановленную Висту

Прочитал, как человеку вернули $73.90 (по курсу ЦБР) за предустановленную на компьютер Windows Vista. Молодец, товарищ, довёл дело до конца! И это у нас, в России!

Я вдохновился и составил памятку Как вернуть деньги за Windows Vista. В смысле, записал, что знаю и о чём слышал. Если кто-то знает о других таких случаях — хорошо бы добавить ссылки на success stories в памятку. Хорошо бы такая практика получила бо́льшее распространение.

Дополнение: Есть сдвиги!
1) Организован сайт для коллективной разработки процедуры возврата денег за предустановленну ОС.
2) Центр свободных технологий начал с порочной практикой бороться.
3) ФАС на нашей стороне!

Как импортировать TSV или CSV в SQLite (когда grep уже не справляется)

Дано: большой текстовый файл с табличкой (колонки разделены табуляцией, TSV), 940 МБ.
Нужно: находить строчки с определённым значением и брать значение другой колонки, несколько сот раз в день.
Решение: конвертировать TSV-файл в базу данных SQLite и пользоваться базой данных. В этой заметке я расскажу как быстро импортировать TSV или CSV файл в SQLite.

Обычный способ, используем awk или grep:
$ time awk '/190711127/ { print $2, $7; }' datafile.tsv
6407305 190711127

real 2m21.116s
user 2m9.340s
sys 0m1.596s
Долго. С grep немного быстрее:
$ time grep 190711127 datafile.tsv | awk '{ print $2, $7; }'
6407305 190711127

real 0m33.283s
user 0m0.952s
sys 0m1.024s
Лучше, но всё равно долго. Можно вырезать заранее только нужные колонки и работать с файлом поменьше:
$ awk -v OFS='\t' '{ print $2, $7;}' datafile.tsv > onlytwo.tsv
$ du -sh onlytwo.tsv
194M onlytwo.tsv
$ time grep 190711127 onlytwo.tsv
6407305 190711127

real 0m7.686s
user 0m0.156s
sys 0m0.228s
Файл 200 МБ. 7 секунд на каждый поиск по нему, для нескольких сот запросов в день это слишком долго. Так что обычный grep здесь уже не справляется. Примерно те же проблемы при работе с файлом из скрипта на python: грузить целиком в память нехорошо, а читать каждый раз по строчке с диска — долго.

Было решено поместить данные из файла в базу данных SQLite, благо конфигурировать её не надо, с таким объёмом данных она справляется отлично, а использовать её можно из любых скриптов.

Первая мысль была сгенерировать SQL-скрипт, который создаст табличку и вставит в неё все значения. Скрипт сгенерировал. Запустил sqlite3 imported.db < onlytwo_insert.sql и стал ждать. Дело шло неспешно (минут за 20 я успел погуглить и прочитать документацию). Оказалось, для быстрого импорта данных в SQLite есть команда .import. Импорт сделал так:
$ sqlite3 imported.db
SQLite version 3.5.3
Enter ".help" for instructions
sqlite> create table a_b ( a integer, b integer ) ;
sqlite> .separator \t
sqlite> .import 'onlytwo.tsv' a_b
sqlite> create index a_idx a_b (a);
sqlite> create index b_idx a_b (b);
sqlite> .timer on
sqlite> select * from a_b where b = 190711127;
6407305|190711127
CPU Time: user 0.000000 sys 0.000000
Теперь после создания индексов поиск с помощью SQL-запроса занимает пренебрежимые в большинстве ситуаций доли секунды. Хочу заметить, что для импорта данных разделённых не табуляцией, а каким-то другим символом (запятой в CSV, пробелом, ещё чем-то) достаточно просто указать другой .separator. В отличие от выполнения SQL-запроса на вставку, .import работает быстро, но ошибки несоответствия типов игнорируются (данные из файла встравляются в базу как строки, что может потребовать выполнить потом ручной update заменяющий что-нибудь на честный null, например).

Полученную базу можно использовать в том числе и в bash-скриптах, примерно вот так:
$ time sqlite3 imported.db 'select * from a_b where b = 190711128;'
6405752|190711128

real 0m0.003s
user 0m0.000s
sys 0m0.004s
Размер базы с двумя колонками примерно равен размеру соответствующего текстового файла (197 МБ), но после создания двух индексов весит уже 534 МБ. Зато работает быстро.

20080717

Как добавить геотаги к фотографиям

Продолжаю серию заметок про GPS. В этот раз расскажу, как, используя GPS, добавить к фотографиям геотаги.
Геотаг
— связанная с фотографией информация о географических координатах места съёмки, высоте над уровнем моря и географическом названии местности; технически, геотаги — это дополнительные поля «GPS Latitude», «GPS Longitude» и «GPS Position» в заголовках EXIF.

Если в фотографии присутствует геотаг, то при загрузке её на Flickr или Panoramio она может быть автоматически привязана к карте (на фликере это надо явно разрешить в настройках учётной записи). Умеют обрабатывать геотаги и многие другие сервисы и программы (например, есть плагин для Gallery2).

Для создания геотагов нужно 3 вещи:
  1. Фотографии с правильными датами и временем в EXIF (для этого лучше ещё до съёмки установить часы фотокамеры по приёмнику GPS).
  2. GPS-трек, в котором записана траектория перемещения фотоаппарата (фотографа) с указанием времени.
  3. Программа, которая присваивает фотографии географические координаты из трека. Я расскажу о двух их них: digiKam и HappyCamel (хотя есть ещё gPicSync и другие программы)

Фотографии

Как я уже отметил, часы фотоаппарата лучше синхронизовать с GPS заранее, ещё до съёмки. Если же так случилось, что часы синхронизованы не были, то в дальнейшем придётся установить какое было расхождение часов в двух приборах и указывать его в процессе присвоения геотагов. Здесь и далее я предполагаю, что фотографии уже обработаны и сохранены в формате JPEG (не RAW) и имеют EXIF-заголовки с правильной датой съёмки.

GPS-трек

Подготовка GPS трека вещь более тонкая. Трек должен содержать время для каждой точки. Трек желательно должен быть непрерывным (я выключаю GPS на время стоянок и ночёвок ⇒ треки отдельных переходов нужно объединить в один). Из трека лучше выкинуть «ошибочные» точки (в зонах с плохим приёмом координаты GPS часто «скачут»). Максимальный временной интервал между точками трека должен быть желательно небольшим (иначе придётся полагаться на автоматическую интерполяцию уже в процессе присвоения координат).
В случае навигаторов Garmin это всё означает, что не надо пользоваться функцией «Save track» в приборе. Она не только не сохраняет трек, но и удаляет из него данные о времени, а также сокращает количество точек ради экономии памяти. Подробнее об этом здесь. Для наших целей нужно или брать основной и единственный активный трек из памяти устройства (он обычно называется «ACTIVE LOG #XX») или «сырой» GPX-трек сохраняемый на карточку памяти. А про функцию «Save track» в Garmin лучше забыть и проводить всю обработку треков на компьютере. Скопировать трек на компьютер можно любым удобным способом (некоторые из них я уже описал в предыдущей заметке).
Похоже, самые универсальный и мощный инструмент для обработки треков — GPSBabel. В случае, если я хочу сразу взять активный трек с устройства Garmin, подключенного к /dev/ttyUSB0, команда будет выглядеть так:
$ gpsbabel -t -w -i garmin -f /dev/ttyUSB0 -x radius,distance=3K,lat=45.058646,lon=6.907375 -x track,pack -x discard,hdop=3,vdop=3 -x position,distance=5m -x interpolate,time=60 -o gpx -F мойтрек.gpx
где параметры коммандной строки означают следующее:
  • -i garmin -f /dev/ttyUSB0 говорит брать исходные данные прямо с устройства Garmin подключенного по USB (если исходные данные уже доступны в виде GPX-файла, можно заменить на -i gpx -f исходный-трек.gpx, если исходных GPX-файлов несколько, то можно указать их всех, напримее -i gpx -f 20080621.gpx -f 20080622.gpx -f 20080623.gpx -f 20080624.gpx);
  • -t -w говорят брать с устройства информацию только о треках (-t) и точках маршрута (-w) — точки маршрута включаю, потому что трек мне пригодится и для других целей;
  • -x radius,distance=3K,lat=45.058646,lon=6.907375 — это фильтр, отсекающий всё, что не входит в круг радиусом 3 км относительно заданной точки (необязательно);
  • -x track,pack — этот фильтр объединяет треки отдельных переходов в один большой;
  • -x discard,hdop=3,vdop=3 — этот фильтр выбрасывает из трека точки, для которых горизонтальная или вертикальная мера ошибки больше 3 (как HDOP и VDOP связаны с метрами), на практике этот фильтр действительно эффективно отсеивает точки внезапно «выскочившие» из траектории, значение «3» эмпирическое, иногда я указываю даже «10»;
  • -x position,distance=5m — объединяет точки, расстояние между которыми меньше 5 м (короткие остановки);
  • -x interpolate,time=60 — интерполирует трек так, чтобы между соседними точками было не более 60 секунд (это один из самых полезных фильтров для создания геотагов!);
  • -o gpx -F мойтрек.gpx — сохраняет результат в файл мойтрек.gpx в формате GPX.
Проверить качество полученного трека можно просмотрев его в какой-нибудь программе. Мне пока больше всего нравится Viking, он быстро запускается и позволяет подгрузить карты Google под трек, чтобы посмотреть как трек соотносится с местностью. Если трек получился красивый и правильный, переходим к следующему этапу.

Добавление геотагов

Расскажу о двух программах, которые позволяют добавить геотаги: о digiKam (красивый фотоорганайзер с большими возможностями, для добавления геотагов есть графический интерфейс) и о HappyCamel (скрипт для коммандной строки, написанный на Python). Желающие могут легко найти и другие инструменты.

В любом случае, при добавлении геотагов на этом этапе кроме подготовленного трека в формате GPX и фотографий потребуется ещё вспомнить, в каком часовом поясе живём (какое время на фотоаппарате). Здесь нюанс: часовой пояс нужно будет указывать с поправкой на летнее время, то есть, если зимой +03:00, то летом будет +04:00, если зимой +01:00, то летом +02:00.

Добавляем геотеги в digiKam

Признаюсь, после долгого перерыва я опять установил digiKam как раз, чтобы попробовать добавить геотаги. К своему удивлению, обнаружил, что как фото-каталогизатор digiKam стала за эти год-два гораздо лучше. Я бы даже сказал лучшим из всех доступных под Linux :-) Настолько, что я его даже оставил и стал пользоваться. Тем более, что работает быстро, структуру моего фотоархива (ГОД/ГГГГММДД-название-альбома) понимает и не пытается переделывать, метаданные пишет в виде стандартного и открытого IPTC, а по возможностям превосходит всех остальных (F-Spot, Blue Marine, gThumb, Picasa).

Ну а как добавить геотаги, проще показать, чем рассказать:
how to add geotags in digiKam animated gif mini-tutorial

Добавляем геотеги с помощью HappyCamel

Можно добавить теги и из коммандной строки. Результат получается примерно тот же, только гибкости немного больше. В репозиториях Debian скрипта HappyCamel ещё нет, но установить его несложно с помощью
$ ./setup.py install --prefix=/префикс/для/установки
(если использовать checkinstall или stow, то потом его можно будет легко и просто убрать).

Лучше, конечно, если в системе будет установлен и exiftool (libimage-exiftool-perl в Debian). HappyCamel умеет писать в EXIF и без него, но автор рекомендует всё же полагаться на exiftool.

Чтобы добавить теги, переходим в каталог с фотографиями и выполняем примерно такую команду:
$ happycamel -x y --exiftool=y \
--utc-offset=+02:00 --elevation=2 --use-outside=y \
-t мойтрек.gpx *.jpg
  • -x y — писать геотэги в EXIF; можно попросить внести туда и географические названия с Geonames.org (-g y);
  • --exiftool=y — читать и писать EXIF используя exiftool;
  • --utc-offset=+02:00 — часовой пояс фотоаппарата (с учётом летнего времени!);
  • --elevation=2 — интерполировать данные о высоте над уровнем моря (не обязательно);
  • --use-outside=y — фотографии, сделанные до начала или после конца записи трека помечать используя первую и последнюю точки трека соответственно (не обязательно);
  • -t мойтрек.gpx — трек с траекторией;
  • *.jpg — no comments :-)
Вот в общем, и всё. Теперь можно загружать фотографии на Flickr или Panoramio. Да, если кому-то интересно, можете посмотреть на мои фотографии.

Другие заметки про GPS в Linux:


Как добавить геотеги без GPS

Если данных GPS, к сожалению, нет, а геотег добавить надо, то можно сделать так: загрузить фотку на Flickr, поместить её на карту с помощью Flickr Organizr, а потом скачать все помеченные таки образом фотки обратно и по метаданным Flickr добавить уже нормальные геотаги. Подробно это описал Sphinx в заметке Геотеги Flickr в EXIF. Он же написал скрипт, который этот процесс автоматизирует.

Дополнение: Второй способ. Как верно заметил в комментариях massalim, можно добавить геотег просто позиционируя фотографию на карте прямо в digiKam (у меня в digiKam 0.9.3 с kipi-plugins 0.1.5 эта возможность есть). В меню выбираем Image → Geolocation → Edit coordinates… и затем просто находим нужное место на карте:

20080715

pdfgrep: поиск слов по PDF файлам

Дано: куча PDF файлов (разные статьи).
Найти: те из них, в которых упоминаются определённые ключевые слова.

Я привык grepать по текстовым файлам в поисках нужного слова или сочетания слов. Вот только grep шаблон файл.pdf ничего не находит. «Не беда,» — решил я и написал обвязку для grep, которая конвертирует PDF в простой текст с помощью pdftotext и ищёт уже в нём, а имя файла показывает исходное.

Получился у меня вот такой скрипт pdfgrep. Запускать можно, например, так:
$ pdfgrep --color=always -i '(navier[- ]){0,1}stokes' *.pdf
Конечно, стоило мне собраться написать об этом в блог и поискать в интернете — как оказалось, что я не первый, кто такой скрипт написал. Вот практически идентичные скрипты: pdfgrep-1 и pdfgrep-2… В общем, идея понятна.

Автоматическая компиляция документов LaTeX (с помощью SCons)

Сколько нужно команд, чтобы скомпилировать PDF из документа LaTeX? Как минимум две: pdflatex mypaper.tex; pdflatex mypaper.tex. А если при этом используется библиография в формате BiBTeX? Уже как минимум четыре: pdflatex mypaper.tex; bibtex mypaper; pdflatex mypaper.tex; pdflatex mypaper.tex. А бывает, что документ нужно пропустить через LaTeX более, чем два раза? Бывает. А сколько раз нужно всё это проделывать во время редактирования документа? Много. Удобно всё это? Если честно, не очень. Но жизнь себе упростить можно.

Берём и устанавливаем SCons. В каталоге с файлом LaTeX создаём файл SConstruct вот такого содержания:
PDF(target='mypaper.pdf',source='mypaper.tex')
Вот и всё! Теперь достаточно одной команды:
$ scons
— и, если что-либо в документе или библиографии изменилось, то scons выполнит все нужные команды самостоятельно, запустит bibtex, если он используется, запустит несколько раз pdflatex (если необходимость этого указывается в логе)…

А если запустить scons повторно? А ничего не будет:
$ scons
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.
scons проверяет MD5-суммы исходных файлов и ничего не делает, если файлы не изменялись.

А если я хочу, чтобы файл перекомпилировался, когда меняется одна из иллюстраций? Проще простого. Поскольку я держу все картинки в подкаталоге imgs/ в формате PDF, то достаточно просто добавить их в список исходных файлов. Например так (содержимое файла SConstruct):
src = [ 'mypaper.tex', Glob('imgs/*.pdf') ]
PDF(target = 'mypaper.pdf', source = src)
— функция Glob создаст список всех файлов, удовлетворяющие шаблону.

А если нужен не PDF, а PS? Или DVI? Соответственно и в SConstruct достаточно написать
PostScript(target='mypaper.ps',source='mypaper.tex')
или
DVI(target='mypaper.dvi',source='mypaper.tex')


Вообще говоря, SCons — может управлять компиляцией не только документов LaTeX. Это универсальный и гибкий инструмент сборки программ, аналог комбинации Make и autoconf/automake. Я о нём узнал совсем недавно, увидев, что он используется в проекте FEniCS — почитал мануал — и понравилось. Файлы SConstruct (аналог Makefile) являются полноценными скриптами на Python (описать простые правила сборки просто, а сложные — возможно); scons умеет автоматически отслеживать зависимости в C, C++ и Java; «из коробки» поддерживаются C, C++, D, Java, Fortran (разных версий), ObjectiveC, Yacc, Lex, Qt, SWIK, и TeX/LaTeX (можно написать собственные «конструкторы» для других языков/инструментов); может отслеживать изменения по MD5-суммам, а не только по дате изменения; SCons умеет брать нужные файлы из других каталогов или систем управления версиями (CVS, Subversion); изначально спроектирован так, чтобы делать кросс-платформенные сборки было просто (в том числе проверки в стиле autoconf требуют на порядок меньшей настойчивости в изучении документации); поддерживаются параллельная сборка и кэш компилятора (как ccache, только не только для C/C++). А для работы самого SCons достаточно почти любого, самого замшелого Python. Ну и вообще, удобно, что «мэйкфайл» для компиляции документа LaTeX — всего одна строчка ;-)

Да, компилировать документы LaTeX можно, конечно, и с помощью старого проверенного make. Вот, кстати, и неплохой Makefile для этого дела.

См. также:

Эта заметка по-английски: Auto-building LaTeX documents with SCons