sed несколько записей в одной команде для удаления данных

Я не уверен, что понимаю Вашу проблему полностью, но как насчет:

$ nc 111.222.233.244 1234 \
    | tee -a /tmp/stream.dump \
    | while ! do_something_here; do : ; done

Кроме очевидного дампа потока в файл с tee (добавление, так как сеть может быть разъединена по различным причинам, и Вы не могли бы обязательно хотеть перезаписывать предыдущую часть потока), это будет повторно метать икру do_somehing_here если это не выходит правильно. Следовательно, если do_somehing_here например, сценарий обертки, можно уничтожить то, что он на самом деле называет, который заставляет сценарий выйти неправильно и быть повторно порожденным сразу же. При редактировании сценария прежде, чем уничтожить его обработка потока может быть изменена без повторного подключения.

2
20.07.2015, 05:04
2 ответа

С помощью sed, если вы можете отформатировать файл в виде сценария sed, вы можете сделать это автоматически. Следующее должно работать с GNU sed. С BSD sed это сработает, если вы сделаете -i '' -e для второго вызова sed....

sed -ne's|[]\*&^.$/[]|\\&|g' \
     -e's|..*|/^@&",/d|p' <./list.txt |
sed -ie'h;s/[^,]*[^@]*//' -f- -eg ./data.txt

Если вы сделаете...

-e's|..*|/^@&",/Id|p' ...

...во второй строке GNU sed будет delete соответствовать любой строке в регистре list.txt нечувствительно, но это будет равносильно синтаксической ошибке с большинством других.

Он пытается оптимизировать совпадения, удаляя первое поле и все до первого @ во втором поле во главе скрипта, который он запускает для каждой строки, затем проверяет совпадения, и, если строка проходит через все из них, gets копию строки, которую он сохранил в верхней части скрипта в hстаром месте. Таким образом, sed не нужно /^[^,]*,[^,]*.../ для каждого совпадения. Если list.txt очень длинный, то, несмотря на это, процесс не будет быстрым. В этом случае grep -F следует отдать предпочтение (и, вероятно, в этом случае).


Как sed, так и grep можно заставить работать лучше - и во многих случаях значимо , так что, если используемая кодовая страница уменьшена в размере. Например, если вы в данный момент находитесь в UTF-8 локали, то выполнение:

(   export LC_ALL=C
    sed -ne's|[]\*&^.$/[]|\\&|g' \
         -e's|..*|/^@&",/Id|p'   |
    sed -ie'h;s/[^,]*[^@]*//' -f-\
         -eg ./data.txt
)   <./list.txt

... может изменить ситуацию к лучшему - вместо того, чтобы рассматривать в качестве совпадений какие-то umpteen-тысячи различных символов, регекс-движку нужно учитывать только 128 возможностей. Это никак не должно повлиять на результат - каждый чар является байтом в C-локации и все будет учтено.

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


Для этого w/ grep и sed -i:

(   export LC_ALL=C
    cut -d\" -f4 | cut -d@ -f2    |
    grep -Fixnf ./list.txt        |
    sed -e's|:*\([0-9]*\).*|:\1|p'\
        -e's||\1!{p;n;b\1|p'      \
        -e's||};n|'               |
    sed -nif- -e:n -e'p;n;bn'     \
        ./data.txt
)   <./data.txt

