bash grep und Spalten
TS
- bash-script
- linux
- webserver
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
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
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
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.
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
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.
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
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
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
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.
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.
☆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
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.
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.
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:
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
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
Hello,
gute Ideen, deshalb nur kurz:
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 brauchbarOb 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
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
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
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.
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.
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
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
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
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.
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 ...
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...