LINUX.ORG.RU

а не изобретаю ли я велосипед, или об одной полезной утилитке

 , ,


0

3

TL;DR: вот почти накарябал прямо в посте простую утилитку сначала на перле, а потом, если не лень будет, и накарябую на с++, но сдается мне, что она уже есть и как-то называется (прошлый раз тут на лор-е мне даже код запостили, делающий примерно то же, что gnu xargs)

_______________________________________________________

предположим, что есть несколько worker-ов, которые складывают описание достигнутых ими результаты в несколько текстовых файлов — чтобы было предметнее, предположим, что воркеры тянут странички из интернета, а в текстовые файлы последовательно кладут url-ки которые им удалось вытянуть, и которые не удалось вытянуть

утилитка должна читать STDIN и печатать в STDOUT те строки из STDIN, которые не встречаются в текстовых файлах (т.к. эти строки, являющиеся url-ками, уже успешно вытянуты из интернета); таким образов в STDOUT не будут попадать url-ки, которые из интернета уже не надо вытягивать

идея «вместо этого воркеры должны просто проверять наличие файла со страницей из инета» не подходит (долго объяснять почему; между воркерами могут быть гонки на предмет создания файла — но это не основная причина)

так как файлы, заданные в командной строке, растут (но не модифицируются — только append), то утилитка должна периодически дочитывать их, начиная с позиции, в которой она была прошлый раз

можно, но не обязательно, дочитывать эти файлы после каждой строки из STDIN а можно скажем каждую секунду — небольшое дублирование работы воркеров не страшно (а как же гонки? ... написать что ли длинное объяснение? ... короче воркеры могут быть разные — скажем одни воркеры резольвят днс, а вторые фетчат страницы, и вот гонки между резольверам и фетчерами не страшны, но гонки *внутри* фетчеров страшны, т.к. потенциально в один файл могут писать 2 фетчера, и сломают его);

так что вопросы:

1. может такое уже есть? как оно называется?

2. по перлу приходит в голову такая идея и возникает вопрос:

#!/usr/bin/perl -W

use strict;

open FA, "<a.txt" or die;
open FB, "<b.txt" or die;

my $a;
my $b;
my %line_is_already_seen = ();

while( defined($a=<FA>) )
{
  $line_is_already_seen{$a}=1;
  print STDERR "$a is already seen\n";
}

while( defined($b=<FB>) )
{
  $line_is_already_seen{$b}=1;
  print STDERR "$b is already seen\n";
}

while(<STDIN>)
{
  if( defined($a=<FA>) )
  {
    $line_is_already_seen{$a}=1;
    print STDERR "$a is already seen\n";
  }
  if( defined($b=<FB>) )
  {
    $line_is_already_seen{$b}=1;
    print STDERR "$b is already seen\n";
  }
  if( exists($line_is_already_seen{$_}) )
  {
    print STDERR "skipping:$_ because it is already seen\n";
  }
  else
  {
    print STDERR "processing:$_\n";
    $line_is_already_seen{$_}=1;
    print $_;
  }
}

close FB;
close FA;

вопрос: файлы FA и FB пишутся блоками (sync после каждой строки *не* производится), и поэтому последняя строка в них может оказаться неполностью записанной; я правильно понимаю, что покуда ньюлайн в FA не будет записан, $a будет undef, и значит этот код правильный? (или надо заморачиваться со сборкой $a из кусочков? вроде же без этого обходимся)

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

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

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

да, оно примерно такое простое, но (если не ошибаюсь) сделал ты неправильно — на файлах нужно *висеть*, т.е. закрываются они только после конца STDIN (смотри как в моем коде)

т.е. мы допустим запускаем:

( generate_urls | xcmp a.txt b.txt | download_urls) &

и *параллельно* этому файлы a.txt и b.txt растут, их аппендит скрипт download_urls, в этом смысл

echo foo.example.com >> a.txt
echo bar.example.com >> b.txt

счетчики непонятно зачем — достаточно бинарной логики 0 и 1 (но вообще-то вместо счетчика может быть дата последнего обновления, но не буду запутывать дальше)

но это мелочи; главное — неужели никто такого не написал?

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от gh0stwizard

похоже, единственным способом донести мою мысль будет самому дописать 5 строк к моему коду

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

т.е. тебе надо вот этот самый xcmp? и он должен читать эти два файла и стдин, строить по ним хеш и выводить в download_urls только то что не встречалось ни в одном из вводов еще? тогда я правильно написал, только "-" еще добавь в тейл, чтобы он стдин читал.

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

tail -qf urls1 urls2 | awk '!a[$0]++' или я неправильно понял задачу?

все же оказывается нет, не так, зря я подумал о своей тупости

дело в том, что строки из urls1 urls2 не должны выводится *вообще* ни разу, а строки из stdin должны:

