LINUX.ORG.RU

[perl][bash] передача аргументов

 ,


0

1

В программе на Perl забирается html-код страницы - и записывается в файл.
Плюс, есть еще bash-скрипт, который парсит html-файл. Делает он это через cat $1 и тд.
Из Perl скрипта вызывается $result = system('parse.sh «file.html»');

Все работает отлично для 1 файла. Но если дело доходит до оооочень большого кол-ва файлов, все работает в цикле - то процесс получается очень медленным, где-то 2 секунды на 1 файл.

Код страницы я забираю через WWW::Mechanize. Затем могу сделать $content = $mech->content();. В результате весь код страницы будет в переменной $content.
Как теперь вызвать bash-скрипт из Perl'овского и передать ему html-код?
Пробовал делать system('parse.sh «$content»');, а в parse.sh менять cat на echo «$1», не работает.

★★★★★
Ответ на: комментарий от sdio

Согласен, но мне было проще парсить html используя grep и sed. Если нельзя сделать так, как хочу, то, видимо, надо будет парсить все на перле

kovrik ★★★★★
() автор топика

system('parse.sh «$content»')

Ээ... А должно? Если я не ошибаюсь, этот код вызывает шелл и говорит: обработай с помощью parse.sh переменную _шелла_ $content, а не твой $content. Каким образом оно интерполируется в одинарных кавычках-то?

Если непременно хочется именно так, то:

system «parse.sh», $content;

Это передаст $content (твой $content из твоего перловского кода) parse.sh в качестве аргумента, строкой.

Правда, тут есть некоторый риск: если в $content затесается какой-нибудь & rm -rf /, то будет ой.

Скачай бесплатно без смс книжку с ламой и почитай гл. 16

Hoodoo ★★★★★
()

А каков сакральный смысл этих манипуляций? Надо что-то выкусить из определённых тегов или что? Может библиотека на CPAN есть для этого?

Hoodoo ★★★★★
()
Ответ на: комментарий от Hoodoo

Там проблема в том, что код страницы кривой и нормально пробежаться по тегам и найти то, что надо - не получится.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от Hoodoo

Я так понимаю, что не работает из-за того, что встречаются левые символы. Какие символы нужно escape'ить в echo?

kovrik ★★★★★
() автор топика

Странно использовать grep, sed и.т.п. когда все равно используешь perl ;)

Если конечно очень надо, то вот решение твоей проблемы:

use IPC::Open2;
open2($out, $in, 'superscript.sh');

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

P.S. И конечно в общем случае парсинг html(xhtml) регэкспами это зло, лучше для этого использовать специализированные html(xml) парсеры, metacpan.org в помощь.

bjorn
()
Ответ на: комментарий от kovrik

http://search.cpan.org/~gaas/HTML-Parser-3.69/Parser.pm

Разработчики утверждают, что умеренно кривой HTML этот модуль может распарсить. Вопрос - насколько кривой данный конкретный HTML.

Я им не пользовался, но например XML::Parser мне прочитал «битый» XML (довольно значительную часть), когда я его вызвал с опцией «игнорировать ошибки».

Hoodoo ★★★★★
()
Ответ на: комментарий от kovrik

Я так понимаю, что

system('parse.sh "$content"');

не должен работать, потому что parse.sh получает в качестве аргумента не переменную $content, а буквально строку «$content», потому что >'parse.sh «Scontent»'< одинарные кавычки же.

По идее

system "parse.sh", $content;

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

Hoodoo ★★★★★
()
Ответ на: комментарий от bjorn

Что-то не работает у меня это, возвращает GLOB'ы.
Эх, видимо, надо все-таки прямо на Перле парсить html

kovrik ★★★★★
() автор топика
Ответ на: комментарий от Hoodoo

open2 и open3 это стандартные функции perl'a, так что я не сказал бы что это «специальная» библиотека ;) Хотя конечно, то что они лежат в отдельном пакете, позволяет их так называть.

bjorn
()
Ответ на: комментарий от bjorn
$content = $mech->content();
open2($out, $content, 'parse.sh');

Скрипт parse.sh всегда возвращает 1 строку. Как теперь ее получить? print «$out\n»; глобы и возвращает.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от bjorn

Виноват, невнимательно прочитал. Увидел use, дальше не стал смотреть.

Но я имел в виду ещё всякие Devel::System, Exception::System и прочие.

Hoodoo ★★★★★
()

> В программе на Perl забирается html-код страницы
> bash-скрипт […] парсит html-файл

о___О

давай сюда свой parse.sh…

