Archive

Archive for the ‘загогулины’ 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 для всех команд ковейера, но мне показалось, что это неудобно.

Пятничная загогулина (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>"

Загадка!

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

Программерские загогулины [2]

August 23rd, 2007 8 comments

Очередная программерская загогулина, на этот раз решение сразу не дам :)

Допустим, надо выполнить какую-либо команду на нескольких хостах, перечисленных в файле. Пишу:

cat file | (
while read host; do
ssh user@$host "command"
done
)

Ошибка, однако! В чем?
Кто сможет разобраться в уме, не запуская код, тому респект и уважуха :)

UPD:

Решил человек, скрывающийся под ником gds:

поток из “cat file” будет выкушан только первой запущенной командой “ssh …”?

gds, респект тебе уважуха :)

codepage horror

June 29th, 2007 1 comment

Иногда мне снятся сны. Бывает, что кошмары. Что такое кошмар для админа? Это когда видишь свой сайт – все работает, все грузится, но кодирЛЮХВ КВЯНЕК ЗИЕОЕИВ!!!

На работе сегодня этот кошмар воплотился: вселился бес в базу и попутал все кодировки. Попутал на славу, так что первый дамп пришлось расшифровать в два приема: utf8->koi и cp1251->koi, а со вторым развлечение было поизощреннее. С каждой перестановкой буквы прыгали издевательски с места на место, подмигивали и смеялись, доводя мой ЙЛЗХмоск до кипения. Но битву они все-так проиграли. После пятой перекодировки cp1251->koi8r из поганцев сгинул злой дух и оборотились они текстом складным и залился дамп в базу и вернулись: юзерам – счастье, а админу – крепкий сон :)

p.s.

А на #freebsd народ показал такой фокус:


#str="трали-вали"; for((i=0; $i<20; i++)); do str=`echo "$str" | iconv -f CP1251 -t KOI8-R`; done;
#echo $str
трали-вали

return 0;

June 27th, 2007 No comments

Я вернулся :)
За время учебного отпуска нагулял отличное рабочее настроение!

В первый рабочий день обратил внимание на небольшой юбилей: 1 год и 4 дня исполнилось моему процессу screen, запущенному на работе, которым я активно пользуюсь по 40 часов в неделю. Слава героям!!

Попутно веду свою маленькую коллекцию программерских загогулин, вот очередной экземпляр:


#!/usr/bin/perl
$var1 = "one|two|three";
$var2 = join('|', split ('|', $var1));

Опрос знакомых программистов показал, что предполагаемым значением $var2 является исходная строка ($var1). PERL думает иначе.

Вся мощь перла с его реально перегруженными функциями именно в этом моменте дала слабину: split в качестве первого аргумента принимает _только_ регулярное выражение, и, соответственно, символ ‘|’ воспринимается как логическое “или”.. Таким образом, в var2 будет “o|n|e||t|w|o”…

Пусть такое поведение и является документированным, меня оно все равно расстраивает..