Я хочу найти строку в каждой строке файла и, если она существует, вернуть другую конкретную строку.
] Это сообщение было отредактировано после публикации решений, чтобы помочь лучше сформулировать вопрос (и поэтому некоторые из предыдущих ответов больше не применимы)
У меня есть этот код:
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. Вывод для третьей строки должен быть пустым.
В качестве образца я использовал следующий текст (измененный образец предоставлено 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 text: span> text: p>
3: Привет, там text: span> text: p>
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
Я не вижу смысла в многократной обработке файла с помощью цикла оболочки.
В 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
}