TS: bash grep und Spalten

Hello,

ich habe ein Verzeichnis voller Kontrolldateien. Die enthalten z. B. Zeilen, die mit

input: \t\t<values>  

enthalten.
"\t" steht für Tabulator
"<values>" steht für die Werte

Gesucht wird nach "input:"

Es können mal Leerzeichen hinzukommen, oder die Tabulatoren fehlen.

Ich will nur die Spalten ab den Values haben. Da es viele Tausende Einzeldateien sind, möchte ich die nicht erst runterladen, sondern möglichst durch einen Grep-Befehl in eind Datei schieben und die dann herunterladen.

Leider habe ich vergessen, wie man mit grep nur einen Teil der Zeile (nur die values) ins Ergebnis schreiben kann, oder bringe ich da 'was durcheinander und das ging gar nicht?

awk und sed sind auf den Geräten nicht vorhanden und passen auch nicht mehr in das Systempaket rein.

Glück Auf
Tom vom Berg

--
Es gibt nichts Gutes, außer man tut es!
Das Leben selbst ist der Sinn.
  1. Hallo,

    kenne mich damit zwar nicht aus, aber neugierig wie ich bin fand ich das Hier im Netz, hört sich zumindest leicht verständlich an.

    lg

    1. Hi,

      kenne mich damit zwar nicht aus, aber neugierig wie ich bin fand ich das Hier im Netz, hört sich zumindest leicht verständlich an.

      deine Hilfsbereitschaft in Ehren, aber ich glaube, dass du da Eulen nach Athen trägst. Ich glaube schon, dass Tom prinzipiell weiß, wie man mit grep umgeht.

      @Tom, an einer Stelle bist du aber auf dem Holzweg, glaube ich. Soweit ich weiß, kann das Kommandozeilentool grep nur ganze Zeilen aus der untersuchten Datei ausgeben, abhängig davon, ob ein bestimmtes Suchmuster in der Zeile vorkommt oder nicht.
      Mir ist nicht bekannt, dass man mit grep direkt auf bestimmte Passagen innerhalb der Zeile zugreifen kann.

      IMO müsstest du also in zwei Stufen vorgehen: In der ersten Stufe suchst du alle Dateien raus, die den Suchausdruck "^input:" enthalten; und in der zweiten Stufe fällst du gezielt über diese Dateien her und friemelst die values heraus.

      Schönes Wochenende,
       Martin

      --
      Computer müssen weiblich sein: Eigensinnig, schwer zu durchschauen, immer für Überraschungen gut - aber man möchte sie nicht missen.
  2. ich habe ein Verzeichnis voller Kontrolldateien.

    Ich will nur die Spalten ab den Values haben. Da es viele Tausende Einzeldateien sind, möchte ich die nicht erst runterladen, sondern möglichst durch einen Grep-Befehl in eind Datei schieben und die dann herunterladen.

    awk und sed sind auf den Geräten nicht vorhanden und passen auch nicht mehr in das Systempaket rein.

    Hmmm... Wenn man das so liest, könnte man meinen, Du hättest via awk/sed die Lösung quasi schon parat. Jetzt weiß ich nicht, wie fett die "Kontrolldateien" sind. Aber ein tar.gz dürfte die dramatisch zusammenschrumpfen lassen. Erzeugen, runterladen, Job tun und fertig.

    1. Hello,

      ich habe ein Verzeichnis voller Kontrolldateien.

      Ich will nur die Spalten ab den Values haben. Da es viele Tausende Einzeldateien sind, möchte ich die nicht erst runterladen, sondern möglichst durch einen Grep-Befehl in eind Datei schieben und die dann herunterladen.

      awk und sed sind auf den Geräten nicht vorhanden und passen auch nicht mehr in das Systempaket rein.

      Hmmm... Wenn man das so liest, könnte man meinen, Du hättest via awk/sed die Lösung quasi schon parat. Jetzt weiß ich nicht, wie fett die "Kontrolldateien" sind. Aber ein tar.gz dürfte die dramatisch zusammenschrumpfen lassen. Erzeugen, runterladen, Job tun und fertig.

      Das wird leider meistens zu eng.
      Es sind immer so 10k bis 14k Einzeldateien zu je ca. 6kBytes Größe. Wir benötigen aber nur pro Datei eine halbe Zeile. Das passt dann gerade noch so auf die Kiste drauf.

      Die Originaldateien benötigen leider beim Runterladen zu viel Zeit. Das liegt wohl an dem Dateisystem. Kann man kurzfristig nicht ändern. Die werden zwar auch abgeholt, aber eben nur nach und nach.

      Ich war der Meinung, dass grep das Spaltenfiltern auch könnte, aber da habe ich mich wohl geirrt. Dann muss eben das "input: " auch nutzlos mit runtergeladen werden.

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. Das wird leider meistens zu eng.
        Es sind immer so 10k bis 14k Einzeldateien zu je ca. 6kBytes Größe. Wir benötigen aber nur pro Datei eine halbe Zeile. Das passt dann gerade noch so auf die Kiste drauf.

        Nochmal hmmmm... Also 80MB Textfiles. Das tar.gz wird winzig. Wenn das nicht mehr auf die Kiste passt, hast Du IMHO ein grundsätzliches Problem.

  3. Hi,

    Es können mal Leerzeichen hinzukommen, oder die Tabulatoren fehlen.

    Steht zwischen den Tabs noch anderes Zeug drin?

    Oder hast Du praktisch input:\s+<value>?

    Prinzipiell sind die Unix-Tools ja eher so aufgebaut, daß jedes Tool eine kleine Aufgabe erledigt. Und die einzelnen Tools werden dann je nach Bedarf per Pipe nacheinander auf den Eingabestring losgelassen (und jedes Tool schreibt in die Standardausgabe, die durch die Pipe der Eingabestring des nächsten Tools wird).

    Bei konstanter Tab-Zahl könnte man cut verwenden.

    Bei Dir ist - da Du schreibst, daß die Tabs auch fehlen können, ggf. ein Regex anwendbar - dann wäre awk oder sed geeignet.

    cu,
    Andreas a/k/a MudGuard

    1. Hello Andreas,

      Es können mal Leerzeichen hinzukommen, oder die Tabulatoren fehlen.

      Steht zwischen den Tabs noch anderes Zeug drin?

      Oder hast Du praktisch input:\s+<value>?

      Prinzipiell sind die Unix-Tools ja eher so aufgebaut, daß jedes Tool eine kleine Aufgabe erledigt. Und die einzelnen Tools werden dann je nach Bedarf per Pipe nacheinander auf den Eingabestring losgelassen (und jedes Tool schreibt in die Standardausgabe, die durch die Pipe der Eingabestring des nächsten Tools wird).

      Bei konstanter Tab-Zahl könnte man cut verwenden.

      Bei Dir ist - da Du schreibst, daß die Tabs auch fehlen können, ggf. ein Regex anwendbar - dann wäre awk oder sed geeignet.

      awk und sed sind leider nicht drauf. Und da das System gepackt ist, bekomme ich die auch nicht generell drauf. Ich müsste sie jedes Mal erst ins /tmp hochladen und scharf schalten.

      Aber mit Grep Regex müsste es ggf gehen. Es stehen nur \t oder Spaces zwischen dem Suchbegriff und dem Beginn der Werte. Leider sind das immer unterschiedlich viele.

      Ich hatte das zwar ausprobiert, aber vermutlich eine falsche Syntax benutzt.

      Glück Auf
      Tom vom Berg

      --
      Es gibt nichts Gutes, außer man tut es!
      Das Leben selbst ist der Sinn.
      1. Hello,

        ich konnte noch zusätzlich cut aktivieren.

        Die Ergebnisdatei ist leider immer noch ca. 407k groß. Ich habe vor der Operation nur noch ca. 1,4MB verfügbaren Permanentspeicher zur Verfügung.

        Es gibt im Ergebnis noch reichlich führende und anhängende Leerzeichen pro Zeile. Die würde ich auch noch gerne loswerden.

        Glück Auf
        Tom vom Berg

        --
        Es gibt nichts Gutes, außer man tut es!
        Das Leben selbst ist der Sinn.
        1. Die Ergebnisdatei ist leider immer noch ca. 407k groß. Ich habe vor der Operation nur noch ca. 1,4MB verfügbaren Permanentspeicher zur Verfügung.

          https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/3%2C5%22-Diskette.jpg/1024px-3%2C5%22-Diskette.jpg

          1. Hello,

            Die Ergebnisdatei ist leider immer noch ca. 407k groß. Ich habe vor der Operation nur noch ca. 1,4MB verfügbaren Permanentspeicher zur Verfügung.

            https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/3%2C5%22-Diskette.jpg/1024px-3%2C5%22-Diskette.jpg

            ☆grins☆

            Ja, so kommt man sich da vor. Embedded Devices für 2020 mit Speicherplatz wie im letzten Jahrtausend. Aber ich mach da ja auch schon wieder Sachen, die ursprünglich nicht vorgesehen waren. Da nimmt man eben, was die Anderen übrig lassen ;-)

            Glück Auf
            Tom vom Berg

            --
            Es gibt nichts Gutes, außer man tut es!
            Das Leben selbst ist der Sinn.
        2. Ich konnte noch zusätzlich cut aktivieren.

          Dann wohl so:

          # Erzeugen der Beispiel-Dateien:
          echo -e "input:\tfoo" > 1
          echo -e "input:\tbar" > 2
          
          # Eigentliche Aktion:
          grep -RP '^input:\t' | cut -b 10- | gzip -c > out.txt.gz
          
          # Ergebnis zeigen
          gzip -dc < out.txt.gz
          bar
          foo
          

          Wenn das keine Lösung sein sollte, dann wäre es vieleicht hilfreich, mehr über Dein "embedded System" zu erfahren. Auf manchen gibt es ja auch ausgewachsene Interpreter z.B. für Python. Dann könnte man das auch damit machen.

          1. Gleich mit Transport:

            grep -RP '^input:\t' | cut -b 10- | gzip -c | ssh user@hostname "cat > /tmp/test.txt"
            

            Vorteil: Es wird kein lokaler Speicherplatz benötigt.

            1. Sorry: Gleich mit entpacken auf dem Zielsystem:

              grep -RP '^input:\t' | cut -b 10- | gzip -c | ssh user@hostname "gzip -dc > /tmp/test.txt"
              

              Beachte bitte, dass Du die Kompression nicht brauchst, wenn SSH auf Deinem Device und auf dem Server die Kompression beherrschen und diese konfiguriert ist.

              Stoff:

              • man ssh_config
              • man sshd_config
              1. Hallo,

                grep -RP '^input:\t' | cut -b 10- | gzip -c | ssh user@hostname "gzip -dc > /tmp/test.txt"
                

                Beachte bitte, dass Du die Kompression nicht brauchst, wenn SSH auf Deinem Device und auf dem Server die Kompression beherrschen und diese konfiguriert ist.

                und auch nicht, wenn die Übertragungsbandbreite "billig" im Vergleich zum Speicherbedarf oder zur Rechenleistung ist (was ich bei einem Embedded Device vermuten würde, wenn es nicht gerade via GSM kommuniziert).

                Ciao,
                 Martin

                --
                Computer müssen weiblich sein: Eigensinnig, schwer zu durchschauen, immer für Überraschungen gut - aber man möchte sie nicht missen.
                1. Hello,

                  Hallo,

                  grep -RP '^input:\t' | cut -b 10- | gzip -c | ssh user@hostname "gzip -dc > /tmp/test.txt"
                  

                  Beachte bitte, dass Du die Kompression nicht brauchst, wenn SSH auf Deinem Device und auf dem Server die Kompression beherrschen und diese konfiguriert ist.

                  und auch nicht, wenn die Übertragungsbandbreite "billig" im Vergleich zum Speicherbedarf oder zur Rechenleistung ist (was ich bei einem Embedded Device vermuten würde, wenn es nicht gerade via GSM kommuniziert).

                  Nee, die Devices sollten alle mindestens mit 16M/16M erreichbar sein. Das benötigen sie für ihre Aufgabe ;-)

                  Möglicher Nachteil: die Verbindung kann mal länger abreißen zwischendurch, als es ssh gefällt. Dann muss man es nochmal versuchen.

                  Glück Auf
                  Tom vom Berg

                  --
                  Es gibt nichts Gutes, außer man tut es!
                  Das Leben selbst ist der Sinn.
              2. Hello,

                gute Ideen, deshalb nur kurz:

                • die Pipe benötigt trotzdem Arbeitsspeicher.
                • ssh Push kommt nicht in Frage. Dafür müsste jedes Device (über 1000) einen eigenen Useraccount auf dem Zielhost haben. Ssh Poll benötigt nur den einen Key auf jedem Quell-Device.
                • grep -hP 'input:' * bringt mich trotzdem weiter (die Dateinamen benötige ich nicht)
                • cut -d: war schon hilfreich. Wenn ich jetzt noch die führenden Leerzeichen und Tabs loswerden könnte, blieben noch ca. 35 Bytes pro Eintrag übrig. Das wäre dann schon sehr brauchbar

                Ob gzip schon bei allen Modellen/Versionen zur Verfügung steht, muss ich noch prüfen, aber es würde (ohne die Leerzeichen) vermutlich nicht mehr viel bewirken.

                Glück Auf
                Tom vom Berg

                --
                Es gibt nichts Gutes, außer man tut es!
                Das Leben selbst ist der Sinn.
                1. Hast Du tr?

                  Die Frage nach der eventuell vorhandenen Skriptsprache (eg. python, perl, nodejs) hast Du nicht beantwortet. Das wäre sehr hilfreich.

                  Hier eine äußerst primitive ltrim.sh, die Tabs und Leerzeichen am Beginn jeder Zeile des Inputs "wegtrimmt":

                  #!/bin/bash
                  
                  # $home/bin/ltrim.sh
                  
                  ## USAGE:
                  # echo -e "\t \thallo\n \twelt" | ltrim.sh
                  # program | ltrim.sh
                  # ltrim.sh < file
                  
                  
                  while read str; do
                          e="";
                          i=0;
                          charFound=0;
                          l="${#str}";
                          while [ $i -lt $l ]; do
                                  e=("${str:$i:1}")
                                  if [ 0 -eq $charFound ]; then
                                          if [ " " != "$e" ]; then
                                                  if [ "\t" != "$e" ]; then
                                                          echo -n $e;
                                                          charFound=1;
                                                  fi
                                          fi
                                  else
                                          echo -n $e;
                                  fi
                                  i=$((i+1))
                          done
                          echo "";
                  done
                  
                  
                  1. Was doch manchmal Quotas so ausmachen... die Bash mag sonst Leerzeichen INNERHALB des Strings (der Zeile) nicht ausgeben.

                    Korrektur:

                    #!/bin/bash
                    
                    # $home/bin/ltrim.sh
                    
                    ## USAGE:
                    # echo -e "\t \thallo\n \twelt\n   Hallo Welt" | ltrim.sh
                    # program | ltrim.sh
                    # ltrim.sh < file
                    
                    while read str; do
                            e="";
                            i=0;
                            f=0;
                            l="${#str}";
                            while [ $i -lt $l ]; do
                                    e=("${str:$i:1}");
                                    if [ 0 -eq $f ]; then
                                            if [ " " != "$e" ]; then
                                                    if [ "\t" != "$e" ]; then
                                                            f=1;
                                                            echo -n "$e";
                                                    fi
                                            fi
                                    else
                                            echo -n "$e";
                                    fi
                                    i=$((i+1))
                            done
                            echo "";
                    done
                    
                    1. Bevor Du das selbst rausfindest:

                      Das gezeigte Bash-Skript zum Trimmen braucht für die 1800 Zeilen meines syslogs stolze 3 Sekunden. Mit einer anderen Skriptsprache, die nicht Zeile für Zeile interpretiert und also neu übersetzt, könnte das sehr viel schneller gehen.

                      Das ist auch der Grund, warum ich wiederholt frage, was auf dem System ist.

                2. ssh Push kommt nicht in Frage. Dafür müsste jedes Device (über 1000) einen eigenen Useraccount auf dem Zielhost haben.

                  Nein, muss nicht. Auch ein eigener SSH-Key pro Device in ~/.ssh/authorized_keys wäre möglich.

                  Müsste man sich auf dem Server halt nur eine Infrastruktur schaffen, um die zu verwalten. Aber ich sehe da kein Problem. Ratschlag: Setze auch die Zahl der erlaubten Logins pro User in /etc/sshd_config passend hoch.

                  1. Hello,

                    ssh Push kommt nicht in Frage. Dafür müsste jedes Device (über 1000) einen eigenen Useraccount auf dem Zielhost haben.

                    Nein, muss nicht. Auch ein eigener SSH-Key pro Device in ~/.ssh/authorized_keys wäre möglich.

                    Müsste man sich auf dem Server halt nur eine Infrastruktur schaffen, um die zu verwalten. Aber ich sehe da kein Problem. Ratschlag: Setze auch die Zahl der erlaubten Logins pro User in /etc/sshd_config passend hoch.

                    Wie kann man dann den "User" identifizieren, der zuletzt Sch***** hochgeladen und ausgeführt hat?

                    Nee, nee. Den aktiven Part (den Private Key) verlegt man besser nicht in das Client-Device, zumal man nicht weiß, wer das in die Finger bekommt.

                    Glück Auf
                    Tom vom Berg

                    --
                    Es gibt nichts Gutes, außer man tut es!
                    Das Leben selbst ist der Sinn.
                    1. Wie kann man dann den "User" identifizieren, der zuletzt Sch***** hochgeladen und ausgeführt hat?

                      Tja.

                      journalctl -f zeigt mir:

                      Aug 04 15:48:36 SERVER sshd[11466]: Accepted publickey for $USER from IP port 41752 ssh2: ED25519 SHA256:$HASH
                      

                      Allerdings geht es natürlich auch, dass Du auf dem Server den ssh-client ausführst:

                      ssh user@DEVICE 'ls -l | grep test.php' > output_DEVICE.txt
                      less output_DEVICE.txt
                      
                      -rw-r--r-- 1 user group   126 Apr 21 12:06 test.php
                      
                      1. Hello,

                        ich hab noch eine ganz witzige Trim-Methode gefunden. Einfach ein | xargs hinten anhängen, dann sind die Spaces weg. Leider aber auch das EOL.

                        Glück Auf
                        Tom vom Berg

                        --
                        Es gibt nichts Gutes, außer man tut es!
                        Das Leben selbst ist der Sinn.
                        1. Einfach ein | xargs hinten anhängen, dann sind die Spaces weg. Leider aber auch das EOL.

                          Das könnte man ja beheben...

                          #!/bin/bash
                          
                          # $home/bin/trim.sh
                          
                          ## USAGE:
                          # echo -e "\t \thallo\n \twelt\n   Hallo Welt" | trim.sh
                          # program | trim.sh
                          # trim.sh < file
                          
                          while read str; do
                                  echo -n $str | xargs -d "\n";
                          done
                          

                          Für die selben Daten (1800 Zeilen syslog) braucht das dann aber sogar mehr als 5 Sekunden. Das andere Skript benötigte 3.

                          1. Die bisher schnellste Lösung macht sich zu nutze, dass echo uneingeschlossene Leerzeichen und Tabs einfach ignoriert:

                            #!/bin/bash
                            
                            # $home/bin/trim.sh
                            
                            ## USAGE:
                            # echo -e "\t \thallo\n \twelt\n   Hallo Welt" | trim.sh
                            # program | trim.sh
                            # trim.sh < file
                            
                            while read str; do
                               echo `echo $str`;
                            done
                            

                            Rund 1 Sekunde ...

                            1. Noch sehr viel schneller:

                              #!/bin/bash
                              
                              # $home/bin/trim.sh
                              
                              ## USAGE:
                              # echo -e "\t \thallo\n \twelt\n   Hallo Welt" | trim.sh
                              # program | trim.sh
                              # trim.sh < file
                              
                              while read str; do
                                 echo $str;
                              done
                              

                              0.1 Sekunde für die selben 1800 Zeilen.

                              Das zweite echo mit den Backticks war gar nicht nötig...