Pifler
Anmeldungsdatum: 6. September 2017
Beiträge: 16
|
Hey Leute,
ich bin neu hier im Forum und brauche unbedingt Eure Hilfe!
Folgende Ausgangslage:
Ich benöte ein Programm, welches aus einer Datei doppelte Inhalter herausfindet und die jeweilige Linie ausgibt. Jede Linie sieht wie folgt aus : Name x IP-Adresse x Mac-Adresse
Das "x" steht hier als Trenner und ist in der Datei vorhanden (Datei wurde so zu Beginn von mir aufbereitet). Nun besteht die Inputdatei aus zum Beispiel 100 Einträgen (kann variieren). Gibt es zum Beispiel in der gesamten Datei doppelte IP´s, so sollen die Duplikate ausgegeben werden. Name und Mac-Adresse können jedoch unterschiedliche sein. Ausgabe sollte dann zum Beispiel so aussehen:
PC1 x 192.168.2.1 x 00-80-41-ae-fd-7e
PC2 x 192.168.2.1 x 55-90-23-ea-c9-8a IP-Adresse, die nur einmal vorhanden sind, müssen nicht ausgegeben werden. Mein Ansatz:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 |
#!/bin/bash
#echo "Es stehen folgende Spalten zur Verfügung: "
#echo "Name =\$1, IP-Adresse=\$2, Windows-Domäne=\$3"
#read -p "Bitte geben Sie die gewünschte Sortierspalte an " spalte
#cat KESL10-6-9-2017-IP-Sort.csv
#echo $spalte cat gs00111.txt| tr '\t' '#' |
awk -F# '{print $1 " x " $2 " x " $3 " x " $4 " x " $5 " x " $12}'|
sort -k2 -V >> gstest.txt
i=2
while read line;
do
IP=$(echo $line | cut -d "x" -f 2)
sed -n "3p" gstest.txt | grep $IP
#i=i+1
done <gstest.txt
#rm gstest.txt
|
Vielen Dank im Voraus!
PS: Bin neu was Bash angeht ^^
Moderiert von sebix: Thema in einen passenden Forenbereich verschoben. Bitte beachte die als wichtig markierten Themen („Welche Themen gehören hier her und welche nicht?“) in jedem Forenbereich. Danke.
Bearbeitet von rklm: Codeblock
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Hi Pifler, erstmal herzlich willkommen hier auf dem Forum ! Solch eine Funktion brauchst Du gar nicht selber zu programmieren, die gibt es schon fix und fertig bei den Standardbefehlen. Konkret wäre das uniq , dort kannst Du auch einstellen, was er genau ausgeben soll. Falls die doppelten Zeilen nicht hintereinander stehen, kannst Du sie mit sort vorher sortieren. Der Einfachheit halber kannst Du die beiden Befehle mit einer Pipe verknüpfen. LG, track p.s.: Falls Du wirklich nur das 2. Feld vergleichen willst (und den Rest der Zeile ignorieren), kannst Du das tatsächlich ziemlich einfach mit einem assoziativen Array (und awk ) erledigen: | awk 'test[$2]==1{print} {test[$2]++}' gs00111.txt
|
Die ganzen Vorbereitungen kannst Du Dir dabei sparen ...
|
Pifler
(Themenstarter)
Anmeldungsdatum: 6. September 2017
Beiträge: 16
|
Hallo,
Vielen Dank für die Hilfe!
Besteht die Möchlichkeit einen 'direkt Chat' mit dir zu starten? Hätte noch die ein oder andere Verständnisfrage dies bezüglich 😀
Aber vielen Dank schonmal!
Bearbeitet von redknight: Forensyntax repariert. Bitte verwende keine reserveirten zeichen im Fließtext.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Nee, im Chat sitze ich nicht. Aber stelle Deine Fragen doch einfach hier, dann haben später auch andere was davon ... LG, track
|
Pifler
(Themenstarter)
Anmeldungsdatum: 6. September 2017
Beiträge: 16
|
Hey, also das mit dem uniq habe ich bereits ausprobiert. Auch die Möglichkeit uniq -d wurde getestet. Doch das Problem hierbei war, dass der Befehl uniq nur ganze Zeilen einliest und miteinander vergleicht. Es wird nicht nur Spalte zwei ( in meinem Beispiel die IP´s) miteinander vergleichen. Somit werden nur exakt doppelte Zeilen ausgegeben. Die Input-Datei ist normalerweise größer und besitzt mehrere Spalten als ich benötige und besitzt zwischen jeder Spalte einen "Tab". Daher bereite ich diese Datei einwenig auf und suche mir nur notwendige Spalten aus und trenne diese mit einem "x". Nun zu track´s Quellcode.
awk 'test[$2]==1{print} {test[$2]++}' gs00111.txt Ich muss sagen, dass das hier mein erstes Script ist und mir diese ganzen regular expressions nicht so geläufig sind 😀 Daher verstehe ich noch nicht so ganz, was dieser Quellcodeschnipsel mir sagen soll ^^ Eine kleine Erklärung wäre eventuell cool 😀 Hier nochmal mein neuer Quellcode mit Kommentierung: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #!/bin/bash
echo "Es stehen folgende Spalten zur Verfügung: "
echo "Name =\$1, IP-Adresse=\$2, Windows-Domäne=\$3"
read -p "Bitte geben Sie die gewünschte Sortierspalte an " spalte
//Eingabe $1 oder $2 oder $3
cat großeInputDatei.csv |
tr '\t' '#' |
awk -F# '{print $1 " x " $2 " x " $3 " x " $4 " x " $5 " x " $12}'|
sort -k2 -V >> gstest.txt
// Es wurde nach IP-Adressen sortiert und nur wichtige Zeilen wurden aus der großen Input-Datei herausgezogen und aufbereitet.
// Hier müsste jedoch beim sort das -k2 variabel gemacht werden. Je nach der obigen Eingabe. (doch wie? Einfach sort -kspalte?)
while read line;
do
IP=$(echo $line | cut -d "x" -f 2)
sed -n "3p" gstest.txt | grep $IP
done <gstest.txt
//Anstatt der while schleife einfach den part von track? (awk 'test[$2]==1{print} {test[$2]++}' gs00111.txt)
rm gstest.txt
//Damit beim nächsten Aufruf einer neuen Input-Datei nur die aktuellen IP´s vorhanden sind
|
Noch ein Beispiel zur Verdeutlichung. Wir haben folgende Zeilen in der aufbereiteten Input Datei: PC1 x 192.168.2.2 x 00-80-41-ae-fd-7e
PC9 x 192.168.2.3 x 55-90-23-ea-c9-8a
PC5 x 192.168.2.3 x 60-80-32-fa-9a-ad
PC2 x 192.168.2.4 x 55-00-23-ha-c9-8a Nun sollte mein Script folgendes ausgeben: PC9 x 192.168.2.3 x 55-90-23-ea-c9-8a
PC5 x 192.168.2.3 x 60-80-32-fa-9a-ad Wäre super, wenn mir jemand helfen könnte wäre echt wichtig für mich 😀 Wer einwenig "Anregung" für seine Mühen haben muss, kann sich gerne auch bei mir mit einer PN melden 😛 Lässt sich über alles reden 😀 Bin wirklich unerfahren auf dem Gebiet 😀 Vielen Dank!
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
Verstehe ich das richtig, Du willst alle Zeilen ausgeben, die eine IP-Adresse enthalten, die mehr als ein mal vorkommt und die Ausgabe außerdem so sortieren, dass Zeilen mit gleichen IP-Adressen zusammen ausgegeben werden? Mein Ansatz für die Ausgabe wäre etwas anders: ich würde dann nur jeweils die IP ein Mal ausgeben und dann eingerückt alle variablen Informationen (also Name und MAC). Das sähe dann so aus (etwas geraten aus Deinem ursprünglichen Skript): 1
2
3
4
5
6
7
8
9
10
11
12
13
14 | awk '-F\t' '
{
s = "\n " $1 " x " $3 " x " $4 " x " $5 " x " $12
++count[$2]
text[$2] = text[$2] s
}
END {
for (i in count) {
if ( count[i] > 1 )
printf "%s%s\n", i, text[i]
}
}
' großeInputDatei.csv
|
awk kann einen Tab als Feldtrenner nutzen (s.o.).
|
Pifler
(Themenstarter)
Anmeldungsdatum: 6. September 2017
Beiträge: 16
|
Also es sollen alle Zeilen ausgegeben werden, die eine doppelte IP-Adresse enthalten. Sortieren bei der Ausgabe wäre natürlich toll, ist aber jedoch nicht unbedingt nötig.
Dein Quellcode ist nun das gesamte Script? Wäre es eventuell möglich, dass du den gesamten Quellcode ein wenig kommentierst? ^^ Ich weiß, dass das sehr komisch klingt 😀 Aber was jetzt zum Beispiel dieses speziell in diesem Fall macht, ist mir nicht ganz bewusst. ^^ PS: Beschäftige mich seit kurzem mit der Thematik 😀 Also bin kein Sktiptkönig wie ihr 😛 VIELEN DANK!
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
Pifler schrieb: Also es sollen alle Zeilen ausgegeben werden, die eine doppelte IP-Adresse enthalten. Sortieren bei der Ausgabe wäre natürlich toll, ist aber jedoch nicht unbedingt nötig.
Aber es hilft doch der Übersicht, wenn man alle Infos zusammen hat.
Dein Quellcode ist nun das gesamte Script?
Ja. Soweit ich das sehe schon. 😬
Wäre es eventuell möglich, dass du den gesamten Quellcode ein wenig kommentierst?
Ja, s.u.
^^ Ich weiß, dass das sehr komisch klingt 😀 Aber was jetzt zum Beispiel dieses speziell in diesem Fall macht, ist mir nicht ganz bewusst. ^^
Er erhöht den numerischen Wert, der unter dem Schlüssel $2 (also das zweite Feld der aktuellen Eingabezeile) um 1. Falls es keinen Wert gibt, wird automatisch 0 angenommen. Du kannst das selbst ausprobieren: | $ awk 'BEGIN {print "vorher:" map["x"]; ++map["x"]; print "nachher: " map["x"]; ++map["x"]; print "nochmal: " map["x"]}'
vorher:
nachher: 1
nochmal: 2
|
PS: Beschäftige mich seit kurzem mit der Thematik 😀 Also bin kein Sktiptkönig wie ihr 😛
Kommt noch!
VIELEN DANK!
Danichfür. 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | awk '-F\t' '
{ # For each input line
# create an output entry for this IP from current input line
s = "\n " $1 " x " $3 " x " $4 " x " $5 " x " $12
# increment counter for this IP
++count[$2]
# Create the combined output text for all occurrences of this IP
# by appending. Initial value from the associative array is empty
# string.
text[$2] = text[$2] s
}
END { # After the whole input is read
for (i in count) {
# for each IP output if more than one occurrence
if ( count[i] > 1 )
printf "%s%s\n", i, text[i]
}
}
' großeInputDatei.csv
|
Eine Anmerkung noch: der Speicherverbrauch dieser Lösung ist recht hoch, da erst die gesamte Datei gelesen werden muss, bevor mit der Ausgabe angefangen wird. Man kann das auch anders lösen, indem man nur zählt und dann während man liest Ausgaben macht. Das wird dann ein wenig kniffliger von der Logik her.
|
Pifler
(Themenstarter)
Anmeldungsdatum: 6. September 2017
Beiträge: 16
|
Aber in deinem Script gehen wir ja davon aus, dass die Datei unsortiert ist oder? Weil du unten die großeInputDatei.csv verwendest. Müsste man vorher nicht eine sortierte Datei anlegen, die später wieder gelöscht wird? Vielen Dank schonmal!
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Pifler schrieb: Nun zu track´s Quellcode.
awk 'test[$2]==1{print} {test[$2]++}' gs00111.txt Ich muss sagen, dass das hier mein erstes Script ist und mir diese ganzen regular expressions nicht so geläufig sind 😀 Daher verstehe ich noch nicht so ganz, was dieser Quellcodeschnipsel mir sagen soll ^^ Eine kleine Erklärung wäre eventuell cool 😀
Ok, ich hatte deine Frage zunächst so verstanden, dass Du nur mal die doppelt vorhandene IP haben wolltest. Und genau das macht dieses Mini-Skript. (Genau genommen wird genau die Zeile mit der 1.Wiederholung ausgegeben. → Probier das mal bitte selber aus !) Das war also die Aufgabe, wie ich sie verstanden hatte. Nun aber zur Vorgehensweise: (die taugt nämlich auch sehr gut für andere, ähnliche Aufgaben !)
Teil: wenn der Test-Zähler für das momentane Muster =1 ist, wird die Zeile gedruckt. (→ awk Bedingung { Befehle } ) Teil: erhöhe den Testzähler für das aktuelle Muster um 1
Der Trick ist, dass das Suchmuster (= die IP bei Dir) als Array-Index verwendet wird. Dadurch wird die Zuordnung automatisch korrekt erledigt, ohne weiteren Aufwand. (→ deshalb "assioziatives" Array) Wenn Du jetzt wirklich alle Zeilen mit der wiederholten IP brauchst, müsst man da natürlich noch etwas drum herum bauen, das diese Zeilen speichert, und am Ende ausgibt. Die nette Einführung in awk gibt es derzeit nur noch aus dem Archiv. LG, track
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
Pifler schrieb: Aber in deinem Script gehen wir ja davon aus, dass die Datei unsortiert ist oder? Weil du unten die großeInputDatei.csv verwendest.
Dem Skript ist es egal, ob die Eingabe sortiert ist oder nicht.
Müsste man vorher nicht eine sortierte Datei anlegen, die später wieder gelöscht wird?
Nein. Und selbst wenn man Sortierung benötigte, könnte man die sortierte Ausgabe in eine Pipe schicken und kommt ohne lästigen Platten-IO und eventuell übrig bleibende temporäre Dateien aus.
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12832
|
Wenn Du eine Lösung ohne awk suchst, dann kann man das so machen: | #!/bin/bash
fgrep -wf <(cut -f 2 großeInputDatei.csv | sort | uniq -d) großeInputDatei.csv
|
Das ist nicht ganz so treffsicher wie die Lösung mit awk und die Eingabedatei muss zwei Mal gelesen werden, aber sollte auch ausreichen. Die Ausgabe ist hier allerdings nicht sortiert. Das könntest Du dann so machen: | #!/bin/bash
fgrep -wf <(cut -f 2 großeInputDatei.csv | sort | uniq -d) großeInputDatei.csv | sort -st $'\t' -k 2
|
|
seahawk1986
Anmeldungsdatum: 27. Oktober 2006
Beiträge: 11180
Wohnort: München
|
Oder als Bash-Skript, das die Datei nur einmal liest, sortiert und dann zeilenweise abarbeitet:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | #!/bin/bash
column=3
separator=' '
function print_lines {
[ ${#lines_with_same_ip[@]} -gt 1 ] &&
printf '%s\n' "${lines_with_same_ip[@]}"
}
while read -r line; do
ip="$(cut -d "$separator" -f $column <<< "$line")"
if [ "$ip" != "$last_ip" ]; then
print_lines
lines_with_same_ip=()
fi
lines_with_same_ip+=("$line")
last_ip="$ip"
done < <(sort -t "$separator" -k $column test.data)
print_lines
|
|
Pifler
(Themenstarter)
Anmeldungsdatum: 6. September 2017
Beiträge: 16
|
Vielen Dank für die ganzen Antworten! Ich habe mich in den letzten Tage ein wenig mit den Vorschlägen auseinandergesetzt. Das ist mein aktueller Stand: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | #!/bin/bash
read -p "Bitte Übergeben Sie eine Datei aus Kaspersky Security Center (CSV)?" inputdatei
echo "Name IP-Adresse Windows Domäne Datenbakversion Einschaltzeit ..."
# Header werden weggeschnitten
sed -ie '/Name/d' $inputdatei
echo "Es stehen folgende Spalten zur Verfügung: "
echo "Name =1, IP-Adresse=2, Windows-Domäne=3"
read -p "Bitte geben Sie die gewünschte Suchspalte an " spalte
awk '-F\t' '
{ # Fuer jede Eingabelinie
# wird eine Ausgabelinie für die IP der aktuellen Eingabelinie
hilf = "\n " $1 " x " $2 " x " $3 " x " $4 " x " $5 " x " $12
# Inkrementierung des Zaehlers fuer den aktuelle Suchwert
++count[$'$spalte']
# Erstellt einen kombinierten Ausgabetext für alle Vorkommnisse der IP/MAC/Name
# Verwendung eines assoziativen Arrays
text[$'$spalte'] = text[$'$spalte'] hilf } END { # Nachdem die gesamte Datei eingelesen wurde
for (i in count) {
# Fuer jede IP-/MAC-Ausgabe, falls mehrere Vorhanden sind
if ( count[i] > 1 )
printf "\n%s%s\n", i, text[i]
}
}
' $inputdatei
|
Akteulles Problem :
Bei der ersten Spalte handelt es sich um URL´s. Zum Beispiel:
facebook.de
youtube.de
ubuntuusers.de Zudem gibt es aber leider auch URL´s, die genauso aufgebaut sind wie oben die, nur mit Tilden etc. danach:
facebook.de~~
facebook.de
youtube.de
ubuntuusers.de Wenn der Benutzer jedoch nach Duplikaten in Feld eins sucht, sollte alles nach dem .de nicht beachtet werden. Das bedeutet, dass beide "Facebook-Zeilen" ausgegeben werden sollten.
Meine Idee war es, den Teil auszuschneiden. Jedoch ist nicht jede URL gleichlang.
Also gibt es eine Möglichkeit (also es wird bestimmt etwas geben 😀 ) wie man den Part vor der Tilde beachten und vergleichen kann? Alle URL's enden auf ".de". Das war bislang meine Idee:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 | #!/bin/bash
read -p "Bitte Übergeben Sie eine Datei aus Kaspersky Security Center (CSV)?" inputdatei
echo "Name IP-Adresse Windows Domäne Datenbakversion Einschaltzeit ..."
# Header werden weggeschnitten
sed -ie '/Name/d' $inputdatei
echo "Es stehen folgende Spalten zur Verfügung: "
echo "Name =1, IP-Adresse=2, Windows-Domäne=3"
read -p "Bitte geben Sie die gewünschte Suchspalte an " spalte
if ($spalte == 1) then
awk '-F\t' '{
hilf = "\n " $1 " x " $2 " x " $3 " x " $4 " x " $5 " x " $12
$spalte= cut --character 31 [$'$spalte']
++count[$'$spalte']
text[$'$spalte'] = text[$'$spalte'] hilf
}
}
END { # Nachdem die gesamte Datei eingelesen wurde
for (i in count) {
# Fuer jede IP-/MAC-Ausgabe, falls mehrere Vorhanden sind
if ( count[i] > 1 )
printf "\n%s%s\n", i, text[i]
}
}
' $inputdatei
else
awk '-F\t' '
{ # Fuer jede Eingabelinie
# wird eine Ausgabelinie für die IP der aktuellen Eingabelinie
hilf = "\n " $1 " x " $2 " x " $3 " x " $4 " x " $5 " x " $12
# Inkrementierung des Zaehlers fuer den aktuelle Suchwert
++count[$'$spalte']
# Erstellt einen kombinierten Ausgabetext für alle Vorkommnisse der IP/MAC/Name
# Verwendung eines assoziativen Arrays
text[$'$spalte'] = text[$'$spalte'] hilf } END { # Nachdem die gesamte Datei eingelesen wurde
for (i in count) {
# Fuer jede IP-/MAC-Ausgabe, falls mehrere Vorhanden sind
if ( count[i] > 1 )
printf "\n%s%s\n", i, text[i]
}
}
' $inputdatei
fi
|
Funktioniert jedoch noch nicht so ganz 😀 Probleme mit der IF-Verzweigung. Zudem ist es glaube ich auch nicht sinnvoll, dass der Quellcode doppelt benutzt wird.
|
track
Anmeldungsdatum: 26. Juni 2008
Beiträge: 7174
Wohnort: Wolfen (S-A)
|
Ehrlich gesagt, diese wilde Mischung aus Shellskript und awk finde ich ziemlich gruselig ... Man könnte es sehr viel sauberer (und damit natürlich weniger fehleranfällig !) hinbekommen, wenn man sich für eine der beiden Varianten entscheiden würde, und die dann durchzieht. Robert hatte Dir oben ja schon ein reines awk -Skript vorgeschlagen, darauf könntest Du sehr gut weiter bauen. Aber vor allem wäre es sehr wichtig (bevor Du anfängst zu skripten !!), zuerst mal zu notieren, was Du überhaupt machen willst, und dann erst zu überlegen, welche Zwischenschritte dafür sinnvoll sind. Das 2. Problem ist, dass Du zwar Dein Skript hier postest, aber keine Test-Daten dazu. Denn können wir zwar freundlich auf deinen Code gucken, aber Fehler finden können wir nicht, weil nicht mal klar ist, wie Deine ursprünglichen Daten überhaupt aussehen. (Deine Beispiele sind mal so, mal so ... z.B.: steht dir URL jetzt in der 1. oder der 2. Spalte ??) Wenn Du dazu etwas mal mehr verrätst, können wir Dir sicherlich etwas gezielter weiterhelfen. LG, track
Edit: Mit den Daten von oben im Strang habe ich mal was für awk gebastelt, ganz ohne Shellbefehle: (sind nur ca. 12 awk -Programmzeilen) track@track:~$ cat gs00111.csv
Name IP-Adresse Windows-Domäne
PC1 facebook.de~~ 00-80-41-ae-fd-7e
PC9 facebook.de 55-90-23-ea-c9-8a
PC5 youtube.de 60-80-32-fa-9a-ad
PC2 ubuntuusers.de 55-00-23-ha-c9-8a
track@track:~$ awk -F"\t" '
NR==1 { print " Welche Spalte bitte ?"; # <- Abfrage bei der 1. Zeile
OFS=FS;
print "1: " $1, "2: " $2, "3: " $3;
getline such < "/dev/stdin"; # <- lies von der Tastatur
getline } # <- lies die 2. Zeile ein
{ if(such==2) ind=gensub("[^[:alpha:]]*$","","",$such); # <- ggf. die Zeichen hinter der URL wegfiltern
else ind=$such;
block[ind]=block[ind] $0 "\n";
anzahl[ind]++; }
END { for(x in anzahl)
if(anzahl[x] > 1) printf "%s", block[x] }' gs00111.csv
Welche Spalte bitte ?
1: Name 2: IP-Adresse 3: Windows-Domäne
2
PC1 facebook.de~~ 00-80-41-ae-fd-7e
PC9 facebook.de 55-90-23-ea-c9-8a Von der Logik her sammle ich ab Zeile 2 alle Zeilen nach der Suchspalte in ein (assioziatives) Array von Blöcken, und zähle jeweils mit, wie viele Einträge im Block liegen. Die verwendeten Befehle kannst Du direkt in man awk finden und nachlesen.
|