PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : bash – Inhalte aus Datei in Variablen?


aths
2010-11-22, 10:05:48
Ich will eine Konfigurationsdatei auslesen, die sowohl Strings als auch Zahlen enthält.

Unterstützt bash Stringvariablen? Wie wandelt man Zahlen und Strings hin- und zurück?

Die Config-Datei sieht ungefähr so aus:

Anzahl: 3
Name: Produkt

Ich müsste also die Datei darauf prüfen, ob eine Zeile mit "Anzahl: " anfängt und den Rest, also die 3, als Zahl in eine Variable bringen. Von den Befehlen "grep" und "cat" etc. habe ich schon mal gehört, aber die man-Seiten dazu erschlagen mich eher mit der Optionsvielfalt.

Nun nehmen wir an eine Zeile lautet:

Preis: 200 Euro

Dann müsste ich nachdem ich nach "Preis:" gefiltert und das vorne weggeschnitten haben, auch das "Euro", also die letzten 4 Zeichen, hinten wegschneiden. Wie macht man das?

Mark
2010-11-22, 10:26:34
Also wegschneiden mit cut müsste so gehen:

cut -d' ' -f1

Das müsste das erste Wort wegschneiden. Notfalls das Ergenis nochmal mittels einer Pipe "|" durch weitere Befehle jagen. Gibt sicher ne schönere Lösung, ich bin da auch noch relativ neu drin.

Limit
2010-11-22, 11:20:23
Ich würde reguläre Ausdrücke benutzen.

Für das "Preis"-Beispiel wäre die Lösung damit z.B.

cat input.txt | sed -r "s/Preis: ([0-9]+) Euro/\1/"

aths
2010-11-22, 11:31:55
Limit,

mit diesem Befehl erhalte ich die Zahl auf der Konsole. Wie bekomme ich den Inhalt in eine Variable?

Bei sed scheint das zwischen [] den erlaubten, zu filternden Zeichensatz anzugeben. Was bedeutet /\1/?

Rolsch
2010-11-22, 11:50:51
Nimm awk, damit gehts einfach. Das Beispiel unten ist vermutlich nicht lauffähig, habe ewig nichts mehr mit Shell Programmierung gemacht und auch keine Möglichkeit zu testen. Es sollte Dir aber einige Anhaltspunkte geben wie du vorgehen kannst.

ZEILEN=`grep Anzahl $INPUT | awk '{print $2}'` #sucht nach Anzahl und schreibt Wert der 2 Spalte in die Variable $ZEILEN


grep Preis $INPUT | awk '{print $2}' #listet alle preise in Spalte 2

Baalzamon
2010-11-22, 11:52:03
[...]
mit diesem Befehl erhalte ich die Zahl auf der Konsole. Wie bekomme ich den Inhalt in eine Variable?

variablename=$(Befehl)

oder

variablename=`Befehl`

