PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Bash: Daten verarbeiten, sortieren und zuordnen.



tibrandt
07-07-2008, 10:25
Hallo,
ich brauche einen Tipp, wie ich folgenden Daten verarbeiten kann:

Artikelname1 Zeitung
Artikelname1 Buch
Artikelname1 Grußkarten
Quantity1 1.00
Quantity1 2.00
Quantity1 1.00
Price1 32.96
Price1 41.20
Price1 74.16

Ich möchte jedes Werte paar als einen Datensatz verarbeiten:
Variablen: artikelname;quantity;price
Datensatz 1: Zeitung; 1.00; 32.96;
Datensatz 2: Buch; 2.00; 41.20;
Datensatz 3 Grußkarten; 1.00; 74.16;
In dem Text können auch mehr als 3 oder auch nur 2 Wertepaare enthalten sein.
Die Werte will ich in eine DB eintragen, wie das geht, weis ich.


Mein Ansatz ist zu Zählen, wie viel mal z.B. Artikelname1 vorkommt.

Aber wie geht es denn weiter?
Ich brauche dringend einen Ansatz.

Danke für Eure Hilfe!

Gruß
Tino Brandt

Aqualung
07-07-2008, 10:48
Die Zuordnung ist ausschließlich über die Reihenfolge festgelegt?

tibrandt
07-07-2008, 11:04
Ja,

die erste Position bildet ein Wertepaar (Zeitung;1.00;32.96), jeweils die zweite usw.



Danke!

Gruß
Tino

Aqualung
07-07-2008, 11:17
Deine Eingabe sei file3. Eine "hölzerne" Lsg. wäre:



mytmp=$(mktemp -d)
grep Artikelname1 file3 | cut -d" " -f2 > $mytmp/1.tmp
grep Quantity1 file3 | cut -d" " -f2 > $mytmp/2.tmp
grep Price1 file3 | cut -d" " -f2 > $mytmp/3.tmp

paste $mytmp/1.tmp $mytmp/2.tmp $mytmp/3.tmp

Zeitung 1.00 32.96
Buch 2.00 41.20
Grußkarten 1.00 74.16

rm -rf $mytmp

tibrandt
07-07-2008, 11:34
Super, danke für den Ansatz.
Ich will ja die Werte in eine DB erfassen, mal sehen wie ich die Zuordnung hinbekommen.

Nochmals Danke!

Gruß
Tino

tibrandt
07-07-2008, 13:55
Hier meine daraus entwickelte Lösung:

##
mytmp=$(mktemp -d)
grep Artikelname1 file3 | cut -d" " -f2 > $mytmp/1.tmp
grep Quantity1 file3 | cut -d" " -f2 > $mytmp/2.tmp
grep Price1 file3 | cut -d" " -f2 > $mytmp/3.tmp

anzahl=$(cat file3 | grep "Artikelname1" | wc -l)

for ((m=1; $m<=$anzahl; m++));do
artikelname=$(head -n$m $mytmp/1.tmp | tail -n1)
echo $artikelname
quantity=$(head -n$m $mytmp/2.tmp | tail -n1)
echo $quantity
price=$(head -n$m $mytmp/3.tmp | tail -n1)
echo $price
done
rm -rf $mytmp
##

Gruß
Tino

jan61
07-07-2008, 19:57
Moin,

hm, das ist eine ziemlich aufwändige Variante. Ich würde hier eher mit Bash-Arrays oder mit awk (in dem Arrays auch möglich sind) arbeiten.

Ich hatte die Behandlung von Arrays im awk neulich schon mal gezeigt, hier eine angepasste Version:

jan@jack:~/tmp/kv2csv> cat datei
Artikelname1 Zeitung
Artikelname1 Buch
Artikelname1 Grußkarten
Quantity1 1.00
Quantity1 2.00
Quantity1 1.00
Price1 32.96
Price1 41.20
Price1 74.16
jan@jack:~/tmp/kv2csv> cat kv2csv.awk
{ if (z_arr[$1] == "") z_arr[$1] = 0; # z_arr: key=variable, value=wertzaehler
else z_arr[$1]++;
f_arr[$1,z_arr[$1]] = $2; # f_arr: key=variable+zaehler, value=wert
}
END { # einlesen beendet: ausgabe
out = ""; # ausgabezeile
max = 0; # hoechster zaehler
for (z in z_arr) { # schleife fuer header: alle variablen
out = out z ";"; # an ausgabe variable + ";" anhaengen
if (max < z_arr[z]) max = z_arr[z]; # max hochzaehlen
}
gsub(/;$/, "", out); # letztes ; entfernen
print out; # ausgeben
for (c = 0; c <= max; c++) { # schleife ueber alle zaehler
out = ""; # ausgabezeile
for (z in z_arr) { # schleife ueber alle variablen
out = out f_arr[z,c] ";"; # an ausgabe wert + ; anhaengen
}
gsub(/;$/, "", out); # letztes ; weg
print out; # ausgeben
}
}

