Восхитительные грабли.
Я недавно открыл для себя восхитительные грабли. Оказывается, что я ходил по ним годами, а наступил только вчера.
Допустим, нам нужно скачать какой-нибудь файлик из сети, посчитать его размер и сохранить в файл.
Код на 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 для всех команд ковейера, но мне показалось, что это неудобно.


Потому что wget есть далеко не везде, и нужно использовать православный curl? кстати опция -O- мне ни о чем не говорит. )
ixpict, нет, вы просто про неработоспособность говорите :)
Представьте, что это фрагмент скрипта, который в зависимости от курса доллара меняет цены на товары в интернет-магазине. Этот скрипт написали, протестировали и выложили на сервер. Он работает несколько месяцев нормально, а однажды раз – и делает все товары в магазине бесплатными. Вот это хромающая надежность :)
(кстати, опция -O говорит wget’у, куда сохранять файл. -O- – это значит не сохранять, а просто вывести в STDOUT)
Я бы добавил ключик -q к вызову wget.
Наверное вы о том, что проверяется код возврата wc а не wget’а?
Roman, ага. И, по-моему, есть тонны кода, в котором это не учитывается.
antage, а я бы в реальном приложении добавил флажок -nv, а результат писал в лог :)
а если такой вариант ?
#!/usr/bin/perl -w
use strict;
use IPC::Open2;
open(FH,’>’,'/tmp/size’) or die “Can’t open /tmp/size: $!\n”;
my $pid1 = open2(“>&FH”, \*PH, ‘wc’,'-c’);
close(FH);
my $pid2 = open2(“>&PH”, \*STDIN, ‘wget’,'-O-’,’http://sveshnikov.ru/httpd.sh‘);
close(PH);
waitpid($pid2,0);
my $ret2 = $? >> 8;
waitpid($pid1,0);
my $ret1 = $? >> 8;
print “wc status => $ret1\n”;
print “wget status => $ret2\n”;
кстати в этом варианте нет никакого shell-а вообще, чистый perl :)
grubber, это сурово :) Ты всегда так пишешь? Мне лень :)
Кстати, есть IPC::Run, с ним это же можно сделать в два раза короче, но все равно многословно.
~$ tclsh
% set failed [catch {exec -- wget -O - http://does.not.exist/httpd.sh | wc -l >/tmp/size} err]
1
% if {$failed} { puts stderr “OMFG, FAIL:\n$err” } else { puts “it works” }
OMFG, FAIL:
–2010-04-30 19:40:38– http://does.not.exist/httpd.sh
Resolving does.not.exist… failed: Name or service not known.
wget: unable to resolve host address `does.not.exist’
~$
“Ну и что же!” — скажут любители перла — “Зато в перле есть оператор `yada-yada’!” :-)
Раз пошла такая пьянка, то:
К тому же, в перле есть оператор yada-yada :)
насколько помню IPC::Run нет в стандартной поставке
Что совсем не мешает его использовать :)
В ubuntu это libipc-run-perl, в других дистрибутивах уверен, что тоже есть.