Zeilen zusammenfassen

Started by bluelupo, 2015/01/22, 19:23:19

Previous topic - Next topic

bluelupo

Hallo zusammen,

ich würde gerne folgende extrahierte Zeilen aus /var/log/apt/history.log jeweils in einer zusammenfassen:

So siehst es aus:

Start-Date: 2015-01-12  17:11:15
Commandline: apt-get dist-upgrade
Start-Date: 2015-01-14  16:58:30
Commandline: apt-get dist-upgrade


Soll dann so aussehen:

Start-Date: 2015-01-12  17:11:15 Commandline: apt-get dist-upgrade
Start-Date: 2015-01-14  16:58:30 Commandline: apt-get dist-upgrade


Wie kann ich das in einem Shellscript-Einzeiler erledigen?

bluelupo

#1
Hab's gelöst und gebe mir selbst die Antwort ;-)


egrep '(Start-Date:.*|Commandline: apt-get )' /var/log/apt/history.log | sed '$!N;s/\n/ /'


Der sed-Befehl fügt immer paarweise die Zeilen zusammen. Die Lösung kam von dieser Seite unter "Füge Zeilenpaare nebeneinander zusammen".

Okay, jetzt habe ich die Lösung, aber verstehen tue ich den sed-Ausdruck nicht :-( Nur soviel das irgendwas durch ein Leerzeichen ersetzt wird. Vielleicht gibt es hier Spezialisten die das auflösen können.

hefee

also "s/\n/ /" ist die normale ersetzung von  newlines durch leerzeichen, denn \n ist ein newline Zeichen und das wird durch ein Leerzeichen ersetzt. Jetzt ist aber leider sed so gestrickt, dass es nur zeilenweise arbeitet, also müssen wir dafür sorgen das immer zwei Zeilen zusammen in den Puffer kommen, dann ereichen wir mit N;

Sauberer wäre es wenn du so was schreibst: N; s/\nCommandline/ Commandline/, dann stellst du sicher, das wirklich die richtigen Zeile zusammengefasst werden.

bluelupo

Danke für die Erklärung hefee  :) :)

musca

#4
Hallo,

guter Hinweis von hefee, das blinde paarweise Zusammenfügen reicht vermutlich nicht. Denn ich habe in meinem history.log auch Einträge dieser Art (also ohne die folgende "Commandline"):
Start-Date: 2015-01-22  18:11:10
End-Date: 2015-01-22  18:11:11

Start-Date: 2015-01-22  18:11:33
End-Date: 2015-01-22  18:11:33

Ich vermute, dass ich hier den Solver von Aptitude bei der Sicherheitsabfrage "Wollen Sie wirklich?" abgebrochen habe.
Dadurch kann also die paarweise Verkettung aus dem Takt kommen.


Mit perl könnte eine Lösung so aussehen ( Newline nur vor dem Wort Commandline durch Space ersetzen)
perl -0ne 's/\RCommandline/ Commandline/g; print;' /var/log/apt/history.log | grep Commandline



Nach einer kleinen Optimierung entfällt sowohl das Multiline-Reading (-0) als auch noch das anschliessende grep:
perl -ne 'chomp;print$l=(/^Com/)?"$l$_\n":"";$l="$_ "' /var/log/apt/history.log
Durch die Optionen -n wird dabei implizit folgendes ausgeführt:
#!/usr/bin/perl
# concatenate all lines beginning with "Commandline" with the previous line
use strict;
use warnings;  # -w switch

{
  while (<>) {               # The -n switch reads filename arguments or STDIN
    chomp;                   # remove trailing newline
    if (/^Commandline/) {    # actual record does begin with "Commandline"
      print "$last$_\n"      # append previous record with actual record
    } else {                 # not yet found a Commandline!
      print ""               # so print nothing in this case (avoid grep)
    }
    $last = "$_ "            # remember actual record until next loop
  }
}


Okay, den else-Teil läßt man nun weg.
Schließlich kann man die -l Option verwenden, um chomp und print zu vereinfachen.
perl -wlne 'print "$last $_" if (/^Command/); $last=$_' /var/log/apt/history.log
oder
perl -wlne '/^Command/ && print "$last $_"; $last=$_' /var/log/apt/history.log


Die Syntax ist auf einmal wieder recht ähnlich zu awk:
awk '/^Command/ { print last " " $0;} { last=$0; }' /var/log/apt/history.log

oder sed:
sed '/^Com/!{h;d};/^Com/{x;G;s/\n/  /}' </var/log/apt/history.log

[edit: hier noch ein Oneliner in bash:]
while read line; do [[ $line =~ Commandline ]] && printf "%s  %s\n" "$last" "$line"; last="$line"; done < /var/log/apt/history.log


perlish greetings
musca
,,Es irrt der Mensch, solang er strebt."  (Goethe, Faust)