Извлеките строки между двумя токенами в текстовом файле с помощью bash

У меня есть текстовый файл, который выглядит так:

random useless text 
<!-- this is token 1 --> 
para1 
para2 
para3 
<!-- this is token 2 --> 
random useless text again

Я хочу извлечь текст между токенами (исключая, конечно же, токены). Я попытался использовать ## и %% для извлечения данных между ними, но это не сработало. Я думаю, что это не предназначено для манипулирования такими большими текстовыми файлами. Любые предложения, как я могу это сделать? возможно awk или sed?

Ответ 1

Вы можете извлечь его, включая токены с sed. Затем используйте головку и хвост, чтобы вырезать токены.

... | sed -n "/this is token 1/,/this is token 2/p" | head -n-1 | tail -n+2

Ответ 2

Нет необходимости в head и tail или grep или для чтения файла несколько раз:

sed -n '/<!-- this is token 1 -->/{:a;n;/<!-- this is token 2 -->/b;p;ba}' inputfile

Пояснение:

  • -n - не делать неявной печати
  • /<!-- this is token 1 -->/{ - если найден стартовый маркер, то
    • :a - метка "a"
      • n - прочитайте следующую строку
      • /<!-- this is token 2 -->/q - если это конечный маркер, закройте
      • p - в противном случае напечатайте строку
    • ba - ответвление на метку "a"
  • } end if

Ответ 3

Возможно, sed и awk имеют более элегантные решения, но у меня есть подход "бедных" с grep, cut, head и tail.

#!/bin/bash

dataFile="/path/to/some/data.txt"
startToken="token 1"
stopToken="token 2"

startTokenLine=$( grep -n "${startToken}" "${dataFile}" | cut -f 1 -d':' )
stopTokenLine=$( grep -n "${stopToken}" "${dataFile}" | cut -f 1 -d':' )

let stopTokenLine=stopTokenLine-1
let tailLines=stopTokenLine-startTokenLine

head -n ${stopTokenLine} ${dataFile} | tail -n ${tailLines}

Ответ 4

Для чего бы то ни было, я бы нашел для Perl с его комбинацией (среди прочих) sed и awk возможностей. Что-то вроде (будьте осторожны - непроверены):

my $recording = 0;
my @results = ();
while (<STDIN>) {
   chomp;
   if (/token 1/) {
      $recording = 1;
   }
   else if (/token 2/) {
      $recording = 0;
   }
   else if ($recording) {
      push @results, $_;
   }
}

Ответ 5

Попробуйте следующее:

sed -n '/<!-- this is token 1 -->/,/<!-- this is token 2 -->/p' your_input_file
        | egrep -v '<!-- this is token . -->'

Ответ 6

не нужно вызывать могущественный sed/awk/perl. Вы можете сделать это "bash -одно":

#!/bin/bash
STARTFLAG="false"
while read LINE; do
    if [ "$STARTFLAG" == "true" ]; then
            if [ "$LINE" == '<!-- this is token 2 -->' ];then
                    exit
            else
                    echo "$LINE"
            fi
    elif [ "$LINE" == '<!-- this is token 1 -->' ]; then
            STARTFLAG="true"
            continue
    fi
done < t.txt

С уважением

Realex