Archive

Archive for the ‘UNIX’ Category

Восхитительные грабли.

April 28th, 2010 13 comments

Я недавно открыл для себя восхитительные грабли. Оказывается, что я ходил по ним годами, а наступил только вчера.

Допустим, нам нужно скачать какой-нибудь файлик из сети, посчитать его размер и сохранить в файл.

Код на perl будет примерно таким:


#!/usr/bin/perl -w
use strict;

if (system('wget -O- http://sveshnikov.ru/httpd.sh | wc -с > /tmp/size') != 0) {
die "OMG! Can't count bytes in httpd.sh file!!!";
}

В дальнейшем этот скрипт используется примерно так: if ./get_value; then работаем с /tmp/size; else сообщаем куда нужно; fi

Так вот, у этого скрипта хромает надежность. Почему? И как это сделать правильно? Жду ваших вариантов, мой – завтра :)

UPD:

Проблема заключается в том, что при использовании пайпов в $? окажется результат последней выполненной команды:

$ blablabla | wc; echo $?
zsh: command not found: blablabla
0 0 0
0

Я решил этот вопрос с помощью bash и его опции pipefail. Пример выше я бы переписал как-то так:


#!/usr/bin/perl -w
use strict;

sub my_system {
return system('bash', '-o', 'pipefail', '-c', @_);
}

unless (my_system('wget -O- http://sveshnikov.ru/httpd.sh | wc > /tmp/size') == 0) {
die "OMG! Can't count bytes in httpd.sh file!!!";
}

Теперь будут отлавливаться все ошибки.
В шелл-скриптах можно еще анализировать массив PIPESTATUS, в котором сохраняется exit code для всех команд ковейера, но мне показалось, что это неудобно.

Debian-пакеты с человеческим лицом на примере Zabbix 1.8

March 29th, 2010 No comments

Несколько задержавшийся кросс-пост с хабрахабра, в котором я описываю пример сборки debian-пакета с использованием CDBS.

Написать эту статью меня заставили две вещи: во-первых, вышел zabbix 1.8 – замечательной системы мониторинга, в которой, судя по новостям, наконец-то занялись проблемами юзабилити админского интерфейса. Во-вторых, появилось стойкое ощущение, что после статей “делаем debian-пакет на коленке”, большинство хабравчан утвердятся во мнении, что debian-пакеты придумали извращенцы для извращенцев.

Связывает два этих события то, что zabbix 1.8 пока нет в репозиториях убунты, и когда появится не понятно, а компилить и ставить из исходников что-то на продакшн-серверах, это, конечно, недостойное джентльмена занятие. В общем, появился хороший повод продемонстрировать, что debian-пакеты – это не страшно.

Итак, приступим:

apt-get install dh-make devscripts cdbs libmysqlclient-dev libcurl4-gnutls-dev
wget http://sunet.dl.sourceforge.net/project/zabbix/ZABBIX%20Latest%20Stable/1.8/
tar zxvf zabbix-1.8.1.tar.gz
cd zabbix-1.8.1
dh_make --createorig --cdbs

Между делом, если бы мы пакетировали не zabbix, а более простую программу, которая обычно ставится с помощью ./configure; make; make install, то на этом создание пакета заканчивается. Осталось бы только собрать пакет с помощью команды ar debuild.

Но мы продолжим: откроем файл debian/control и пропишем себя, любимого, в качестве создателя пакета, и укажем, какие пакеты должны приехать на сервер вместе с установкой zabbix. Для этого нужно найти строку ‘Depends’ и добавить в ее конец следующее: “fping, adduser, apache2, php5, php5-mysql, php5-gd”.

Обычно скрипт configure можно вызывать без параметров и он сгенерирует жизнеспособный конфиг, но для zabbix это не так – нужно отдельными опциями включать компиляцию серверной части и агента. Так как агент из репозитория ubuntu меня полностью устраивает, ограничимся сборкой только сервера. Для этого нужно дописать в конец файла debian/rules следующую строку:

DEB_CONFIGURE_USER_FLAGS := --enable-server --with-mysql --with-libcurl

