Печатать соответствующий шаблон и предыдущие строки до разрыва строки или пробела, используя sed или awk

Имя файла нужно заключить в кавычки:

touch "directory/file with whitespace (and special characters)"

Пожалуйста, прочитайте о кавычках и специальных символах в руководстве bash.

0
04.09.2020, 08:37
4 ответа

Использованиеawk:

$ awk -v RS='' -v ORS='\n\n' 'match($0,".*TMP") { print substr($0,1,RLENGTH) }' file
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP

Это рассматривает набор строк, разделенных хотя бы одной пустой строкой, как запись. Если запись соответствует регулярному выражению .*TMP, бит после совпадения удаляется, а остальная часть записи печатается.

Бит после последнего вхождения TMPудаляется путем сопоставления регулярного выражения .*TMPс текущей записью, а затем использования substr()для вывода только бита, соответствующего этому выражению. Регулярное выражение будет соответствовать от начала записи до последнего TMPв ней, а функция match()устанавливает для переменной RLENGTHдлину этого совпадения.

Вы получите дополнительную пустую строку в конце вывода, так как мы используем двойную новую строку в качестве разделителя выходных записей.


Если вместо этого ваш файл выглядит как

AAA
CBH
VFh

GFD
DFC
VGF
HGD
tmp/some/path/here
JHG

TFD
PI1
98A
tmp/some/path/here
tmp/some/path/here
765
UBS

... и вы хотели бы сделать такое же преобразование на основе строк, начинающихся с tmp, а затем изменить регулярное выражение, используемое с match()в команде, чтобы оно полностью соответствовало символ новой строки в конце «tmp- строки»:

$ awk -v RS='' -v ORS='\n\n' 'match($0,".*tmp[^\n]*") { print substr($0,1,RLENGTH) }' file
GFD
DFC
VGF
HGD
tmp/some/path/here

TFD
PI1
98A
tmp/some/path/here
tmp/some/path/here

Обратите внимание, что я не уверен на 100%, как awkдолжен интерпретировать \nвнутри выражения в квадратных скобках, но все awkреализации, к которым у меня есть доступ (OpenBSD awk, mawk,и GNE awk), кажется, рассматривает is как новую строку, а не как два отдельных символа \и n.

3
18.03.2021, 23:07

Просто инвертируйте текст и выполните обычную печать от регулярного выражения к регулярному выражению, а затем снова -инвертируйте, чтобы получить исходный порядок

 tac < file.txt | sed -n '/TMP/,/^$/p' | tac
1
18.03.2021, 23:07

Если вы согласны с первым TMP. Пустой RSприведет к режиму абзаца, где два или более последовательных символа \nбудут использоваться в качестве разделителя записи

.
$ # sub is used to remove everything after first occurrence of TMP
$ # return value of sub (0 if no match, 1 if match is found) determines
$ # if record should be printed or not
$ # use \nTMP\n to match only whole line
$ awk -v RS= 'sub(/TMP\n.*/, "TMP\n")' ip.txt
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP

Если вам нужно до конца TMP, вы можете сделать это с помощьюGNU awk(из-заgensub)илиperl

$ # use \nTMP\n to match only whole line
$ # same as: perl -00 -ne 'print if s/.*TMP\n\K.*/\n/s' ip.txt
$ awk -v RS= '/TMP/{print gensub(/(.*TMP\n).*/, "\\1", 1)}' ip.txt
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP
1
18.03.2021, 23:07

Вы пометили sed, поэтому мы можем сделать это, как показано. Обратите внимание, что мы используем GNU sed. Насколько я понимаю, вы хотите удалить все после последней строки, начинающейся с tmp (в нижнем регистре )в каждом абзаце текстового файла. Абзац — это остров из непустой строки (и ), отделенный хотя бы одной пустой строкой от следующего экземпляра.

sed -e '
  /./{H;$!d;}
  x;/\ntmp/!d
  :chop
  /\ntmp[^\n]*$/!s/\n[^\n]*$//
  t chop
' file

Накопить абзац в удержании. Когда мы наталкиваемся на границу (пустой строки или конец ), мы начинаем исследовать параграф. Если в начале новой строки не видно tmp, мы сразу же удаляем этот параграф. В противном случае мы начинаем обрезать строки с конца параграфа, пока не увидим строку tmp. Остановите обработку этого параграфа и распечатайте его.

В GNU awk мы работаем в режиме абзаца и устанавливаем входное поле разделитель на новую строку. Начинайте рассматривать поля с конца. Как только мы увидим поле, начинающееся с tmp Мы уменьшаем параграф до этого поля и печатаем и покончим с этим абзацем.

awk -F '\n' -v RS= -v OFS='\n' '
  /(^|\n)tmp/ {
    for(i=NF; i; i--) {
      if ($i ~ /^tmp/) {
        NF=i; NF++; print; break
      }
    }
  }
' file

rindex сообщит позицию подстроки в строке с конца. Так что получите позицию последнего tmp с помощью rindex abd и используйте эту позицию, чтобы получить позицию следующей ближайшей новой строки справа.

perl -p00e '
  s/.*//s,next unless /^tmp/m;
  s/\z/\n/;
  my $p = 1+rindex($_, "\ntmp");
  my $q = 1+index($_, "\n", $p);
  substr($_, $q) = "\n";
' file
0
18.03.2021, 23:07

Теги

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