arsi ★★★★★
()
Ответ на: комментарий от arsi

Что-то вроде такого (здесь читает из файла - cat, а хотелось бы передавать весь код из перла в качестве аргумента, правда там, видимо, надо будет экранировать символы):

echo -n `cat $1 | grep -v "title" | grep -A1 "Name" | tail -1 | sed 's/.*<b>//' | sed 's/<\/b>.*//'`;
Код страницы меняться не будет.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

проверь на своей страничке:

#!/usr/bin/perl

use strict;

sub find_name {
    local $_;
    while ($_ = shift) {
        next if /title/;
        last if /Name/;
    }
    $_ = shift or return undef;
    s:.*<b>::;
    s:</b>.*::;
    return $_;
}

open my $fh, '<', 'test.html' or die $!;

my $name = find_name <$fh>;

print "Name: $name\n";
arsi ★★★★★
()
Ответ на: комментарий от arsi

Ну эта команда была не единственная. Там еще несколько примерно таких же. Как сделать их? Например, как заменить grep -A3 или grep -B3 ?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

> Например, как заменить grep -A3 или grep -B3 ?

допустим, есть массив строк:

my @lines;

получаешь номера строк, удовлетворяющих регекспу:

my @match = grep { $lines[$_] =~ /regexp/ } 0..$#lines;
my $i = $match[0]; # первое совпадение

дальше:

print $lines[$i-2];   # на три строчки назад до совпадения
print $lines[$i+2];   # третья строчка после совпадения
print @lines[$i-2..$i]; # две строчки до совпадения + совпадение
print @lines[$i..$i+2]; # совпадение + ещё две строчки

ну, суть ты уловил, надеюсь :)

ну и по такой схеме предыдущий код можно написать так:

#!/usr/bin/perl

use strict;

open my $fh, '<', 'test.html' or die $!;
my @lines = grep { !/title/ } <$fh>;
my ($i) = grep { $lines[$_] =~ /Name/ } 0..$#lines;
local $_ = $lines[$i+1];
print "Name: $1\n" if m:<b>(.*?)</b>:;
arsi ★★★★★
()
Ответ на: комментарий от arsi

А может, сделать next'ов для всех слов, которые он ищет, и упаковать результаты поиска в хэш, а потом оттуда достать, отформатировать и вывести?

Hoodoo ★★★★★
()
Ответ на: комментарий от Hoodoo

А может запихать все в одну строку и раскопать многострочными регэкспами? Чем-то таким для примера ТС.

unless ( /title/ ){
    if (m{Name.*\n(?<match1>.*)}m){
        $+{match1} =~ m{<b>(?<found>.*)</b>};
        say $+{match1}, $+{found};
        }
}

Ай! Не бейте ногами, я только начал в нем ковыряться.

IceAlchemist
()
Ответ на: комментарий от kovrik

значит, перед этой строкой была строка с текстом «Name»…

ну да, ты же последнее совпадение искал… тогда так:

sub find_name {
    local $_;
    my $t;
    while ($_ = shift) {
        next if /title/;
        $t = $_ = shift and redo if /Name/;
    }
    $t =~ s:.*<b>::;
    $t =~ s:</b>.*::;
    return $t;
}

ну или так:

#!/usr/bin/perl

use strict;

open my $fh, '<', 'test.html' or die $!;
local $/;
my $body = <$fh>;

my @lines = grep { !/title/ } split /\n/, $body;
my @i = grep { $lines[$_] =~ /Name/ } 0..$#lines;
print "Name: $1\n" if $lines[$i[-1]+1] =~ m:<b>(.*?)</b>:;

arsi ★★★★★
()
Ответ на: комментарий от kovrik

Что-то s2p простенькую строчку на sed перевел в 121 строку жуткого perl кода...

покажи эту строчку на sed

sdio ★★★★★
()
Ответ на: комментарий от sdio

Я уж и не помню какую брал. Что-то вроде:

sed 's/.*<b>//'

kovrik ★★★★★
() автор топика

А что надо вообще-то?

Я практикуюсь сейчас делать всякую фигню на Perl, могу в качестве упражнения сделать твой скрипт.

Hoodoo ★★★★★
()
Ответ на: комментарий от Hoodoo