0. не выводится, если они присутствуют в urls1 или urls2

1. выводится ровно по одному разу если они не присутствуют и в urls1 и в urls2

т.е. stdin и urls не симметричны

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от val-amart

выводить в download_urls только то что не встречалось ни в одном из вводов еще

нет, немного сложнее

я обновил свой скрипт в топике

вроде бы через tail и awk как ты написал это не сделать, но я еще подумаю (хотя перловый скрипт в топике уже все что надо делает, кмк)

если народ пускает worker-ы, неужели нет такой готовой проги, а я о ней просто не знаю? (типа как start-stop-daemon в дебиане — незнающему человеку трудно догадаться, что он есть)

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от www_linux_org_ru

Какая-то ерунда получается. Допустим, я сделал вариант, который дочитывает файлы a.txt, b.txt. После первого же прогона, выплевывается все что дал generate_urls. И далее есть два варинта:

1. generate_urls - хитрожопая программа, которая аппендит в свой STDOUT новые урлы. О чем ты умолчал.

2. xcmp выплевывает в stdout повторами через 1 секунду то, что было запихнуто изначально до тех пор пока в файлах a.txt, b.txt не появится то, что должно быть, т.е. выхлоп generate_urls.

Иначе, смысла держать открытыми файлы a.txt, b.txt нет. Проще завершиться, сохранить позиции. А при новом запуске считать их.

gh0stwizard ★★★★★ ()
Последнее исправление: gh0stwizard (всего исправлений: 1)
Ответ на: комментарий от gh0stwizard

generate_urls - хитрожопая программа, которая аппендит в свой STDOUT новые урлы. О чем ты умолчал.

download_urls аппендит в файлы a.txt и b.txt новые урл-ы (точнее, аппендит не она сама, а воркеры, которые она запускает, но это не существенно)

мне всегда кажется, что собеседник понимает все гораздо глубже, чем на самом деле — это такая моя проблема

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

Что должен ожидать xcmp читая постоянно файлы a.txt и b.txt? Вот xcmp запустился, сделал сравнение выплюнюл разницу, а дальше что? Либо он продолжает плеваться до тех пор пока не останется никакой разницы, либо что-то должно еще происходить.

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

Что должен ожидать xcmp читая постоянно файлы a.txt и b.txt?

ожидать новых записей из STDIN конечно же

скрипт в посте (я его обновил) только ARGV не использует, а так (вроде) меня устраивает

если у меня не получается тебе объяснить, то забей

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от val-amart

если бы у tail был режим, в котором каждая строка выводилась с префиксом, т.е. например так:

user@host: echo 888 | xtail -n+1 -qf a.txt b.txt -
a.txt: foo
a.txt: bar
b.txt: bazbaz
b.txt: mmm
-: 888
user@host:

то можно было бы действительно написать элегантнее, на этом xtail и awk — логика работы awk должна быть разная в зависимости от того, откуда (a.txt, b.txt, или stdin) пришла строка

либо если бы awk умела висеть на файлах как tail -f и проходить свой список аргументов не один раз, а бесконечно много

без одного их этих, похоже, никак

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

Это было не очевидно.

даже самая первая версия кода в моем посте вела себя именно так; хотя, м.б., неочевидно было, что она вела себя правильно :-)

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

Другая версия: http://pastebin.com/Dmj1vHws Запуск:

cat infile | perl xcmp.pl file1 file2

Вообще, таска весьма-весьма неопределенная. Вот в этой версии упор сделан на то, чтобы не перечитывать с начала файлы file1 и file2. Поэтому их содержание кэшируется в ОЗУ. И если infile не будет выплевывать теже строки что в file1, file2, тогда программа будет жрать дофига памяти. Проще говоря, «нулевое» потребление ОЗУ будет достигнуто, когда infile, file1 и file2 будут иметь одинаковые строки.

На диск в файл positions.txt сбрасывается последний считанные позиции файлов.

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

если бы у tail был режим, в котором каждая строка выводилась с префиксом

так это же дефолтный режим =)

вот так сойдет?

tail -f - 1.txt 2.txt | awk '/^==> standard input <==$/ {u=1; next} /^==> [0-9]\.txt <==$/ {u=0; next} /^$/ {next} !a[$0]++ && u'

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

а вообще хочу тебе сказать что это грязь и только для one-off jobs. в нормальных приложениях используются message queues для таких целей. а еще всяких краулеров миллион, наверняка можно использовать уже готовый.

val-amart ★★★★★ ()
Последнее исправление: val-amart (всего исправлений: 1)
Ответ на: комментарий от val-amart

так это же дефолтный режим =)

я *принципиально* никогда не закладывался на те строки, которые могут быть перепутаны с данными ( хотя, конечно, url-ки не могут иметь вид «==> standard input <==» )