Sehr hilfreich: Advanced Bash Scripting Guide (http://tldp.org/LDP/abs/html/)

aths
2010-11-22, 12:48:19
variablename=$(Befehl)

oder

variablename=`Befehl`

Sehr hilfreich: Advanced Bash Scripting Guide (http://tldp.org/LDP/abs/html/)Danke. Das case-Konstrukt scheint sehr nützlich. Endlich keine endlosen If-Listen mehr.

aths
2010-11-25, 11:05:01
Ich würde reguläre Ausdrücke benutzen.

Für das "Preis"-Beispiel wäre die Lösung damit z.B.

cat input.txt | sed -r "s/Preis: ([0-9]+) Euro/\1/"Das funktioniert offenbar nur, wenn dies die erste Zeile in der Datei ist. Wie bekommt man es hin, dass es funktioniert wenn es in irgendeiner anderen Zeile in der Textdatei steht?

hasufell
2010-11-25, 11:29:16
Das funktioniert offenbar nur, wenn dies die erste Zeile in der Datei ist. Wie bekommt man es hin, dass es funktioniert wenn es in irgendeiner anderen Zeile in der Textdatei steht?
startest du vorher einen Prozess? eventuell bleibt dein skript stehen, weil du einen Prozess ausführst, den du parallel laufen haben willst?

http://de.linwiki.org/wiki/Linuxfibel_-_Nutzerkommandos_-_Prozesssteuerung

mit "&" am Ende eines Kommandos wird dasselbe im Hintergrund ausgeführt.
mit "nohup" wird es von der aktiven Shell abgekoppelt und läuft unabhängig von dieser weiter.

PHuV
2010-11-25, 14:44:29
Nimm awk, damit gehts einfach. Das Beispiel unten ist vermutlich nicht lauffähig, habe ewig nichts mehr mit Shell Programmierung gemacht und auch keine Möglichkeit zu testen. Es sollte Dir aber einige Anhaltspunkte geben wie du vorgehen kannst.

ZEILEN=`grep Anzahl $INPUT | awk '{print $2}'` #sucht nach Anzahl und schreibt Wert der 2 Spalte in die Variable $ZEILEN


grep Preis $INPUT | awk '{print $2}' #listet alle preise in Spalte 2

Wenn Du große Konfigurationsdateien hast, wird das u.U. sehr langsam.

Eine schnelle Form ist das einlesen mit while read und case:

while read param wert
do
case param in
Anzahl:) var=$wert;;
Name:) echo "nix";;
*) echo "irgendwas"
esac
done < Konfigurationsdatei


Mit

IFS="Zeichen"
export IFS

kann man den FileSeparator setzen bzw ändern, default ist immer Tab/Blank.

Übrigens: mit typeset -L|R kann man Zahlen definieren (linksbündig mit führenden Nullen, rechtsbündig ohne Nullen etc.) Dadurch, daß while read und typeset buildin-Kommandos der bash sind, geht die Verarbeitung wesentlich schneller als so grep, sed und awk-Zeugs, was jedesmal einen neuen Prozess öffnet und damit sehr viel Zeit braucht. Mit typeset -a kann man auch Variablen definieren.

aths
2010-11-25, 19:01:30
Leider habe ich morgen nicht richtig Zeit, alles zu testen. Ab Montag werde ich mal versuchen, das irgendwie hinzukriegen und wenn dann noch Fragen auftauchen, melde ich mich wieder.

aths
2010-11-26, 13:38:35
startest du vorher einen Prozess? eventuell bleibt dein skript stehen, weil du einen Prozess ausführst, den du parallel laufen haben willst?Nee, mein Fehler war dass ich das cat nur ein mal aufrief, ich das aber natürlich in einer Schleife machen muss :D

Im Moment versuche ich eine Schleife zu programmieren die so lange durchloopt bis die Datei am Ende ist. Hat ja jemand eine Idee wie man das macht, ohne dass ich auf ein spezielles Codewort in der Datei angewiesen bin die das Ende markiert?

Baalzamon
2010-11-26, 13:49:39
Du kannst dir mit

cat dateiname | wc -l

die Anzahl der Zeilen ausgeben lassen. Eine Schleife mit einer Zählvariable sollte dann ein einfaches sein. =)

aths
2010-11-26, 14:06:10
Danke, damit bekomme ich die Zeilenanzahl. Allerdings raffe ich das noch nicht: cat input.txt | sed -r "s/Preis: ([0-9]+) Euro/\1/"

Liest das immer nur eine Zeile? Oder wie bekomme ich Zeile für Zeile vernünftig eingelesen? Mit diesem Codebeispiel kann ich leider noch nicht so viel anfangen da ich die Wirkung nicht verstehe:

while read param wert
do
case param in
Anzahl:) var=$wert;;
Name:) echo "nix";;
*) echo "irgendwas"
esac
done < KonfigurationsdateiRead liest doch von der Konsole, denke ich? Müsste ich also ein Script schreiben und dann die Datei nach dem Script pipen?