Сборка пакета осуществляется следующим образом: создается временная директория (debian/zabbix), в которую копируется содержимое пакета. Затем эта директория упаковывается в архив, добавляются метаданные, и все – пакет готов. Это очень грубое, но пока достаточное описание процесса. Бинарные файлы попадут в директорию debian/zabbix/usr/bin сами собой (CDBS запустит команду make install DESTDIR=debian/zabbix), а файлы php-интерфейса нам нужно туда поместить вручную. Для этого перечислим их в файле debian/install вот каким образом:

frontends/php/* usr/share/zabbix/
misc/conf/zabbix_server.conf etc/zabbix

Кроме того, для нормальной работы заббиксу нужны еще и директории для лог-файлов и локов. Они тоже являются частью пакета, поэтому создать их нужно прямо в той временной директории. Стандартный способ создания пустых директорий – перечисление их в файле debian/dirs:

/var/log/zabbix-server
/var/run/zabbix-server

Теперь дело за инит-скриптом. В комплекте с заббиксом идет инит-сктипт (misc/init.d/debian/zabbix-server), но без обработки напильником он работать не будет. Поэтому лучше его заменить скриптом из убунты (http://bazaar.launchpad.net/~ubuntu-branches/ubuntu/karmic/zabbix/karmic/annotate/head%3A/debian/zabbix-server-mysql.zabbix-server.init), который нужно сохранить под именем debian/init.

Совсем не лишним было бы настроить logrotate:

cat > debian/logrotate
/var/log/zabbix-server/zabbix_server.log {
daily
rotate 7
compress
missingok
notifempty
create 0640 zabbix zabbix
sharedscripts
}
^D

Раз пакет мы собираем не для кого угодно, а только для себя, то почему бы и не положить конфиг апача рядом с заббикксом:

mkdir misc/apache2-vhosts
cat > misc/apache2-vhosts/zabbix

ServerName zabbix.example.com
ServerAdmin admin@example.com
DocumentRoot /usr/share/zabbix
CustomLog /var/log/apache2/zabbix_access.log combined
ErrorLog /var/log/apache2/zabbix_error_log

^D
echo "misc/apache2-vhosts/zabbix etc/apache2/sites-available" >> debian/install

Иногда недостаточно просто разложить файлики по файловой системе, чтобы настроить среду для исполнения программы. В таких случаях на помощь приходят preinst, postinst, prerm и postrm скрипты, которые выполняются уже на сервере, куда пакет устанавливается. Названия у них вполне понятные, пояcнить нужно только то, что ‘inst’, и ‘rm’ – это фактическая распаковка или удаление содежимого пакета (все той же временной директории debian/zabbix, в нашем случае), кроме того – каждый из этих скриптов может вызываться с разными аргументами в зависимости от ситуации. Нам интересен скрипт postinst, вызывающийся с аргументом configure – нужное место скрипта можно легко найти в шаблоне postinst.ex. Приступим:


cd debian
mv postinst.ex postinst
vim postinst

После строки configure) нужно вставить следующий код:

# Создадим пользователя
useradd zabbix || echo "User zabbix was not added"

# Установим права на рабочие директории
chown zabbix:zabbix /var/log/zabbix-server /var/run/zabbix-server

# В этой директории веб-интерфейс пытается сохранить конфиг-файл, дадим ему на это права:
chown www-data /usr/share/zabbix/conf
chmod 775 /usr/share/zabbix/conf

# Настроим автозапуск сервера:
update-rc.d zabbix-server defaults

# Включим новый virtualhost и перезапустим апач:
a2ensite zabbix
invoke-rc.d apache2 reload

;;

abort-upgrade|abort-remove|abort-deconfigure)

Осталась одна деталь: при стандартных настройках PHP интерфейс заббикса не заведется, нужно править max_execution_time и еще несколько параметров. Если бы мы готовили пакет для домашнего сервера-торрентокачалки, то, конечно, проще было бы поправить php.ini напрямую. Но идеологически более правильно (и это очевидно при коллективной разработке, когда роли админа и разработчика разделены) поместить эти настройки тоже в пакет. Сделать это можно так:


mkdir misc/php.conf
cat > misc/php.conf/zabbix.ini
post_max_size = 16M
max_execution_time = 300
mbstring.func_overload = 2
^D
echo "misc/php.conf/zabbix.ini etc/php5/conf.d" >> debian/install

На этом все, можно собрать пакет с помощью команды debuild, поставить его с помощью dpkg -i и apt-get install -f.

Categories: UNIX Tags: , , , ,

Магия в zsh

November 4th, 2008 1 comment

Захотелось мне в этот чудесный выходной понедельник поагитировать за свой любимый шелл. Чем, кстати, вообще может похвастаться какой-либо шелл, имея в конкурентах такого монстра, как bash? Ведь в bash есть все, что может только пожелать обычный пользователь. А, собственно, и нечем. Поэтому разработчикам zsh пришлось пойти на хитрость и добавить в свое детище некоторое количество магии :)

Большая ее порция досталась подсистеме автодополнения. Вот таким можно сделать дополнение имен файлов:
zsh menu completion
(выбрать нужный вариант можно табом по старинке, курсорными клавишами или стандартными emacs-хоткеями)

А вот так – дополнение опций:

Еще одна моя любимая магическая штуковина в автодополнении выглядит так: допустим, нужно подправить файл /usr/local/etc/rc.d/mysql. Пользователь bash бы ввел такую команду:
vim /usr/l[TAB]/e[TAB]/r[TAB]/m[TAB]
В zsh все несколько проще, а именно:
vim /u/l/e/r/m[TAB]
После нажатия [TAB] все элементы пути будут дополнены. Если вариантов несколько, можно будет вручную выбрать нужный.

Ну вот, я вам передал примерно 0.65 процента магии zsh. Помимо автодополнения, ее залежи находятся в подсистеме подстановок (man zshexpn), но там я сам не очень уверенно ориентируюсь. Все-таки одна из прелестей zsh заключается в том, что мне необязательно учиться им пользоваться – для начала можно ограничиться только bash-совместимым подмножеством функций, регулярно натыкаясь на приятные мелочи в реализации привычных вещей.

Дальнейшее чтение:
обзор zsh от Алексея Федорчука,
Собрание хаков и идиом zsh: zsh lovers.

Categories: SHELL, UNIX, tips Tags: , , ,

Пятничная загогулина (bash)

April 11th, 2008 15 comments

Ну вот, пришло время для очередной программерской загогулины. Следующий код работать не будет:

#!/usr/bin/bash
echo "one two three" | read a b c
echo "<$a> <$b> <$c>"

Что в нем не так?

Я ожидаю увидеть два ответа. Первый — это просто переписать этот код так, чтобы он заработал. И второй ответ — опционально — объяснение, почему не работает в таком виде.

p.s. второго ответа я пока сам не знаю, поэтому буду с интересом ждать его от вас :)
p.p.s. ответивший верно, как всегда – умничка (-ца (маловероятно)).

UPD: На второй вопрос ответ уже есть:
gds
есть мнение, что переменные, прочитанные read, остаются в переменных “экземпляра” баша, запущенного для выполнения “read a b c”, и не попадают в родительский баш, запустивший “echo … | read a b c”.

gds’у – респект!
А я понял, что в следующий раз надо постить задачки посложнее..

UPD2 свое решение выложу сегодня в конце рабочего дня
UPD3

Кажется, я нечаянно сорвал рабочий день моего начальника, но зато он мне прислал вот такое остроумное решение:
str=`echo "one two three" | (read a b c; echo "a=$a; b=$b; c=$c;") `
eval "$str";
echo "<$a> <$b> <$c>"

Петя, ты отжог :)

Но на самом деле все это можно сделать несколько проще – через “here doc”:
read a b c <<<`echo "one two three"`
echo "<$a> <$b> <$c>"

Заговор от битых кластеров (добавляем информацию для восстановления архивов с помощью par2)

April 10th, 2008 14 comments

В этой заметке я предложу способ чтения данных с поцарапанных, погрызанных собакой или обработанных шредером компакт-дисков.

Итак, к делу: регулярно, при записывании данных на диск, остается некоторое количество свободного места. Лет 5 назад можно было положить диск на полочку, пометив, что еще 30 мегабайт можно забить каким-нибудь хламом, но сейчас стоимость болванки – 10 рублей, такой аргумент не действует. И, соответственно, привычку оставлять место “на потом” надо искоренять. Мне кажется, наиболее разумным было бы добавить на диск данные для восстановления – т.н. корректирующие коды Рида-Соломона, которые могут пригодится в случае, если диск будет поврежден.

Собственно весь заговор выглядит следующим образом:
#sudo aptitude install par2
#man par2
#cd backups
#ls
dump.sql.gz
#par2 create -v -r10 -n1 -m500 dump.sql.gz
#ls
dump.sql.gz dump.sql.gz.par2 dump.sql.gz.vol000+100.par2

Эта команда добавит 10% избыточной информации к данным, запишет все это в один файл, при этом программе par2 разрешено использовать 500 мегабайт оперативной памяти. Файлы *.par2 следует записать на диск вместе с дампом.

Мне эта программа понравилась, я захотел ее проверить в боевых условиях. Сделал архив размером около 400 Мб, добавил данные для восстановления – еще 200Мб – *.par2 файлы (50% избыточность, по умолчанию – 5%). Все это я записал на CD-RW, в котором потом сделал, простите, дырку. В итоге стандартными средствами удавалось прочитать только первые 150 Кб данных. Чтобы восстановить файл нужно сначала все считать пускай с ошибками – для этого есть программа dd_rescue, которая является практчески полным аналогом dd с одним исключением – она умеет игнорировать ошибки чтения:

диск с дыркой

#sudo dd_rescue -Av -b 1048576 -B 1048576 /dev/scd0 brokencd.iso(-A – заполняь нулями те участки файла, которые считать не удалось, v – verbose, -b – размер блока данных, -B – размер блока данных для проблемных областей диска)
# sudo mount -o loop brokencd.iso mnt # монтируем получившийся образ диска
# cp mnt/* dump; cd dump # копируем содержимое
# par2 r archive.par2 # приводим в изначальный вид

На этом эксперимент завершился – все данные с диска восстановлены. Даже скучно.
Но пример, мне кажется, весьма красноречив :)

Categories: UNIX, tips Tags: , , , ,

SSH для бэкапов и мониторинга: ограничиваем доступ.

March 4th, 2008 1 comment
“Бэкап – акт проявления трусости” (c) народная мудрость

Я труслив :) Во-первых, я делаю бэкапы. Во-вторых, я их боюсь передать без шифрования и, в третьих, иногда и храню их зашифрованными.

Как правильно организовать передачу бэкапов с одного сервера на другой? Дампы этого блога у меня передаются по http. Они зашифрованны, да и особо ценных данных здесь нет, поэтому http меня не смущает. А как поступить с данными по-важнее? Конечно, ssh. А как при этом запретить пользователю выполнять какие-либо действия, кроме как копирования к себе бэкапа? Представим себе, что бэкап-сервер находится во вражеском дата-центре или просто вы не можете проконтролировать к нему доступ (в т.ч. физический). Нас выручит опция command файла authorized_keys.

Например, если необходимо предоставить доступ только к файлу backup.tgz, то в authorized_keys в самом начале соответствующей строки можно дописать следующее “command=’cat backup.tgz’”. Теперь при каждом коннекте будет автоматически выполняться команда cat backup.tgz, вам остается только перенаправить вывод в файл. Если дампов несколько, то можно написать небольшой скриптик вида:#!/bin/sh
read file
case "$file" in
"foo") cat foo.tgz ;;
"bar") cat bar.tgz ;;
esac
И прописать его в качестве команды по умолчанию. Да, не лишним было бы добавить опции no-port-forwarding, no-pty и все прочие no-*

Теперь копировать файл с удаленной машины можно вот такой командой:echo "foo" | ssh backup@server > foo-`date +%Y-%m-%d`.tgz

Кроме как для бэкапов, такое же решение может подойти и для мониторинга. Когда нагиос стучится на удаленный сервер, чтобы собрать какую-либо статистику, полный ssh-доступ ему не нужен.

По-моему, все, что я описал примитивно по сложности и вместе с тем весьма надежно. Так может быть прекратим без надобности дописывать authorized_keys на серверах? Одна запись для себя, любимого, одна для бэкапов, одна для мониторинга.. ой, еще каких-то две.. дальше продолжать? :)

Кстати, а есть ли способы более тонко настраивать уровень доступа к ssh?

Categories: SHELL, UNIX, tips Tags: , , ,

О прозрачности в командной строке [pv]

December 4th, 2007 4 comments

Эта запись будет посвящена программе с лаконичным названием pv. А слово “прозрачность” пусть не ассоциируется у вас с пресловутыми compiz-fusion, тут все гораздо интереснее :)

Итак, чем плох избитый пример c передачей файлов через netcat? Мне, например, не удобно то, что нет возможности наблюдать за процессом. В самом деле, возможности юниксовых шеллов в этом плане довольно скромные: с помощью tee можно сохранить поток в файл, который потом наблюдать через tail или следить за его размером командой ‘watch ls’. Этого достаточно для отладки, но мало для комфортной работы.

Поэтому программа pv просто не могла не появиться. Все очень в духе UNIX – маленькая утилитка, которая пропускает через себя поток данных и в STDERR отправляет некоторую статистику по ним. Но _как_ она это делает! Покажу на примерах:

Всегда хотел узнать:
dainichi@fujitsu:~$ cat /dev/toilet_paper | pv >/dev/shreder
1.01GB 0:00:02 [ 569MB/s] [ <=> ]

Ага, больше десяти рулонов в секунду..

Прикинемся взрослой программой с выводом а-ля wget:
dainichi@fujitsu:~$ cat file | pv -s `stat -c %s file` | nc so.far.away 1234
236kB 0:00:05 [ 346КB/s] [==================> ] 72%

Ограничим скорость обработки файла, чтобы mplayer не тормозил:
dainichi@fujitsu:~$ cat big_log | pv -L 100к | log_processor > report

Пример из мана – архивирование каталога и одновременное отображение прогрессбара
(tar cf - . | pv -n -s `du -sb . | awk '{print $1}'` \
| gzip -9 > out.tgz) 2>&1 | dialog --gauge 'Progress' 7 70

И, напоследок, скриншот с домашней страницы проекта:
скриншот с домашней странички проекта pv

Здорово? :) По-моему, да :)

Кстати. а какие еще способы “очеловечить” интерфейс командной строки вы знаете?

UPD Андрей Афанасенко поделился ссылкой вот на такое чудо:

Написана эта программа на bash и во многих случаях ее использовать гораздо удобнее, чем pv.

Categories: SHELL, UNIX, tips Tags: , ,

Загадка!

October 19th, 2007 9 comments

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

Итак, есть следующий код:

#!/bin/sh

get_size() {
if [ "`uname`" = "FreeBSD" ]; then
r=`stat -f %z $1`
else
r=`stat -c %s $1`
fi
return $r
}

if [ -z $1 ]; then
echo "usage: print_size ";
else
get_size $1;
echo $?
fi

Вот результат работы скрипта:
asveshnikov@fe08a023fb1cc27a:~/tmp$ ls -la
total 112
drwxr-xr-x 2 asveshnikov asveshnikov 4096 2007-10-19 15:38 .
drwxr-xr-x 85 asveshnikov asveshnikov 8192 2007-10-19 13:15 ..
-rw-r--r-- 1 asveshnikov asveshnikov 39781 2007-08-29 15:22 DSC02473.jpg
-rw-r--r-- 1 asveshnikov asveshnikov 49553 2007-08-31 17:41 DSC02481.jpg
-rwxr-xr-x 1 asveshnikov asveshnikov 231 2007-10-19 15:27 print_size.sh
asveshnikov@fe08a023fb1cc27a:~/tmp$ ./print_size.sh print_size.sh
231
asveshnikov@fe08a023fb1cc27a:~/tmp$ ./print_size.sh DSC02473.jpg
39781

Как видите, абсолютно рабочий код, проверялся в Linux и FreeBSD.
При этом он имеет просто ужасную.. нет, чудовищную проблему переносимости, которую при некоторых обстоятельствах крайне сложно обнаружить в скриптах несколько большего размера – какую?

p.s. угадавший первым будет умничкой :)

UPD:

Уже двое человек указали на то, что `uname` желательно взять еще и в кавычки. Вобщем, предлагаю не мелочиться, здесь проблема если и есть, то явно не “чудовищная”. Чтобы больше никого не смущать этим, подправил код.

Решено!

Михаил достаточно подробно описал проблему:

Если не брать в голову отсутствие кавычек вокруг $1, а также неполный способ определения размера файла (под солярой нет stat, например), то мне кажется основная проблема в возврате значения из функции. На самом деле таким образом можно вернуть только целое, причем очень ограниченное, так как по сути возвращается код завершения функции (если не изменяет память – максимум – 256). таким образом на больших файлах получим неверное значение.

Решением может служить использование для возврата глобальное переменной, либо через echo.

С моей стороны осталось лишь пояснить, почему проблема является “чудовищной” – она запросто может привести к порче данных, так как никакие сообщения об ошибках выводиться не будут. Ну и выглядит внешне этот код вполне надежным, так что отлавливать такой баг может быть задачей не из легких.

Собственно, основные проблемы возникают при портировании скрипта с sh на bash:

asveshnikov@fe08a023fb1cc27a:~/tmp$ ls -al DSC02473.jpg
-rw-r--r-- 1 asveshnikov asveshnikov 39781 2007-08-29 15:22 DSC02473.jpg
asveshnikov@fe08a023fb1cc27a:~/tmp$ bash print_size.sh DSC02473.jpg
101

Kubuntu 7.10 && Enlightenment 0.17

October 19th, 2007 2 comments

Смешанные чувства: нет эйфории, которая была с 7.04: просто прервал работу на полчаса, поставил новую систему, добавил с десяток нужных мне пакетов и продолжил работу.

И уже в процессе работы стал натыкаться на приятные мелочи: dolphin, который действительно мне показался шагом вперед в плане юзабельности, KNetworkManager, в котором появилось окошко статуса, strigi – служба полнотекстового поиска по локальным файлам, и еще опера, которая теперь доступна из ‘Canonical partner’ – репозитория.

Хм. Да вроде бы и все.

Куда больше интересного произошло на работе: нам выдали по второму монитору и видеокарте, и я пару часов потратил на приятнейшие хлопоты с подключением-настройкой всего этого добра. Выяснилось, правда, что по-нормальному со вторым монитором (в режиме xinerama) не работает ни одна из привычных мне систем: в KDE просто не получилось на него вывести никакой полезной информации, в enlightenment 0.16 обнаружился неприятный глюк – смазывался текст при манипуляциях с окнами. Наверняка все заработало бы в режиме TwinView или MergedFB, но у меня вторая видеокарта – старая ATI под слот PCI, поэтому другого варианта не было.

скриншот с офсайта
Искать счастья я стал в e17, и.. нашел!! Я в полном восторге от нее! Все, чем мне нравился e16 там осталось, но как плюс – красивые темы, полноценная панель управления, гениальнейший диалог быстрого запуска, плагины и сносная поддержка xinerarma.

Недочеты следующие: во-первых, никогда нельзя с уверенностью сказать, на каком мониторе откроется новое приложение, и во-вторых, виртуальные десктопы не общие, а свой набор для каждого монитора. Как оказалось, жизнь это не сильно портит.

Все равно, в итоге я от самых простых вещей теперь тащусь, как удав по стекловате!!

Categories: UNIX Tags: , ,

HTTP сервер в одну строку: версия 2.0

August 30th, 2007 49 comments

Идея с HTTP сервером на bash мне не дает покоя.

Вернуться к ней меня побудила.. попытка воспользоваться старой версией: получалсь не очень :)

Поэтому решил довести его до ума. Что-то дописал, что-то выкинул, полчилось следующее:
:;while [ $? -eq 0 ];do nc -vlp 8080 -c'(r=read;e=echo;$r a b c;z=$r;while [ ${#z} -gt 2 ];do $r z;done;f=`$e $b|sed 's/[^a-z0-9_.-]//gi'`;h="HTTP/1.0";o="$h 200 OK\r\n";c="Content";if [ -z $f ];then($e $o;ls|(while $r n;do if [ -f "$n" ]; then $e "<a href=\"/$n\">`ls -gh $n`</a><br>";fi;done););elif [ -f $f ];then $e "$o$c-Type: `file -ib $f`\n$c-Length: `stat -c%s $f`";$e;cat $f;else $e -e "$h 404 Not Found\n\n404\n";fi)';done

Теперь по URL http://ваш_ip:8080/ можно получить доступ ко всем файлам, находящимся в текущим каталоге. Очевидных и прямолинейных способов сменить каталог нет. Как и раньше, протестировано и работает под Linux, bash 3.2.13, и с хоббитовским netcat v1.10 с поддержкой опции -с (запустите netcat -h и посмотрите. Как минимум в Ubuntu, Debian и Fedora Core такая опция есть).

Теперь строка занимает 434 байт, что на 212 больше, чем прошлая версия. И мне кажется, что я с толком потратил эти байты: новый сервер обрабатывает ошибку 404, кроме того, теперь он работает без задержки, которая раньше требовалась для того, чтобы была возможность его остановить с помощью Ctrl-C. Для успешных запросов сервер отдает нормальные заголовки, с размером файла и даже с его MIME-типом. Например:

dainichi@fujitsu:~/backup$ echo "GET /6.1-RELEASE-i386-disc1.iso HTTP/1.1" | nc localhost 8080 | head -n3
HTTP/1.x 200 OK
Content-Type: application/x-iso9660
Content-Length: 529784832

Для удобства можно сохранить его в файл и написать в .bash_profile примерно следующее:alias share='sh ~/bash_httpd.sh'

Да, были попытки его еще уменьшить, но они не увенчались успехом. Есть такой вариант, но он даже больше:
w=while;d=done;e=echo;r=read;echo "true; $w [ @? -eq 0 ];do nc -vlp 8080 -c'($r a b c;f={$e @b|sed 's/[^a-z0-9_.-]//gi'{;h=}HTTP/1.x};o=}@h 200 OK#};c=}Content};if [ -z @f ];then($e @o;ls|(while $r n;do $e }@n
};done));elif [ -f @f ];then $e }@o@c-Type: {file -ib @f{#@c-Length: {stat -c%s @f{#};cat @f;else $e }@h 404 Not Found##404#};fi)';done"|tr "@{}#" '$`"
'|sh
Оставил его, потому что выглядит страшно, детей пугать – самое то :)

Если у вас получится сэкономить еще пару байтов, то милости просим в комментаторскую :)

UPD: 03.09.2007: Код обновлен, в него вошли исправления, которые сделал тов. jetxee, за которые ему большое спасибо.

UPD: 03.09.2007: Я удивлен тому факту, что эта заметка попала в топ news2, спасибо dik’у за то, что добавил ее. Я рад, что она многим показалась интересной.

UPD: 07.09.2007: Товарищ Ed очень здорово прооптимизировал код. Вот, что у него вышло:
while :;do nc -p8080 -vnlc'r=read;e="echo -e";$r a b c;while [ -n "`$e $a|tr -d "\r\n"`" ];do $r a;done;f=`$e $b|sed s/.//`;h="HTTP/1.0";z="404 Not Found\n";[ -z $f ]&&(ls|while $r n;do [ -f $n ]&&$e "$n";done)||([ -f $f ]&&($e "$h 200 OK\r\nContent-Type: `file -ib $f`\n";cat $f)||$e "$h $z\n$z")';done

Работоспособность проверялась автором на таблетке Nokia N800, а у меня в Kubuntu 7.04 что-то не заработало. Сегодня вечером буду разбираться :)

p.s. Мне теперь стыдно из-за того, что сам не догадался использовать && и || вместо if..then. :(

p.p.s. stay tuned!

UPD 18.08.2008: О, написали про это безобразие на Хабре, назвали меня извращенцем. Хулиганьё!! :)