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

Я хочу найти строку в каждой строке файла и, если она существует, вернуть другую конкретную строку.

] Это сообщение было отредактировано после публикации решений, чтобы помочь лучше сформулировать вопрос (и поэтому некоторые из предыдущих ответов больше не применимы)

У меня есть этот код:

Numlines=$(grep "" -c File.txt)
for (( line=1; line<=$Numlines; line++ )) ; do 
awk -v line="$line" 'NR==line ...???

Строка, которую я ищу, - style-name = "T . Если эта строка находится в строке в цикле for, верните цифры, которые стоят непосредственно после T . Строки в File.txt может содержать такие строки, как style-name = "T2" , и в этом случае я хочу вернуть только 2 . Строка не находится в одном и том же месте в каждой строке в ] File.txt , поэтому я не думаю, что могу использовать обозначения полей в awk .

IIRC, "/ style-name \ = \" T / " должен предоставить совпадение, но если я использую это в своем коде, он либо выдает ошибку, либо ничего не возвращает. Возможно, сценарий проверит, может ли он дать совпадение, и, если да, использовать вторую строку кода, чтобы получить строку, хотя я думаю, что awk может выполнить это с помощью одной строки кода , как только код-предшественник вычислен.

Вот пример File.txt :

<TEST1>     <text:p text:style-name="P4">Hello<text:span text:style-name="T2">world</text:span></text:p>
<tyi.ggg>     <text:p text:style-name="P9">Hi<text:span text:style-name="T16">there</text:span></text:p>
<TEST2>     <text:p text:style-name="P540">0 <text:s/>oooh yeah<text:s text:c="2"/>kool-aid<text:s text:c="12"/>0:00</text:p>

Вывод для первой строки (первый раз через для цикла ) должно быть 2. Вывод для второй строки (второй раз через для цикла ) должен быть 16. Вывод для третьей строки должен быть пустым.

0
задан 28 October 2020 в 06:57

2 ответа

В качестве образца я использовал следующий текст (измененный образец предоставлено OP):

<TEST1>     <text:p text:style-name="P4">Hello<text:span text:style-name="T2">world</text:span><text:span text:style-name="T3"></text:p>
<TEST2>     <text:p text:style-name="P540">0 <text:s/>oooh yeah<text:s text:c="2"/>kool-aid<text:s text:c="12"/>0:00</text:p>
<ANOTHER_TEST15>     <text:p text:style-name="P9">Hi<text:span text:style-name="T16">there</text:span></text:p>

Я придумал эту комбинацию команд grep и sed , которая может быть не самой эффективной, но относительно простой для понимания:

grep -n 'style-name="T' File.txt | grep -P -o '^\d+:<\w*>|style-name="T\d+' | sed -z 's/style-name="//g; s/:/ /; s/\nT/ T/g'

Нарушение команды:

  • grep -n 'style-name = "T' File.txt добавляет номера строк, для которых был найден style-name =" T .

    Вывод:

     1:   Hello  world   
    3:   Привет,  там  
     
  • grep -P -o '^ \ d +: <\ w *> | style-name = "T \ d +' использует предыдущий вывод как ввод и сопоставляет номера строк с текстом внутри : < и > рядом с ними и style-name = "T с номером рядом с ним. Каждое совпадение печатается в новой строке.

    Результат:

     1: 
    style-name = "T2
    style-name = "T3
    3: 
    style-name = "T16
     
  • sed -z 's / style-name = "// g; s /: / /; s / \ nT / T / g' использует предыдущий вывод как ввод и удаляет имя-стиля = " и : и заменяет перенос строки перед T ( \ nT ) одним пробелом, за которым следует T ( T ).

    Вывод:

     1  T2 T3
    3  T16
     
2
ответ дан 4 January 2021 в 08:18

Я не вижу смысла в многократной обработке файла с помощью цикла оболочки.

В GNU awk вы можете записывать подшаблоны в массив с помощью соответствует функции . Таким образом, вы можете просто сделать:

gawk 'match($0,/style-name="T([0-9]+)"/,m){print m[1]}' File.txt

В обычном POSIX awk функция match не имеет этой расширенной функциональности, но вместо этого вы можете использовать ее RSTART и RLENGTH , чтобы извлечь нужную подстроку:

awk 'match($0,/style-name="T[0-9]+"/){print substr($0,RSTART+13,RLENGTH-14)}' File.txt

[Если вы должны сделать это в цикле оболочки, нацеленного на конкретный номер строки на каждой итерации по какой-то неуказанной причине, то вы можете изменить правило на NR == line && match (. ..) {...} ].


Если вам нужно извлечь несколько T # значений для каждой записи, вы можете заключить совпадение в цикл, который перебирает $ 0 пример:

gawk '
  {test=$1}       # save the `TEST#` before we start the `match` loop 
  {
    T=""
    while(match($0,/style-name="T([0-9]+)"/,m)) {
      T = T=="" ? m[1] : T OFS m[1]     # append the `T#`
      $0 = substr($0,RSTART+RLENGTH)    # remove the part we already matched
    }
  } 
  T !="" {
    print NR,test,T    # print the record (line) number, `TEST#`, and accumulated `T#`s
  }
' File.txt
1 <TEST1> 2
2 <TEST2> 16

Чтобы сделать то же самое в awk, отличном от GNU, используя методы RSTART и RLENGTH , которые я показал ранее, замените цикл while с:

while(match($0,/style-name="T[0-9]+"/)) {
  t = substr($0,RSTART+13,RLENGTH-14)
  T = T=="" ? t : T OFS t
  $0 = substr($0,RSTART+RLENGTH)    # remove the part we already matched
}
2
ответ дан 4 January 2021 в 08:18

Другие вопросы по тегам:

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