Baalzamon
2010-11-26, 14:30:18
[...]
Read liest doch von der Konsole, denke ich? Müsste ich also ein Script schreiben und dann die Datei nach dem Script pipen?
Mit dem letzten Befehl

done < Konfigurationsdatei

'schiebst' du eine Datei in die while-Schleife in die Standardeingabe. Die wird dann zeilenweise abgearbeitet. Da musst du nichts mehr pipen, sondern einfach <Konfigurationsdatei> mit deinem Dateinamen ersetzen.

Probiere doch einfach ein kleines Skript:

while read i
do
echo $i
done < dateiname


Das sollte dir die Datei <dateiname> einfach wieder auf der Konsole ausgeben. Hinter dem echo kannst du dann deinen sed Befehl 'ranpipen'.

Oder habe ich dich falsch verstanden?

aths
2010-11-26, 14:36:24
Das klappt, also das zeilenweise Ausgeben und bringt mich schon mal ein gutes Stück weiter. Wie aber genau funktioniert "while read i"? Liefert read false wenn es nichts mehr einzulesen gibt?

edit: Mit Stringoperationen brauche ich nicht mal sed oder awk, damit experimentiere ich gleich mal rum :)

edit2: Ok, ich glaube das was ich machen will, kann ich jetzt machen. Danke an alle.

Baalzamon
2010-11-26, 14:44:52
Das klappt, also das zeilenweise Ausgeben und bringt mich schon mal ein gutes Stück weiter. Wie aber genau funktioniert "while read i"? Liefert read false wenn es nichts mehr einzulesen gibt?

Hmmm... das kann ich dir jetzt auch nicht so direkt beantworten. ;(

PHuV
2010-11-27, 01:13:16
Wie aber genau funktioniert "while read i"? Liefert read false wenn es nichts mehr einzulesen gibt?

Ganz einfach, ließ alle Zeilen bis EOF. Jede gefundene Zeile wird dann den mitgegebenen Variablen in der while read var1 ...varn zugeteilt, die dem vorher gesetzten Internal Field Separator zuteilt ist.

Beispiele:

für csv-Dateien
export IFS=";'

while read a b c d e f
do

done <inputdatei

Inputdatei:

a1;b1;c1;d1;e1;f1;g1;h1
a2;b2;c2;d2;e2;f2;g2;h2
a3;b3;c3;d3;e3;f3;g3;h3

Wenn weniger Variablen als Anzahl von Inputvariablen angegeben werden (hier a-f), dann würde beim Ausgabe von a und f folgendes rauskommen:

export IFS=";'

while read a b c d e f
do
echo "Ausgabe $a $f"
done <Inputdatei

Ausgabe a1 f1;g1;h1
Ausgabe a2 f2;g2;h2
Ausgabe a3 f3;g3;h3

Um das Ende mußt Du Dir keine Gedanken machen, bei EOF hört read automatisch auf, genau wie

for var in `cat Inputdatei`
do

done

Wie bereits gesagt, Default IFS ist immer blank und tab.

Unix arbeitet intern viel mit Pipes und IO-Umlenkungen, da mußt Du nix mehr machen. ;) Das ist ja das schöne an Unix-Shell-Programmierung. Und wer schnelle Verarbeitung mag, immer schön BuildIn-Funktionen verwenden. ;)

Mach mal selbst einen Test, mit AWK oder Berechnungen mit expr, und dazu mal eine BuildIn-Funktion

zz=`expr zz +1`

oder

zz=$((zz+1))

Übrigens gibt es auch einfache String- und Pattern-Funktionen für ksh:

http://oreilly.com/catalog/korn2/chapter/ch04.html#t5
Pattern-Matching Operators

Die sind alle viel schneller als die cut, awk und sonstigen Operationen. Man kann damit zwar nicht alles machen, aber vieles. ;)