Bash: Jak pobrać kilka wierszy z pliku i zapisać wynik do innego pliku [zamknięte]

Jan 17 2021

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

dawg Jan 17 2021 at 22:29

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 awkmoż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
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

awkjest 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.

potong Jan 17 2021 at 18:07

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ść.

IdrissNeumann Jan 17 2021 at 18:15

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, sorti uniqnie 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.

Enlico Jan 17 2021 at 18:47

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
EdMorton Jan 17 2021 at 22:50

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.