Это самый быстрый способ, который я могу себе представить, используя sed's -i. Он ломается следующим образом:

  1. cut | cut

    • Первые две cuts уменьшают ваши входные строки ./data.txt от/до....

     "foxva****omes****","scott@hotmail.com","8*** Rd","Ne***ah","Wi***in","54***","*******"
    

     hotmail.com
    
  2. grep

    • grep может затем сравнить этот вход с каждой линией в своем образце -file -перечне. txt используя регистр -insensitive -Fixed string -x вся строка совпадает и сообщает о том, что строка -number находится в начале каждой строки его вывода.
  3. sed -e

    • sed полосы grep выводят только номера строк и записывают другой сценарий sed, который выглядит как (учитывая гипотетические grep совпадения на строках 10 и 20) :

     :10
     10!{p;n;b10
     };n
     :20
     20!{p;n;b20
     };n
    
  4. sed -inf-

    • Последний sed читает -stdin как свой скрипт и выполняет его только один раз - он не выполняет скрипт для каждой входной строки, как это обычно делается в скриптах w/ sed, а выполняет скрипт в первый и единственный раз, когда он проходит через входную строку - и ему нужно всего лишь попробовать один тест на каждую входную строку.

    • В нашем предыдущем примере для строк 1-9 sed сделает:

      • Если текущая строка - это ! не 10, {, а p протяните токовую строку, перезапишите токовую строку с помощью n ext input line, и b ранчо вернётся к метке : с именем 10.
    • и для последней серии строк sed будет print; затем перезапишите текущую строку с next, ранчо b на метку :n до тех пор, пока оно не перезапишет все входные данные.


Это не работает, если ./data.txt очень большой, потому что sed застрял в попытке обработать входной файл скрипта намного больше, чем он может надежно обработать. Обходной путь заключается в том, чтобы принимать входные данные в кусочках. Этот может быть надежно обработан - даже в конвейере - если вы используете правильный тип читателя. dd - это тот самый читатель.

Я создал такой тестовый файл:

sh -c ' _1=\"foxva****omes****\",\"scott@
        _2='\''","8*** Rd","Ne***ah","Wi***in","54***","*******"'\''
        n=0
        for m do printf "$_1%s$_2\n$_1$((n+=1))not_free.com$_2\n" "$m"
        done
'       $(cat ~/Downloads/list.txt) >/tmp/data.txt

...где list.txt - здесь - по вашему -другому вопросу . Он делает каждую другую строку, как...

"foxva****omes****","scott@11mail.com","8*** Rd","Ne***ah","Wi***in","54***","*******"
"foxva****omes****","scott@1not_free.com","8*** Rd","Ne***ah","Wi***in","54***","*******"
"foxva****omes****","scott@123.com","8*** Rd","Ne***ah","Wi***in","54***","*******"
"foxva****omes****","scott@2not_free.com","8*** Rd","Ne***ah","Wi***in","54***","*******"

Затем я довёл его до чуть более 80 мб, как...

while [ "$(($(wc -c <data.txt)/1024/1024))" -lt 80 ]
do    cat <<IN >./data.txt
$(    cat ./data.txt ./data.txt)
IN
done
ls -hl ./data.txt
wc -l <./data.txt

-rw-r--r-- 1 mikeserv mikeserv 81M Jul 19 22:22 ./data.txt
925952

... а потом я...

(   trap rm\ data.tmp 0;  export  LC_ALL=C
    <./data.txt dd bs=64k cbs=512 conv=block    |
    while       dd bs=64k cbs=512 conv=unblock  \
                count=24  of=./data.tmp
                [ -s ./data.tmp ]
    do          
    <./data.tmp cut -d\" -f4 |  cut -d@  -f2    |
                grep -Fixnf ./list.txt          |
                sed -e's|:*\([0-9]*\).*|:\1|p'  \
                    -e's||\1!{p;n;b\1|p'        \
                    -e's||};n|'                 |
                sed -nf- -e:n -e'p;n;bn' ./data.tmp
    done        2>/dev/null
)|  wc -l

1293+1 records in
7234+0 records out
474087424 bytes (474 MB) copied, 21.8488 s, 21.7 MB/s
462976

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

Методика работает, потому что dd читает и записывает в байт - даже через трубку, если вы знаете, о чем говорите. И можно даже пробить ввод по строке с той же степенью точности, если можно надежно convert по максимальной длине строки block-размером (что здесь 512, или {_POSIX_LINE_MAX}).

Воображаемый читатель может справедливо предположить, что та же самая техника может быть применена к потоку любого вида - даже вида live-log - с небольшим изменением здесь или там (а именно, чтобы сделать это безопасно, первые аргументы dd должны измениться с bs= на obs=). В любом случае, однако, вам понадобится некоторая уверенность в максимальном размере входной строки, и, если строка может законно заканчиваться символом <пробела>, дополнительный механизм фильтрации, вставленный перед процессами dd для защиты от удаления трейлинговых <пространств> с помощью dd conv=unblock (который работает путем удаления всех трейлинговых пустот для каждого cbs-размерного conv-блока стирания и добавления \newline). tr и (un|)expand приходят мне на ум как вероятные кандидаты на такой фильтр.

Это не самый быстрый способ - для этого нужно посмотреть на -merge sort -операцию, я ожидаю, но она довольно быстрая, и будет работать с вашими данными. Правда, это вроде как нарушает -i, но я думаю, что это будет правдой, куда бы вы ни пошли.

3
27.01.2020, 22:05

Вы можете решить эту проблему несколькими способами. Во-первых, sed поддерживает несколько выражений за один запуск:

sed -i -e '/^[^,]*,[^,]*hotmail/Id' -e '/^[^,]*,[^,]*gmail/Id' -e '/^[^,]*,[^,]*yahoo/Id' data.txt

Вы также можете сделать это в одном выражении:

sed -i -e '/^[^,]*,[^,]*\(hotmail\|gmail\|yahoo\)/Id' data.txt

Обратите внимание, что (, ) и | , все должны быть экранированы.

0
27.01.2020, 22:05

Теги

Похожие вопросы