Bash: Как получить несколько строк из файла и сохранить вывод в другой файл [закрыто]

Jan 17 2021

У меня есть такой файл журнала

$ cat build.log
..........
[ 60% 60917/101076] AAPT2 compile ....
[ 60% 60918/101076] AAPT2 compile ....
[ 60% 60919/101076] AAPT2 compile ....
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

Как сгенерировать новые проанализированные журналы, чтобы вывод нового журнала файлов был таким:

$ cat parsed.log
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

Например, получить только последний прогресс [ 60% 60920/101076]до конца файла, используя, может быть, grep, sed или что-то еще. Спасибо

Ответы

dawg Jan 17 2021 at 22:29

Вот перл:

$ perl -0777 -lne 'print $1 if /(^\[[^[]*\z)/m' file

Или канал perl:

$ perl -E 'say reverse <>' file | perl -lpE 'if (/^\[/){ say; last}' | perl -E 'say reverse <>'

Для awkвас вы можете:

$ awk 'BEGIN{RS="\\["}END{print "[" $0}' file

Конечно, вы можете знать, что если сбой всегда составляет 3 строки, самым простым является использование tail:

$ tail -n 3 file

Вся печать:

[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1
WilliamPursell Jan 17 2021 at 17:57
$ cat build.log ........ [ 60% 60917/101076] AAPT2 compile .... [ 60% 60918/101076] AAPT2 compile .... [ 60% 60919/101076] AAPT2 compile .... [ 60% 60920/101076] AAPT2 compile .... ninja: build stopped: subcommand failed. 21:41:22 ninja failed with: exit status 1 $ awk '$2 != n[2]{print p} {p = $0; split(p,n,FS)} END{print p}' build.log
........
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

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

potong Jan 17 2021 at 18:07

Это может сработать для вас (GNU sed):

sed '/^\[/h;//!H;$!d;x' file

Если строка начинается, [сохраните ее в удерживаемом пространстве (перезаписав все, что было там ранее).

В противном случае добавьте текущую строку в удерживаемое пространство.

Удалите все строки, кроме последней.

В конце файла переключитесь на место хранения и распечатайте его содержимое.

IdrissNeumann Jan 17 2021 at 18:15

Иногда вам не везет, если вы хотите выполнить такой фильтр, не меняя порядок строк. И если эти строки написаны не в начале файла или в конце: tac, sortи uniqне было бы правильные инструменты.

Вот решение, использующее awk:

$ awk 'function push(a,e) { a[length(a)+1] = e } BEGIN {split("", lines); to_replace="toreplace"; exists=0} {if ($0 ~ "^\\[ [0-9]+%"){ll=$0; if (exists <= 0) {exists++; push(lines,to_replace)}} else {push(lines, $0)}} END {for (e in lines){if (lines[e] == to_replace) {print ll} else {print lines[e]}}}' test.log 
..........
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

Доступная для чтения и объясненная версия:

# a function to append an element to an array dynamically
function push(a,e) { 
  a[length(a)+1] = e 
} 

BEGIN {
  split("", lines); # initializing an array
  to_replace="toreplace"; # you can change the replace key if you want
  exists=0
} 

{
  if ($0 ~ "^\\[ [0-9]+%"){ # matching all percentages/progression lines, regardless of their values ll=$0; 
    if (exists <= 0) {
      exists++; 
      push(lines, to_replace)
    }
  } else {
    push(lines, $0)
  }
}

END {
  for (e in lines) {
    if (lines[e] == to_replace) {
      print ll
    } else {
      print lines[e]
    }
  }
}

Разница с решениями Уильяма Персела заключается в том, что ваши проценты растут. Посмотрим, чем отличается поведение в этом случае:

$ cat test.log 
..........
[ 60% 60917/101076] AAPT2 compile ....
[ 60% 60918/101076] AAPT2 compile ....
[ 60% 60919/101076] AAPT2 compile ....
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####
$ awk 'function push(a,e) { a[length(a)+1] = e } BEGIN {split("", lines); to_replace="toreplace"; exists=0} {if ($0 ~ "^\\[ [0-9]+%"){ll=$0; if (exists <= 0) {exists++; push(lines,to_replace)}} else {push(lines, $0)}} END {for (e in lines){if (lines[e] == to_replace) {print ll} else {print lines[e]}}}' test.log 
..........
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####
$ awk '$2 != n[2]{print p} {p = $0; split(p,n,FS)} END{print p}' test.log 
..........
[ 60% 60919/101076] AAPT2 compile ....
[ 61% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

Таким образом, вы можете выбрать, хотите ли вы сохранить только последнюю строку прогрессии, даже если процентное соотношение не то же самое, или если вы хотите отфильтровать только строки, которые имеют один и тот же процент несколько раз.

Enlico Jan 17 2021 at 18:47

Если гарантировано, что первая строка, которую вы хотите вывести, это последняя строка, начинающаяся с [, тогда вы можете сделать это, что по сути заменяет все от начала файла до последнего, [которому предшествует разрыв строки, на один [:

sed -z 's/.*\n\[/[/' file
EdMorton Jan 17 2021 at 22:50

Предполагая, что ...строка в начале вашего образца ввода представляет больше ведущих [...] ...строк:

$ awk '/^\[/{p=$0 ORS; next} {print p $0; p=""}' build.log
[ 60% 60920/101076] AAPT2 compile ....
ninja: build stopped: subcommand failed.
21:41:22 ninja failed with: exit status 1

#### failed to build some targets (17:26 (mm:ss)) ####

Если это предположение неверно, отредактируйте свой вопрос, чтобы показать минимальный, полный , проверяемый пример - только с репрезентативными значениями, а не ...s.