Ну тут 2 варианта:
1. Сделать так, чтобы можно было передавать html-код страницы(там он идет в bash'евское echo, а там мешают многие символы - видимо, надо экранировать) в качестве аргумента скрипту, выполняемому через system();.
2. Кучу команд, состоящих из grep'ов, sed'ов, head'ов и tail'ов перевести на Перл

kovrik ★★★★★
() автор топика

Печатать перлом на stdout, читать bash-ем с stdin? А вообще тут правильно сказали, лучше уж парсить сразу перлом.

undertaker ★★
()
Ответ на: комментарий от undertaker

А можно простенький примерчик?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от undertaker

Фактически, вся задача сводится к следующему: передать html-код страницы в bash-скрипт из перловского скрипта и получить результат.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

Вообще говоря, скинь мне на почту пару подопытных страниц с указанием, что из них выцепить (не регэкспы, а можно даже тупо картинку).

Эта задача хорошо ложится на упражнения, которые мне всё равно надо делать, так что если у меня получится её решить, то мне это будет полезно, а тебе в таком случае достанется скрипт.

Если же не получится, то придётся мне сидеть в уголке и плакать.

Почта в профайле, заниматься буду после десяти по Москве.

Hoodoo ★★★★★
()
Ответ на: комментарий от Hoodoo

А там проблема даже не в перле, а в bash'евском echo. Там надо экранировать кучу символов.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от sdio

А еще такой вопрос:
допустим, для ускорения я сделаю N скриптов, каждый будет забирать свою часть страниц. Если я в Перловском скрипте вызываю bash-скрипт system('parse.sh «resultN.html» >> result '), и запущу сразу все N скриптов, то смогут ли они писать в файл result одновременно? Или разделить по файлам?

kovrik ★★★★★
() автор топика
Ответ на: комментарий от kovrik

1) Аааааа, тебе надо через аргумент, я то думал на STDIN надо кидать, через аргумент можно так:

my $res;
open OUT, '-|', './test.sh', $arg;
{
        local $/;
        $res = <OUT>;
}
print $res, "\n";

bjorn
()
Ответ на: комментарий от kovrik

Вообще говоря смогут, если flock не ставить. НО ЭТО АД! ;) Не делай так пожалуйста, скажи конкретно что тебе надо, а то какой то цирковой набор костылей получается для явно тривиальной задачи.

bjorn
()
Ответ на: комментарий от bjorn

Я и не стал так делать, я просто N скриптов запускаю, каждый пишет в свой файл.

kovrik ★★★★★
() автор топика
Ответ на: комментарий от arsi

Запустил 10 экземпляров скрипта. Посмотрел - где-то, видимо, утечка памяти. Все спарсить не успеют - память кончится. Помогите найти утечку. Код выглядит как-то так:

for ($page = 1; $page <= 14770; $page++) {
        undef($url);
        undef($stream);
        $url = "https://...$page";
        $mech->get($url);
        $mech->form_name("auth_form");
        $mech->set_fields(Email => $EMAIL, Passwd => $PASSWD);
        $mech->click();
        $mech->get($url);
        $stream = HTML::TokeParser->new(\$mech->{content}) || die "Can't open: $!";

        undef(@hrefs);
        # get all hrefs
        while (my $token = $stream->get_tag("a")) {
                if ($token->[1]{id} eq 'ahref') {
                        push(@hrefs, $token->[1]{href});
                }
        }
        foreach (@hrefs) {
                $mech->get($_);
                $content = $mech->content;
                open my $fh, '>:utf8', "result.html" or die "$file: $!";
                print $fh $content;
                system('./parse.sh "result.html" >> result');
                close($fh);
                undef($content);
        }
}
За 2 часа потребление памяти (судя по top'у) увеличилось на 2.4% - с 0.4 до 2.8% (для каждого запущенного скрипта).

kovrik ★★★★★
() автор топика

Пробовал делать system('parse.sh «$content»');,

а с чего ты вообще взял, что у тебя содержимое $content передастся в $1 ? Собственно передастся как раз строка «$content», а не содержимое этой переменной.

а вообще делай открывай пайп на 'bash parse.sh' (без аргументов) и пиши в него все, что надо распарсить.

и поменяй cat $1 на cat «$@» хотя бы.

xydo ★★
()
Ответ на: комментарий от kovrik

Едрен батон, что это? ;) Зачем все эти undef? У тебя в перле my выпилили? Почему у тебя файл закрывается после system? Сколько вообще этих hrefs? Каждый system делат форк, так что может у тебя .sh скрипты текут или их слишком много, посмотри htop'om дерево процессов.

bjorn
()
Ответ на: комментарий от bjorn

undef уже от безысходности навставлял)

kovrik ★★★★★
() автор топика
Ответ на: комментарий от bjorn

А что делать с форками от ssytem'а?

kovrik ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.