Bash: Как получить несколько строк из файла и сохранить вывод в другой файл [закрыто]
У меня есть такой файл журнала
$ 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 или что-то еще. Спасибо
Ответы
Вот перл:
$ 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
$ 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
правильный инструмент для такого рода вещей. Здесь мы проверяем, совпадает ли второе поле со вторым полем предыдущей строки, и выводим его на печать. Затем сохраните предыдущую строку и повторите. Всегда печатайте последнюю строку ввода.
Это может сработать для вас (GNU sed):
sed '/^\[/h;//!H;$!d;x' file
Если строка начинается, [
сохраните ее в удерживаемом пространстве (перезаписав все, что было там ранее).
В противном случае добавьте текущую строку в удерживаемое пространство.
Удалите все строки, кроме последней.
В конце файла переключитесь на место хранения и распечатайте его содержимое.
Иногда вам не везет, если вы хотите выполнить такой фильтр, не меняя порядок строк. И если эти строки написаны не в начале файла или в конце: 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)) ####
Таким образом, вы можете выбрать, хотите ли вы сохранить только последнюю строку прогрессии, даже если процентное соотношение не то же самое, или если вы хотите отфильтровать только строки, которые имеют один и тот же процент несколько раз.
Если гарантировано, что первая строка, которую вы хотите вывести, это последняя строка, начинающаяся с [
, тогда вы можете сделать это, что по сути заменяет все от начала файла до последнего, [
которому предшествует разрыв строки, на один [
:
sed -z 's/.*\n\[/[/' file
Предполагая, что ...
строка в начале вашего образца ввода представляет больше ведущих [...] ...
строк:
$ 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.