а вот если бы в гипотетическом xtail название файла всегда шло префиксом строки, то его можно было бы однозначно из строки выделить (приняв, скажем, что в именах файлов нет табуляций — разбирать до первой табуляции; а если нужна 100% секьюрность — то опция -0, где сначала идет имя файла с терминирующим \0, затем строка)

awk вроде работает вот так, но мое эстетическое чувство протестует против закладки на стрелочки:

#!/bin/sh

tail -n+1 -f a.txt b.txt - | awk '

/^==> standard input <==$/ { stdin=1; next;                        }
            /^==> .* <==$/ { stdin=0; next;                        }
                           { if( !was_seen[$0]++ && stdin ) print; }

'

но это существенно короче, да, и поэтому надежнее — кстати, в коде в посте у меня баг (должны быть while вместо if)

зато ты упорно игнорируешь подсказываемый мной "-n+1", а без него баг у тебя

www_linux_org_ru ★★★★★ ()
Ответ на: комментарий от val-amart

в нормальных приложениях используются message queues

вот замечательно; а как мне из одной queue повычитать содержимое нескольких других queues, и чтобы это из командной строки работало, для легкости тестирования?

текстовый файл как queue, по-моему, вполне нормально

www_linux_org_ru ★★★★★ ()
Ответ на: комментарий от val-amart

message queues

если есть какие-то с удобной работой из ком. строки, назови

а то сейчас воркеры у меня аппендят каждый в свой файл (чтобы не перезаписать друг друга); а можно ли аппендить нескольким воркерам в один файл?

всяких краулеров миллион

у меня своя довольно необычная логика, но конечно же интересен хорошо расширяемый краулер, которому можно было бы задавать правила на dsl, учитывающем массу сведений о html-файле

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от val-amart

message queues

да-да, и пожалуйста чтобы они *не требовали* запуска демона

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

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

а как мне из одной queue повычитать содержимое нескольких других queues, и чтобы это из командной строки работало

такой (простой) способ мне не известен.

зато ты упорно игнорируешь подсказываемый мной "-n+1", а без него баг у тебя

lol, ну да =)

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

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

что это грязь

а приличный бы tail добавлял в начало каждой строки (кроме тех, где записано имя файла) \t, и тогда бы его выхлоп парсился бы 100% однозначно

давно думаю, что стоит предложить широкой общественности такой формат, фактически, мультиплексирования

www_linux_org_ru ★★★★★ ()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от www_linux_org_ru

а то сейчас воркеры у меня аппендят каждый в свой файл (чтобы не перезаписать друг друга); а можно ли аппендить нескольким воркерам в один файл?

в общем случае, нет увы.

я тут пока писал прошлый ответ понял что у нас для это (stream processing в консоли) есть специальный доморощеный инструмент, который какраз так и называется — xtail. работает с нашими stream-источниками, умеет в фильтры а-ля авк. вроде бы не опенсорс, спрошу завтра, может заопенсорсим обрезанную версию для работы с файлами и фифо.

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

stream processing в консоли

об этом можно поговорить завтра

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

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

не правы авторы tail -f и мораль в том, что необходимо написать свою tail -f

я уже в значительной мере обдумал правильную tail, которая может работать еще и менеджером воркеров, и видимо напишу это новым постом, на этот раз в /admin/

www_linux_org_ru ★★★★★ ()
Ответ на: комментарий от val-amart

может заопенсорсим обрезанную версию для работы с файлами и фифо.

интересен не столько код (его-то наваять совсем не проблема), а *доки*; хорошая спецификация важнее кода

причем еще не факт, что я соглашусь с вашим видением утилит, и возможно предложу свое видение — однако все юзкейсы, которые есть у вас, весьма и весьма интересны

ну и доки, я думаю, заопенсорсить куда проще, чем код

в чем разница в видении: мне более интересен не скриптовый язык а-ля awk (хотя и это тоже), а готовая утилита, куда подставляешь названия файлов (и при желании/необходимости — регэкспы); я считаю, что этим проще и удобнее пользоваться

www_linux_org_ru ★★★★★ ()
Ответ на: комментарий от val-amart

еще насчет доков: в общении с тобой я понял, что надо писать свою tail -f, а до этого, естественно, утрясти ее спецификацию

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

Вообще, таска весьма-весьма неопределенная.

безусловно; и в таком случае надо не бросаться писать код (хотя его куски скорее всего пригодятся), а общаться с автором (т.е. со мной) на предмет уточнения задачи

в результате общения в этом треде я задачу порядком уточнил (это получается такой tail -f на стероидах), но все равно стоит запостить в /admin/ еще один тред для окончательного уточнения, причем обязательно с коротенькими рабочими референсными реализациями

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