PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : awk - Problem



FireF
15-04-2008, 13:54
Hallo,
ich müsste aus Dateien einige Strings ausschneiden, scheitere aber am "awk".

Ich habe Dateien mit X-Zeilen (immer unterschiedlich) die so aufgebaut sind:

Item1=ABC_1_3_ah
Item2=ABD_4_4_hk
Item3=ABH_t_z_33
.
.
Item380=XBK_5_6_tt

Aus dem letzten "Item" in dieser Datei soll die Zahl ausgeschnitten werden, in diesem Beispiel also die reine "380".

Außerdem müsste ich die ItemX-Zeilen ausschneiden das ich dieses Ergebnis bekäme, müsste also "Item*=" weggeschnitten und die _ mit einem Leerschritt ersetzt werden:

ABC 1 3 ah
ABD 4 4 hk
ABH t z 33
.
.
XBK 5 6 tt

Den "_" sollte ich ja mit | tr "_" " " wegbekommen, aber vom Rest habe ich keine Ahnung.
Kann mir jemand erklären wie ich das hinbekommen könnte?

Vielen Dank.

Gruß,
Markus

Detrius
15-04-2008, 14:49
grep Item foo.txt | tail -n 1 | sed 's/Item[0-9]*=//'

Dann brauchst Du nur noch die Unterstriche ersetzen und das hast Du ja schon. ;)

FireF
15-04-2008, 15:35
Danke für deine Antwort.

Geht noch nicht ganz.

Mein letzter Eintrag im file.ini lautet: Item385=0

Script:
grep -i item file.ini | tail -n 1 | sed 's/Item[0-9]*=//'
Resultat:
0

Ich bräuchte aber die 385!

Script:
grep -i item file.ini | tail -n 1 | sed 's/Item//'
Resultat:
385=0

Wie kann ich jetzt aber das "=" Zeichen und was dahinter kommt noch abschneiden ?

Gruß,
Markus

Detrius
15-04-2008, 15:54
Du willst nur die Nummer haben? Dann

grep Item foo.txt | tail -n 1 | sed 's/Item//' | sed 's/=.*//'

FireF
15-04-2008, 16:20
PERFEKT ... funktioniert wunderbar ... DANKE DIR :)

FireF
15-04-2008, 16:36
Einen hätte ich noch:

ZAHL1=`grep -i item file.ini | tail -n 1 | sed 's/Item//' | sed 's/=.*//'`
ZAHL2=`expr $ZAHL1 + 1`
echo "$ZAHL2"

awk 'NR<=$ZAHL2 {print $0}' file.ini

Im letzten awk setzte er die Zahl aus der Variable $ZAHL2 nicht um, die wird komplett ignoriert und die komplette Datei ausgegeben, muss ich die anders einsetzen?

Gruß,
Markus

BLUESCREEN3D
15-04-2008, 17:37
In ' werden von der Shell keine Variablen ersetzt. Du musst statt ' einfach " nehmen.

FireF
16-04-2008, 09:48
Ohje, sollte vielleicht mal nen SCRIPT-Kurs machen ...:-(

Klappt wunderbar, DANKE DIR.

Markus

jan61
17-04-2008, 19:49
Moin,


Einen hätte ich noch:
ZAHL1=`grep -i item file.ini | tail -n 1 | sed 's/Item//' | sed 's/=.*//'`
ZAHL2=`expr $ZAHL1 + 1`
echo "$ZAHL2"
awk 'NR<=$ZAHL2 {print $0}' file.ini


da hätte ich auch noch einen ;-)
Mach dem System das Leben nicht so schwer - da kannst Du einige Prozesse einsparen.

1. Die beiden sed's kannst Du zusammenlegen:
sed 's/^Item\([0-9]\+\)=.*/\1/'Damit wird der Teil zwischen "Item" und dem "=" (gruppiert durch die Klammern) in \1 gespeichert und die ganze matchende Zeile eben durch diesen Puffer ersetzt.

2. Den grep kannst Du einsparen (die -i-Option ist hier übrigens sinnlos, der sed arbeitet ja auch nicht case-insensitiv):
sed -n '/^Item[0-9]\+=.*/s/^Item\([0-9]\+\)=.*/\1/p'Dadurch wird sed stillgelegt (-n Option), schlägt nur an, wenn das Suchmuster zutrifft und schreibt dann explizit raus (/p-Modifier). Den tail kannst Du auch hinten anbammeln:
sed -n '/^Item.\+=.*/s/^Item\([^=]\+\)=.*/\1/p' | tail -13. Der awk ist zum Kürzen von Dateien ein wenig oversized - da gibts einfachere Mittel:

