Bash: Jak pobrać kilka wierszy z pliku i zapisać wynik do innego pliku [zamknięte]
Mam taki plik dziennika
$ 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)) ####
Jak wygenerować nowe, przeanalizowane dzienniki, aby dane wyjściowe nowego dziennika pliku wyglądały następująco:
$ 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)) ####
Jak tylko uzyskać ostatni postęp [ 60% 60920/101076]
do końca pliku, używając może grep, sed lub czegokolwiek. Dziękuję Ci
Odpowiedzi
Oto perl:
$ perl -0777 -lne 'print $1 if /(^\[[^[]*\z)/m' file
Lub rura perlowa:
$ perl -E 'say reverse <>' file | perl -lpE 'if (/^\[/){ say; last}' | perl -E 'say reverse <>'
W przypadku awk
można zrobić:
$ awk 'BEGIN{RS="\\["}END{print "[" $0}' file
Oczywiście możesz wiedzieć, że jeśli awaria jest zawsze 3 linijki, najprostszym jest po prostu użycie tail
:
$ tail -n 3 file
Wszystkie wydruki:
[ 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
jest odpowiednim narzędziem do tego typu rzeczy. Tutaj sprawdzamy, czy drugie pole pasuje do drugiego pola w poprzednim wierszu i drukujemy, jeśli tak. Następnie zapisz poprzednią linię i powtórz. Zawsze drukuj ostatnią linię wejścia.
To może zadziałać dla Ciebie (GNU sed):
sed '/^\[/h;//!H;$!d;x' file
Jeśli linia zaczyna się, [
zapisz ją w przestrzeni do przechowywania (nadpisując wszystko, co tam było wcześniej).
W przeciwnym razie dołącz bieżącą linię do miejsca przechowywania.
Usuń wszystkie wiersze oprócz ostatniej.
Na końcu pliku zamień na miejsce przechowywania i wydrukuj jego zawartość.
Czasami nie masz szczęścia, jeśli chcesz wykonać tego rodzaju filtr bez konieczności zmiany kolejności wierszy. A jeśli te linie nie są zapisywane na początku pliku lub na końcu: tac
, sort
i uniq
nie byłoby odpowiednie narzędzia.
Oto rozwiązanie wykorzystujące 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)) ####
Czytelna i objaśniona wersja:
# 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]
}
}
}
Różnica w stosunku do rozwiązań William Pursel polega na tym, że Twoje wartości procentowe rosną. Zobaczmy różnicę w zachowaniu w tym przypadku:
$ 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)) ####
Więc możesz wybrać, czy chcesz zachować tylko ostatnią linię progresji, nawet jeśli procent nie jest taki sam, lub jeśli chcesz filtrować tylko wiersze, które mają ten sam procent wiele razy.
Jeśli masz gwarancję, że pierwsza linia, którą chcesz umieścić, jest ostatnią linią zaczynającą się od [
, możesz to zrobić, co zasadniczo zastępuje wszystko od początku pliku do ostatniej [
poprzedzonej znakiem końca wiersza pojedynczym [
:
sed -z 's/.*\n\[/[/' file
Zakładając, że ...
linia na początku przykładowego wejścia reprezentuje więcej wiodących [...] ...
linii:
$ 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)) ####
Jeśli to założenie jest błędne, edytuj swoje pytanie, aby pokazać minimalny, kompletny , weryfikowalny przykład - taki, który zawiera tylko reprezentatywne wartości, ...
a nie s.