#bash #awk #sed
#bash #awk #sed
Вопрос:
У меня есть переменный входной файл, который можно отформатировать, как показано ниже.
text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueE valueM valueF
text1 valueG valueM valueH
text1 valueI valueN valueJ
text2 valueX
text1 valueK valueO valueL
text1 valueP valueO valueQ
text1 valueR valueN valueS
text1 valueT valueM valueU
Я хочу напечатать только, text1 valueA valueN valueB
если text2 valueX
существует под предыдущей строкой. Например, вывод должен быть:
text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
Мне также нужно иметь возможность сопоставлять часть valueX
. Допустим valueX=a.b.c.d-e
, мне нужно сопоставить a.b.
myvariable=a.b.
echo $myvariable
a.b.
Обновить:
Приносим извинения за нечеткие входные данные… Я предполагал, что смогу сопоставить часть текста, но я ошибся.
Итак, если данные выглядели следующим образом:
text1 valueA valueN valueB
text2 a.b.c.d-e
text1 valueC valueN valueD
text2 a.b.c.d-e
text1 valueE valueM valueF
text1 valueG valueM valueH
text1 valueI valueN valueJ
text2 a.b.c.d-e
text1 valueK valueO valueL
text1 valueP valueO valueQ
text1 valueR valueN valueS
text1 valueT valueM valueU
Как бы вы сопоставили, например, a.b.
, если val=a.b.
echo $val
a.b.
Комментарии:
1. чтобы соответствовать части
valueX
, например:val
Пожалуйста, приведите пример (посколькуval
это часть каждой записи).2. Это было глупо с моей стороны: D … итак, если
valueX
естьa.b.c.d-e
, как вы, например, сопоставляете это какa.b.
?3.Если я
awk '$0~/text2 val/{print p ORS $0}{p=$0}' file
получу вывод:text1 valueA valueN valueB text2 valueX
и т.д… Но еслиval=val
awk '$0~/text2 $val/{print p ORS $0}{p=$0}' file
ничего не возвращает — @JamesBrown4. Переменные в awk используются не так, вам нужно ввести их с помощью, например,
awk -v var=val 'BEGIN{print var}'
, так что в этом случае это было быawk -v s=val '$0~"text2 " s {THE REST}
.5. @JamesBrown да, спасибо за разъяснение по переменным awk. Я прокомментировал ваш пост, используя awk var.
Ответ №1:
Вот одна в awk:
$ awk '$0=="text2 valueX"{print p ORS $0}{p=$0}' file
Вывод:
text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
Объяснено:
$ awk '
$0=="text2 valueX" { # if record is a match
print p ORS $0 # print previous buffered record and current
}
{
p=$0 # buffer record for next round
}' file
Обновленное обновление с обновленными данными:
Поскольку .
это метасимвол регулярного выражения, его необходимо экранировать при вводе, чтобы избежать сопоставления, например, с abbb
:
$ awk -v s="a\.b\." '$0~s{print p ORS $0}{p=$0}' file
Выводите сейчас:
text1 valueA valueN valueB
text2 a.b.c.d-e
text1 valueC valueN valueD
text2 a.b.c.d-e
text1 valueI valueN valueJ
text2 a.b.c.d-e
Комментарии:
1. Обновлено с частичным совпадением.
2. Что я сделал, так это заменил
.
на_
cat file | sed 's/./_/g' > file2
. Затем я устанавливаюvar2=a_b_
. Теперьawk -v s="$var2" '$0~s{print p ORS $0}{p=$0}' file2
выдает хороший вывод с подчеркиванием, но я могу снова заменить его на точки с помощью sed. Вероятно, есть лучший способ сделать это, но, эй, это работает!3. Пример вывода:
text1 valueA valueN valueB text2 a_b_c_d-e
Ответ №2:
Попробуйте это:
awk 'NR>1amp;amp; $0 == "text2 valueX"{print a"n"$0} {a=$0}' input.txt
Обратите внимание, что этот скрипт выводит что-либо, только если текущая строка точно соответствует ‘text2 ValueX’.
Ответ №3:
Другой простой подход с tac
и awk
.
tac Input_file | awk '$0=="text2 valueX"{print;getline;print}' | tac
Результат будет следующим.
text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
Некоторые strace
из tac
(обработки произвольного файла):
lseek(3, 351051776, SEEK_SET) = 351051776
read(3, "83,10.1579,56.1257,1412067900n41"..., 8192) = 8192
write(1, "104,210,84,194,10.1313,56.1528,1"..., 4096) = 4096
write(1, "2092,56.1724,1412068200n129,20,3"..., 4096) = 4096
lseek(3, 351043584, SEEK_SET) = 351043584
read(3, "7900n148,159,77,186,97,10.2090,5"..., 8192) = 8192
write(1, ",140,182,10.1208,56.1784,1412067"..., 4096) = 4096
write(1, "10.1859,56.1239,1412067900n53,17"..., 4096) = 4096
lseek(3, 351035392, SEEK_SET) = 351035392
Вы можете увидеть позицию уменьшения в lseek
.
Комментарии:
1. 1. Узнал, что есть такая вещь, как
tac
, случайно противоположнаяcat
. Выглядит аккуратно, но меня беспокоит только эффективность использования памяти. Я надеюсь, чтоtac
это не загрузит весь файл в память, чтобы отменить его.2. @darksky Нет,
tac
не соответствует. Согласноstrace
он считывает блоки размером 8 КБ с диска, записывает 2 блока размером 4 КБ (во всяком случае, при настройке моего диска) и lseeks более раннюю позицию в файле.3. @JamesBrown, Спасибо, Джеймс сэр, за добавление информации о tac
4. @RavinderSingh13 Могу ли я добавить немного
strace
дампа к вашему ответу?5. @JamesBrown, вам не нужно спрашивать, сэр, всегда рады добавить / отредактировать мой ответ, сэр, пожалуйста, продолжайте;)
Ответ №4:
sed
Решение:
$ sed -n -e '/text2 valueX/{' -e 'H;x;p;}' -e 'h' file
text1 valueA valueN valueB
text2 valueX
text1 valueC valueN valueD
text2 valueX
text1 valueI valueN valueJ
text2 valueX
Измените регулярное выражение на /^text2 valueX$/
, если вы хотите, чтобы совпадала целая строка.
Это равно значению GNU sed:
sed -n '/text2 valueX/{H;x;p;};h' file
POSIX sed
должен начинать перевод строки или нового -e
блока после {
открывающей или }
закрывающей скобки.
H Append the contents of pattern space to hold space, separate them by a newline character.
x Exchange the contents of pattern space and hold space.
p Print the contents of pattern space.
h Overwrite the contents of hold space with pattern space.
/text2 valueX/
это регулярное выражение, оно соответствует текущей строке (пространство шаблонов), если оно соответствует, команда или {}
блок после нее будут выполнены.
Внутри блока добавьте строку, чтобы сохранить пробел, в котором уже есть последняя строка. Затем замените пространство удержания пространством шаблона, затем распечатайте.
И после блока переместите текущую строку в пространство удержания, чтобы заменить ее прежнее содержимое.
Примечание: с помощью этого решения у вас не может быть нескольких text2 valueX
последовательных строк, иначе будут напечатаны дополнительные строки. Если такое произойдет, пожалуйста, прокомментируйте, дайте мне знать, как вы хотите с ними справиться.
Комментарии:
1. Спасибо за это! Не могли бы вы, пожалуйста, объяснить
{' -e 'H;x;p;}' -e 'h'
часть?2. @TempoChocolate добавил некоторое объяснение.
3. Спасибо за объяснение. Как вы сопоставляете часть
valueX
, например:val
?4. @TempoChocolate Что вы хотите для части , которую вы сопоставили? Для разных целей будут использоваться разные способы. Я предлагаю вам ознакомиться с руководством sed .
Ответ №5:
Это может сработать для вас (GNU sed):
sed '$!N;/ntext2 valueX/p;D' file
Откройте движущееся окно из двух строк по всей длине файла.
Если вторая строка совпадает text2 valueX
, выведите обе строки.
Удалите первую строку и повторите.
Для измененного вопроса:
sed '$!N;/ntext2.*a.b./p;D' file
Используйте тот же метод, но введите новый шаблон в качестве регулярного выражения.
Примечание. Для нового шаблона может потребоваться, чтобы метасимволы были заключены в кавычки, например, периоду должна предшествовать
.
regexp='a.b.'
sed '$!N;/ntext2.*'"$regexp"'/p;D' file