head -n $ZAHL2 file.iniSo, damit haben wir eine etwas ressourcenschonendere Variante wie folgt:

ZAHL=`sed -n '/^Item[0-9]\+=.*/s/^Item\([0-9]\+\)=.*/\1/p' file.ini | tail -1`
test -z "$ZAHL" && exit 1
head -n `expr $ZAHL + 1` file.iniWie Du feststellen wirst, habe ich das Suchmuster etwas angepasst. Das ^ am Anfang stellt sicher, dass "Item" am Anfang der Zeile steht, der reguläre Ausdruck "[0-9]\+" gewährleistet, dass Du tatsächlich eine Zahl in $ZAHL hast. Zum Abfangen eines Fehlers ist die test-Zeile da.

Jan

EDIT: Die Ergänzung durch "^" halte ich deshalb für wichtig, weil ja in Ini-Dateien mit ";Item123=..." Einträge im Normalfall auskommentiert werden - die willst Du wahrscheinlich nicht berücksichtigen.

Nochmal ich: Man muss dem sed natürlich auch eine Datei zum Fraß vorwerfen, hatte ich vergessen.

jan61
17-04-2008, 20:53
Moin,


In ' werden von der Shell keine Variablen ersetzt. Du musst statt ' einfach " nehmen.

das ist aber gefährlich - und gibt Probleme:

jan@jack:~/tmp/awk_falle> cat awk_falle.sh
#! /bin/bash
export ZAHL=20
awk " NR<=$ZAHL { print $0 } " file.ini
jan@jack:~/tmp/awk_falle> ./awk_falle.sh
awk: Kommandozeile:1: NR<=20 { print ./awk_falle.sh }
awk: Kommandozeile:1: ^ syntax error
awk: Kommandozeile:1: NR<=20 { print ./awk_falle.sh }
awk: Kommandozeile:1: ^ Nicht-beendeter Regulärer Ausdruck
awk: Kommandozeile:2: (END OF FILE)
awk: Kommandozeile:2: syntax error
jan@jack:~/tmp/awk_falle> cat awk_falle_entkommen.sh
#! /bin/bash
export ZAHL=20
awk -v zahl=$ZAHL ' NR<=zahl { print $0 } ' file.ini
jan@jack:~/tmp/awk_falle> ./awk_falle_entkommen.sh
Item1=a_b_c
Item2=33
Item3=4544
Item55=A
letzte Zeile, die geschrieben werden soll
die Zeile soll nicht mehr auftauchen
Was ist das Problem? Ganz einfach - $0 ist in jedem Shell-Script der Progammname und wird natürlich durch die fleißige Shell genauso wie $ZAHL ersetzt, bevor der awk überhaupt zum Zug kommt.

@FireF: Und das funktioniert bei Dir??? *verwirrt guck*

Besser ist die -v Option des awk, die Shellvariablen an awk-Variablen übergibt.

Ein anderes Problem habe ich jetzt erst so richtig mitgekriegt (siehst Du oben in der Ausgabe). In meinem vorigen Posting habe ich reine Syntax-Kosmetik betrieben, aber jetzt bin ich auf die semantische Fußnote gestoßen ;-) Du willst einfach nur alle Zeilen bis zum letzten "Item123=..." plus der nächsten Zeile ausgeben, oder irre ich mich da?

Dann geht Deine Logik natürlich baden, wenn die Item-Zeilen nicht fortlaufend durchnummeriert sind - und nebenbei kann man diese Aufgabe viel einfacher lösen:

ZAHL=`sed -n '/^Item[0-9]\+=.*/=' file.ini | tail -1`
test -z "$ZAHL" && exit 1
head -n `expr $ZAHL + 1` file.iniDer Modifier "/=" im sed macht die Arbeit - wie vorher wird dem sed ein Nuckel in den Mund gesteckt (-n), damit er nicht losbrüllt und mit dem Modifier wird bei einer passenden Zeile die Zeilennummer (nicht der Inhalt) ausgespuckt. Davon noch die letzte Ausgabe - schon haben wir die letzte passende Zeilennummer. Völlig unabhängig davon, ob da auskommentierte Zeilen sind, Lücken in den Nummern auftauchen usw. Sollte eigentlich (wenn ich Dich nicht völlig falsch verstanden habe) wesentlich betriebssicherer sein.

Und so sieht es aus:
jan@jack:~/tmp/awk_falle> ZAHL=`sed -n '/^Item[0-9]\+=.*/=' file.ini | tail -1`
jan@jack:~/tmp/awk_falle> head -n `expr $ZAHL + 1` file.ini
Item1=a_b_c
Item2=33
Item3=4544
Item55=A
letzte Zeile, die geschrieben werden soll
Jan