jan@jack:~/tmp/kv2csv> awk -f kv2csv.awk datei
Quantity1;Artikelname1;Price1
1.00;Zeitung;32.96
2.00;Buch;41.20
1.00;Grußkarten;74.16
Vorteil: Kommt mit beliebigen Variablennamen (und beliebiger Anzahl Variablen und Werte) klar, kommt ohne temp. Dateien aus, nur 1 Prozess statt vieler (nach meiner Zählung 17 Prozesse + 8 Subshells durch die Pipes), wahrscheinlich deutlich schneller - vor allem bei größeren Dateien.

Jan

P.S.: Bitte bitte - lasst diese unsäglichen Konstruktionen wie 'cat file3 | grep "Artikelname1" | wc -l'! Der grep kann - wie die meisten Unix-Kommandos - Dateinamen als Kommandozeilenargument verarbeiten, da ist kein cat vorneweg nötig. Und mit einem Blick in die grep-Manualpage hätte sich die Option -c geradezu aufgedrängt. Das Ganze ließe sich also unter Einsparung von 2 Prozessen und 2 Subshells einfach als 'grep -c "Artikelname1" file3' schreiben.

EDIT: Die Anzahl Prozesse + Subshells in Deiner Lösung sind die Minimalwerte bei 1 Wert. Mit jedem weiteren Schleifendurchlauf kommen 6 Prozesse und 3 Subshells dazu.

jan61
07-07-2008, 22:12
Moin,

der Vollständigkeit halber hier noch 2 Varianten mit bash-Arrays. Ich musste für die 2. (flexible) Version etwas basteln, deshalb die Verspätung ;-)

1. Version: Die Feld(Variablen)-Namen sind bekannt und festgelegt. Ich nutze fest definierte bash-Arrays, die maximale Anzahl von Werten ermittle ich gleich in der 1. Schleife, um mir extra Berechnungen zu ersparen. Das Script sollte relativ einfach zu verstehen sein:

jan@jack:~/tmp/kv2csv> cat kv2csv.sh
#! /bin/bash

declare -a Artikelname1
declare -a Quantity1
declare -a Price1

max=0
while read k v; do
case $k in
Artikelname1)
n=${#Artikelname1
}
Artikelname1[$n]="$v"
;;
Quantity1)
n=${#Quantity1
}
Quantity1[$n]="$v"
;;
Price1)
n=${#Price1
}
Price1[$n]="$v"
;;
esac
test $max -lt $n && max=$n
done <datei

echo "Artikelname1;Quantity1;Price1"
for i in `seq 0 $max`; do
echo "${Artikelname1[$i]};${Quantity1[$i]};${Price1[$i]}"
done

exit 0
Die 2. Version ist ziemlich schwer verdaulich, zeigt aber, wie man auch in der bash mit variablen Variablennamen arbeiten kann ;-) Das wichtigste Shell-Builtin für solche Anwendungen ist eval, das einen übergebenen Ausdruck evaluiert (in der Shell ausführt). Den zu evaluierenden Ausdruck setze ich mit diversen echo-Kommandos zusammen:

jan@jack:~/tmp/kv2csv> cat kv2csv_var.sh
#! /bin/bash

max=0
vars=" "
while read k v; do
echo "$vars" | grep -q " $k " || vars="$vars$k "
m=`eval echo \`echo '${#'$k'
}'\``
test $max -lt $m && max=$m
eval `echo \`echo $k'['$m']='"$v"\``
done <datei
echo "$vars"

echo $vars | sed 's/ /;/g'
for i in `seq 0 $max`; do
out=""
for k in $vars; do
v="`eval echo \`echo '${'$k'['$i']}'\``"
if test -n "$out"; then
out="$out;$v"
else
out="$v"
fi
done
echo "$out"
done

exit 0
Das Ding ist sogar kürzer als die 1. Version, aber flexibel bei Anzahl und Namen der Variablen - man muss nur ziemlich lange grübeln, bis man hinter die Funktion kommt ;-) Um die Funktionsweise zu verstehen, solltet Ihr ein "set -x" an den Anfang des Scripts setzen, dann wird Euch jeder Schritt angezeigt.

Man kann noch ein wenig vereinfachen und Prozesse sparen, wenn man die Möglichkeiten der bash ausreizt (der grep könnte u. U. durch ${parameter/pattern/string} ersetzt werden, die for-Schleife im Old-Unix-Style durch die C-Style-Variante, ...).

Jan

EDIT: Die 2. Variante habe ich unnötig kompliziert gemacht (so kommt das, wenn man bastelt und bastelt und die Leichen drin lässt). So sieht es viel einleuchtender aus:
jan@jack:~/tmp/kv2csv> cat kv2csv_var.sh
#! /bin/bash

max=0
vars=" "
while read k v; do
echo "$vars" | grep -q " $k " || vars="$vars$k "
m=`eval echo '${#'$k'
}'`
test $max -lt $m && max=$m
eval $k'['$m']='"$v"
done <datei

echo $vars | sed 's/ /;/g'
for i in `seq 0 $max`; do
out=""
for k in $vars; do
v="`eval echo '${'$k'['$i']}'`"
test -n "$out" && out="$out;"
out="$out$v"
done
echo "$out"
done

exit 0