sed / awk между двумя шаблонами в файле: шаблон 1, заданный переменной из строк второго файла; образец 2, обозначенный указанным символом - PullRequest
1 голос
/ 14 июля 2020

У меня два файла. Один файл содержит шаблон, который я хочу сопоставить во втором файле. Я хочу использовать этот шаблон для печати между этим шаблоном (включенным) до указанного символа (не входит в комплект), а затем объединить его в один выходной файл.

Например,

File_1:

a
c
d

и File_2:

>a
MEEL
>b
MLPK
>c
MEHL
>d
MLWL
>e
MTNH

Я использовал варианты этого l oop:

while read $id;
     do 
       sed -n "/>$id/,/>/{//!p;}" File_2;
done < File_1

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

>a
MEEL
>c
MEHL
>d
MLWL

Но еще не повезло. Я играл с grep / fgrep awk и sed, и между этими тремя, похоже, не получается получить правильный (или какой-либо результат). Не мог бы кто-нибудь указать мне правильное направление?

Ответы [ 4 ]

5 голосов
/ 14 июля 2020

Попробуйте:

$ awk -F'>' 'FNR==NR{a[$1]; next}  NF==2{f=$2 in a} f'  file1 file2
>a
MEEL
>c
MEHL
>d
MLWL

Как это работает

  • -F'>'

    Это устанавливает разделитель полей на >.

  • FNR==NR{a[$1]; next}

    При чтении первого файла это создает ключ в массиве a для каждой строки в файле file.

  • NF==2{f=$2 in a}

    Для каждой строки в файле 2, который имеет два поля, это устанавливает для переменной f значение true, если второе поле является ключом в a, или false, если это нет.

  • f

    Если f верно, вывести строку.

2 голосов
/ 14 июля 2020

Простое (GNU) sed решение. Файлы читаются только один раз. Предполагается, что символы в File_1 не нужно заключать в кавычки в выражении sed.

pat=$(sed ':a; $!{N;ba;}; y/\n/|/' File_1)
sed -E -n ":a; /^>($pat)/{:b; p; n; /^>/ba; bb}" File_2

Объяснение: Первый вызов sed генерирует регулярное выражение, которое будет использоваться в второй вызов sed и сохраняет его в переменной pat. Цель состоит в том, чтобы избежать многократного чтения всего File_2 для каждой строки File_1. Он просто "проглатывает" File_1 и заменяет символы новой строки | символами. Таким образом, образец File_1 становится строкой со значением a|c|d. Регулярное выражение a|c|d соответствует, если совпадает хотя бы одна из альтернатив (a, b, c для этого примера) (это расширение GNU sed).

Второй sed выражение, ":a; /^>($pat)/{:b; p; n; /^>/ba; bb}", можно преобразовать в псевдокод следующим образом:

begin:
    read next line (from File_2) or quit on end-of-file
label_a:
    if line begins with `>` followed by one of the alternatives in `pat` then
label_b:
        print the line
        read next line (from File_2) or quit on end-of-file
        if line begins with `>` goto label_a else goto label_b
    else goto begin
1 голос
/ 14 июля 2020

Позвольте мне попытаться объяснить, почему ваш подход не работает:

  • Вам нужно сказать while read id вместо while read $id.
  • Команда sed />$id/,/>/{//!p;} исключит строки, которые начинаются с >.

Тогда вы можете сказать что-то вроде:

while read id; do
    sed -n "/^>$id/{N;p}" File_2
done < File_1

Результат:

>a
MEEL
>c
MEHL
>d
MLWL

Но приведенный выше код неэффективен, потому что он читает File_2 столько раз, сколько идентификаторов в File_1. Попробуйте вместо этого элегантное решение от John1024.

0 голосов
/ 14 июля 2020

Если доступен ed, и поскольку оболочка задействована.

#!/usr/bin/env bash

mapfile -t to_match < file1.txt

ed -s file2.txt <<-EOF                                               
  g/\(^>[${to_match[*]}]\)/;/^>/-1p
  q
EOF
  • Он будет запускаться только ed один раз, а не каждую строку, имеющую шаблон, который соответствует из file1. Например, если у вас есть от a до z из file1, ed не будет запускаться 26 раз.

  • Требуется bash4 + из-за mapfile.

Как это работает

  • mapfile -t to_match < file1.txt Сохраняет запись / значение из файла1 в массив с именем to_match

  • ed -s file2.txt укажите на file2 флаг -s, что означает, что не печатайте информацию о файле, ту же информацию, которую вы получите с wc file

  • <<-EOF Здесь документ, синтаксис оболочки .

g/\(^>[${to_match[*]}]\)/;/^>/-1p
  • g означает поиск по всему файлу или глобальному.

  • ( ) группа захвата , необходимо экранирование, потому что ed поддерживает только BRE, базовое c регулярное выражение.

  • ^> Если строка начинается с >, ^ является привязкой, означает начало.

  • [ ] - выражение в скобках соответствует тому, что находится внутри него, в данном случае значение массива "${to_match[*]}"

  • ; Включить следующий адрес / шаблон

  • /^>/ Сопоставить с начальным >

  • -1 go назад на одну строку после совпадения с шаблоном.

  • * 10 83 *p напечатайте все, что совпадало с шаблоном.
  • q выйти ed

...