michahe
Anmeldungsdatum: 12. Dezember 2013
Beiträge: 810
|
Hallo, ich dachte, das ist einfach: Ein txt-File zeilenweise auslesen, in den drei Zeilen stehen jeweils zwei mit TAB getrennte Werte, die will ich prüfen. Zur Sicherheit mein txt-File als Anhang, denn nicht einmal das Lesen der Zeilen klappt. Mein Ansatz war:
| #!/bin/sh
while read LINE; do
echo "$LINE"
read -p "Press any key to resume ..."
# while read spalte_1 spalte_2 ;
# do
# echo "xxx: $spalte_1 --- $spalte_2"
# done < $line
done < test.txt
|
Das liefert:
| $ bash test.sh
a1 b1
a3 b3
$
|
Bearbeitet von rklm: Passendes Syntaxhighlighting
- test.txt (18 Bytes)
- Download test.txt
|
dingsbums
Anmeldungsdatum: 13. November 2010
Beiträge: 3532
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | #! /bin/bash
datei="test.txt"
# arbeitskopie erzeugen
cp -p $datei kopie.txt
# sicherheitshalber leerzeile anhaengen,
# damit die letze zeile auch bei fehlendem zeilenumbruch eingelesen wird
echo "" | tee -a kopie.txt
# zaehler auf 0
zaehler=0
cat kopie.txt | while read line;do
zaehler=$(($zaehler + 1))
spalte1=$(echo $line | awk '{print $1}')
spalte2=$(echo $line | awk '{print $2}')
echo "zeilennummer $zaehler: spalte1 ist $spalte1, spalte2 ist $spalte2"
done
#arbeitskopie löschen
rm -f kopie.txt
|
Geht bestimmt viel eleganter, aber für den Hausgebrauch reichts.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
michahe schrieb: Hallo, ich dachte, das ist einfach: Ein txt-File zeilenweise auslesen, in den drei Zeilen stehen jeweils zwei mit TAB getrennte Werte, die will ich prüfen. Zur Sicherheit mein txt-File als Anhang, denn nicht einmal das Lesen der Zeilen klappt. Mein Ansatz war:
#! /bin/sh
while read LINE; do
echo "$LINE"
read -p "Press any key to resume ..."
done < test.txt
Das liefert:
$ bash test.sh
a1 b1
a3 b3
$
Kein Wunder, Zeile 4 liest zwischen den Zeilen jeweils eine weitere Zeile. Wenn aus einer Datei gelesen wird, drückt da natürlich niemand enter zwischendurch. #! /bin/sh
while read LINE; do
echo "$LINE"
done < test.txt würde also wohl funktionieren.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
Mit
| cat test.txt | mapfile tt
|
kannst Du die Datei in ein Zeilenarray speichern, so dass alle Zeilen dann mit
a1 b1 a2 b2 a3 b3
ausgegeben werden können, oder Zeile 2 (wg. Arrayindizierung ab 0) mit
a2 b2
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12801
|
michahe schrieb:
ich dachte, das ist einfach: Ein txt-File zeilenweise auslesen, in den drei Zeilen stehen jeweils zwei mit TAB getrennte Werte, die will ich prüfen.
Ich weiß nicht, wie Deine Prüfung aussehen soll, aber da bietet sich auf jeden Fall awk an. Egal, ob alle Werte dieselbe Prüfung überstehen müssen oder Du für alle Werte unterschiedliche Kriterien hast, kannst Du das recht elegant mit awk machen.
|
michahe
(Themenstarter)
Anmeldungsdatum: 12. Dezember 2013
Beiträge: 810
|
Danke für Eure Hilfe! Der Hinweis von user_unknown hat meinen Rumpf-Code zum Laufen gebracht, dieses Uralt-Posting: von rklm erlaubt es mir einzelne Spaltenwerte anzusprechen; so sieht es jetzt aus:
| #! /bin/sh
while read -r Line; do
echo "Line: $Line"
array=($Line)
echo "${#array[@]} items: ${array[@]}" # Quelle: https://forum.ubuntuusers.de/topic/datei-auslesen-und-zeilen-splitten/
echo "1: ${array[0]}"
echo "2: ${array[1]}"
done < test.txt
|
In zwei Spalten sind immer Zahlen in der Form 10.12345678, mit denen weiter gerechnet werden soll. Die Ergebnisse für beide Spalten werden zu einer Zahl vereinigt und die wird zur ursprünglichen Textdatei hinzugefügt. Ich lasse das Thema noch offen, vielleicht habt Ihr noch Vorschläge oder es kommt doch noch ein Problem. Danke, Michael
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
michahe schrieb: Ich lasse das Thema noch offen, vielleicht habt Ihr noch Vorschläge oder es kommt doch noch ein Problem.
Ja, habe ich, abgesehen davon, dass Du den Thread eh nicht schließen, sondern nur die Frage als beantwortet markieren kannst. Ich wollte erst wissen, ob ich mehr auf den Text und Code der Frage oder mehr auf die Überschrift achten soll, wie es andere getan haben. Dazu hätte ich nämlich auch noch einen Vorschlag. Wenn Du so ein 2dimensionales Array hast, bei dem immer beide Spalten besetzt sind, kannst Du das ganze Array in ein eindimensionales Array einlesen, und mit Hilfe einer Funktion auf die Elemente zugreifen, als wäre es 2dimensional: 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 | #!/bin/bash
#
# Get element in row/col (r, c) from 2-Dim-Array with rowlength rl.
# Basis ist aber ein 1-dim Array.
#
getRowCol () {
r=$1
c=$2
rl=$3
idx=$((rl*(r-1)+(c-1)))
echo $idx
}
# 2d-Array:
#
echo "3 4
7 91
53 25" > /tmp/tmpArray.dat
declare -a array
array=($(cat /tmp/tmpArray.dat))
# Sichtkontrolle:
cat /tmp/tmpArray.dat
echo ${array[@]}
#
# 1-d-Array
#
# 3 4 7 91 53 25
for r in 1 2 3; do
for c in 1 2; do
idx=$(getRowCol $r $c 2)
echo -e "${array[idx]} \t r $r c $c\t idx: $idx"
done
done
|
Die Werte wie im Abs. 1 und dazugehöriger Index im eindimensionalen Array, wie es nach Einlesen vorliegt (Abs.2) und wie es mit Zeilen/Spalten korrespondiert:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | Arraywerte in Userfriendly Form:
#
# 3 4
# 7 91
# 53 25
Arrayindizees, in eindimensionalem Array:
#
# 0 1
# 2 3
# 4 5
row-col-Paare:
#
# 1,1 1,2
# 2,1 2,2
# 3,1 3,2
|
Die Umrechnung der Koordinaten geschieht mit der einfachen Formel idx=rowlength*(row-1)+(col-1) und funktioniert für 2-dim Arrays beliebiger Größe, nur Sparse-Arrays, also solche mit Lücken, sollten es nicht sein. Hier wird als Demo einfach "echo" aufgerufen, aber stattdessen kann man natürlich etwas anderes machen, auch abhängig davon, in welcher Spalte der Wert steht, natürlich. Das Arbeiten mit Arrays in der Bash hat einige Fallstricke und ist nicht sonderlich intuitiv - andererseits ist es aber oft zu praktisch, um es zu ignorieren und die Bash drängt sich als Werkzeug auf, weil sie andere Programme aufruft, was wiederum aus anderen Sprachen umständlich ist. Den Code könnte man noch so umbauen, dass er selbständig ermittelt, wie viele Spalten das Array und für die for-loop auch, wie viele Zeilen die Datei hat, aber wenn man es nicht braucht - wozu? Das -e hinter echo braucht man, damit der Tab \t als Tab interpretiert wird.
|
michahe
(Themenstarter)
Anmeldungsdatum: 12. Dezember 2013
Beiträge: 810
|
Danke, die Ausgangs-Textdateien haben 1.000 oder mehr Zeilen und derzeit 20 Spalten. Mit meinem Code komme ich "eigentlich" klar, insbesondere kann ich das mit meinem begrenzten Wissen nachvolziehen. Nun habe ich aber das Problem, dass in manchen Zeilen einzelne Spalten keine Werte haben, in der .txt-Datei steht also z.B. (–> bzw. ––> für TAB):
1-->2 -->3 -->4-->5-->6-->7 -->8 -->
1-->54321-->Egon-->--->--->--->10.12345678-->90.87654321-->Uschi
Die Spalten 7 und 8 haben immer Zahlen, werden aber in Abhängigkeit der Anzahl der Leerfelder vorher falsch erkannt. Was muss ich ändern?
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
Mapfile ist Dein Freund,
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 | #!/bin/bash
#
#
#
getIndex () {
r=$1
c=$2
rl=$3
idx=$((rl*(r-1)+(c-1)))
echo $idx
}
# IFS=$'\t\n'
mapfile -d $'\t' arr < <(cat sparse.txt | tr "\n" "\t")
echo "${arr[@]}"
for r in {1..5}
do
echo "Row $r: "
for c in {1..4}
do
idx=$(getIndex $r $c 4)
echo -e "\tColumn $c:\tIdx: $idx\t${arr[idx]}"
done
done
|
aber ein leicht heikler Freund ☺ . Man kann der Datei die Zeilenumbrüche entziehen und diese auch in Tabs wandeln. Aus mir unbekannten Gründen muss man einen Delimiter wie Tab mit einem $ dekorieren: -d $'\t' . So sieht meine Testdatei, sparse.txt, aus:
| cat sparse.txt
1 2 3 4
1 3 4
2 4
4
1 2 3 4
|
Und so der Output, meines Proof-of-Concept:
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 | 1 2 3 4 1 3 4 2 4 4 1 2 3 4
Row 1:
Column 1: Idx: 0 1
Column 2: Idx: 1 2
Column 3: Idx: 2 3
Column 4: Idx: 3 4
Row 2:
Column 1: Idx: 4 1
Column 2: Idx: 5
Column 3: Idx: 6 3
Column 4: Idx: 7 4
Row 3:
Column 1: Idx: 8
Column 2: Idx: 9 2
Column 3: Idx: 10
Column 4: Idx: 11 4
Row 4:
Column 1: Idx: 12
Column 2: Idx: 13
Column 3: Idx: 14
Column 4: Idx: 15 4
Row 5:
Column 1: Idx: 16 1
Column 2: Idx: 17 2
Column 3: Idx: 18 3
Column 4: Idx: 19 4
|
Die Zeilen zeilenweise in ein Array zu lesen sollte genauso klappen. 1000 mapfile-Aufrufe kosten sicher auch nicht die Welt. Mit Deinem
| while read -r Line; do
echo "Line: $Line"
array=($Line)
|
klappt das meines Wissens nicht - auch nicht mit Umsetzung von IFS - da bekomme ich dann für Row 2 3 Elemente mit Index 0, 1, 2, nicht 0, 1, 4. Vielleicht weiß jmd. mit mehr Erfahrung mit IFS da besser Bescheid. Ich bin vor IFS immer zurückgeschreckt, nachdem ich einmal auf die Nase gefallen bin, und mich selbst aus der Shell quasi herausgeschrieben habe. Vielleicht hilft Dir colrm weiter: | colrm 1 16 < sparse.txt | colrm 2
3
3
3
|
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12801
|
michahe schrieb:
In zwei Spalten sind immer Zahlen in der Form 10.12345678, mit denen weiter gerechnet werden soll. Die Ergebnisse für beide Spalten werden zu einer Zahl vereinigt und die wird zur ursprünglichen Textdatei hinzugefügt.
Was bedeutet "vereinigen"? Beschreibe doch mal, was Du wirklich erreichen willst.
|
michahe
(Themenstarter)
Anmeldungsdatum: 12. Dezember 2013
Beiträge: 810
|
Danke für die Rückmeldung: In zwei Spalten sind immer Zahlen in der Form 10.12345678, mit denen weiter gerechnet werden soll. Die Ergebnisse für beide Spalten werden zu einer Zahl vereinigt und die wird zur ursprünglichen Textdatei hinzugefügt.
Was bedeutet "vereinigen"? Beschreibe doch mal, was Du wirklich erreichen willst.
Die beiden Spalten enthalten in jeder Zeile den X- und Y-Teil einer Koordinate. Ziel ist es, die Zeilen so zu sortieren, das nahe beieinander liegende Punkte in Gruppen sortiert werden. Ich möchte die txt-Liste zeilenweise durchlaufen und
den Bereich aller Werte ermitteln, also Kmin=min(X, Y), Kmax(X,Y), dazu muss ich in meinem Beispiel oben die Spalten 7 und 8 finden, auch wenn Spalten davor leer sind; die Größe des Koordnatenbereichs ermitteln, also BreiteX=Kmax(X) - Kmin(X) und ebenso in Y-Richtung; die Breite des Bereichs in neun Segmente teilen, also SegmentWeiteX=BreiteX / 9, ebenso für Y.
Dann wird die txt-Liste erneut zeilenweise durchlaufen und
für den X- (Spalte 7) und Y-Wert (Spalte 8) die X- und Y-Segmente berechnet, in die die Koordinate gehört, also zwei Ziffern 1..9; um schließlich eine Sortierung der Liste zu ermöglichen, wird eine Zahl Ergebnis=SegemntX * 10 + Segemnt Y berechnet.
Ich bin "blutiger" Anfänger mit BASH-Programmierung, eine zielsichere Erkennung der gesuchten Spalten auch mit vorherigen Leer-Spalten würde mir reichen. Ich bin user_unknown sehr dańkbar für seinen Input 9234862, werde aber mindestens eine Woche brauchen, um die ganzen Fremdworte M(Mapfile, getIndex, colrm) überhaupt nur zu verstehe, geschweige denn anzuwenden ...
|
michahe
(Themenstarter)
Anmeldungsdatum: 12. Dezember 2013
Beiträge: 810
|
Der Hinweis von user_unknown auf IFS hat mich auf diese Variante gebracht:
| #! /bin/sh
echo -n "$IFS" | od -x # Beweisen, dass IFS nicht dauerhaft geändert wurde, Quelle: http://openbook.rheinwerk-verlag.de/shell_programmierung/shell_007_002.htm#RxxKap00700204004E731F04A1BE
while IFS=$'\t' read -r -a arrZeile; do # Quelle: https://stackoverflow.com/questions/9736202
echo "${#arrZeile[@]}" " items: " "${arrZeile[@]}" # Quelle: https://forum.ubuntuusers.de/topic/datei-auslesen-und-zeilen-splitten/
echo "X || Y: ${arrZeile[6]} || ${arrZeile[7]}}"
done < "test.txt"
echo -n "$IFS" | od -x # Beweisen, dass IFS nicht dauerhaft geändert wurde, Quelle: http://openbook.rheinwerk-verlag.de/shell_programmierung
|
Das funktioniert; seht Ihr "Gegenanzeigen"? colrm habe ich versucht, bekomme die Ergebnissen aber nicht in neue Variablen.
|
user_unknown
Anmeldungsdatum: 10. August 2005
Beiträge: 17548
Wohnort: Berlin
|
Nur kleine. Die Überschrift sagt "Bash", aber als Shebang verwendest Du /bin/sh. Benutz da ruhig Bash, wenn Du es mit der Bash ausführst, sonst bewirken | bash meinSkript.sh
# und
./meinSkript.sh
|
Unterschiedliches. Es macht nicht viel Sinn, Quotes zu schließen, um sie dann gleich wieder zu öffnen:
| # hektisch:
echo "${#arrZeile[@]}" " items: " "${arrZeile[@]}"
# ruhig:
echo "${#arrZeile[@]} items: ${arrZeile[@]}"
|
Mapfile, getIndex, colrm
Mapfile liest ein mapping aus einer Datei und läuft auch unter dem Namen readarray. colrm löscht Spalten Zeichenweise
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 | echo -e 1234567{a..f}"\n" | sed 's/ //'
1234567a
1234567b
1234567c
1234567d
1234567e
1234567f
t201:~ > echo -e 1234567{a..f}"\n" | sed 's/ //' | colrm 3 3
124567a
124567b
124567c
124567d
124567e
124567f
t201:~ > echo -e 1234567{a..f}"\n" | sed 's/ //' | colrm 3
12
12
12
12
12
12
t201:~ > echo -e 1234567{a..f}"\n" | sed 's/ //' | colrm 1 3
4567a
4567b
4567c
4567d
4567e
4567f
|
getIndex zählt in einer Matrix oder einem 2-D-Array, das man von oben nach unten und links nach rechts liest, jeweils beginnend mit 1, den Index (beginnend mit 0) aus. So kann man ein eindimensionales Array wie ein 2dimensionales behandeln. | 0 1 2 3
4 5 6 7
8 9 10 11
|
Man muss nur sagen, wie lange die Zeilen sind (hier in Feldern, durch Whitespace getrennt, nicht in Zeichen, wie bei colrm). Und es zählt nicht wirklich, sondern rechnet. D.h. Du könntest das ganze Array auf einen Rutsch einlesen (die ganze Datei), und dann mit 20, weil 20 Spalten immer die Spalten 7 und 8 abfragen:
| rows=$(cat file | wc -l)
for row in $(seq 1 $rows)
do
xi=$(getIndex $row 7 20)
yi=((xi+1))
x=${arr[xi]}
y=${arr[yi]}
# Jetzt was machen, mit x und y
done
|
|
rklm
Projektleitung
Anmeldungsdatum: 16. Oktober 2011
Beiträge: 12801
|
michahe schrieb:
Ich möchte die txt-Liste zeilenweise durchlaufen und
den Bereich aller Werte ermitteln, also Kmin=min(X, Y), Kmax(X,Y), dazu muss ich in meinem Beispiel oben die Spalten 7 und 8 finden, auch wenn Spalten davor leer sind; die Größe des Koordnatenbereichs ermitteln, also BreiteX=Kmax(X) - Kmin(X) und ebenso in Y-Richtung; die Breite des Bereichs in neun Segmente teilen, also SegmentWeiteX=BreiteX / 9, ebenso für Y.
Dann wird die txt-Liste erneut zeilenweise durchlaufen und
für den X- (Spalte 7) und Y-Wert (Spalte 8) die X- und Y-Segmente berechnet, in die die Koordinate gehört, also zwei Ziffern 1..9; um schließlich eine Sortierung der Liste zu ermöglichen, wird eine Zahl Ergebnis=SegemntX * 10 + Segemnt Y berechnet.
Mir fehlt leider die Zeit um das auszuprogrammieren. Hier wäre mein Ansatz
Man nehme awk Benutze -F \\t (Tab als Feldtrenner) Rufe awk zwei Mal mit dem Dateinamen auf, damit die Datei zwei Mal durchlaufen wird Drei Pattern-Action-Blöcke, einer mit FNR == NR, einer mit FNR != NR und einer mit ENDFILE der erste berechnet die aggregierten Werte (min, max) der ENDFILE Block aggregiert über die min und max falls noch nicht berechnet der zweite wendet die aggregierten Werte an und gibt das Resultat aus
die Ausgabe wird dann sortiert mit sort , wobei man Tab als Feldtrenner angibt und die zu sortierenden Felder mit -k
Du brauchst übrigens nicht x und y zu kombinieren. Es reicht, wenn Du nach zwei Feldern sortierst.
|
michahe
(Themenstarter)
Anmeldungsdatum: 12. Dezember 2013
Beiträge: 810
|
So, jetzt läuft mein Code - allerdings nur mit der von user_unknown vorgeschlagenen Zeile Mapfile:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | #! /bin/bash
while IFS= read -r line; do # txt-Datei lesen, alle TAB-Felder in Array
mapfile -td $'\t' arrZeile < <(printf %s "$line") # mapfile, um leere Spalten zu erkennen
echo "${#arrZeile[@]}" " items: " "${arrZeile[@]}" # definierte Spalten des Array auslesen; Quelle: https://forum.ubuntuusers.de/topic/datei-auslesen-und-zeilen-splitten/
# declare -p arrZeile # Alle Spalten des Array mit [Nr] anzeigen (zu Test-Zwecken)
if [ "${arrZeile[11]}" != "rel" ]; then
echo "Typ || LAT || LON: ${arrZeile[11]} || ${arrZeile[12]} || ${arrZeile[13]}"
if (( $(echo "${arrZeile[12]} < $minLAT" |bc -l) )); then # minLAT finden
minLAT=${arrZeile[12]}
fi
if (( $(echo "${arrZeile[12]} > $maxLAT" |bc -l) )); then # maxLAT finden
maxLAT=${arrZeile[12]}
fi
if (( $(echo "${arrZeile[13]} < $minLON" |bc -l) )); then # minLON finden
minLON=${arrZeile[13]}
fi
if (( $(echo "${arrZeile[13]} > $maxLON" |bc -l) )); then # maxLON finden
maxLON=${arrZeile[13]}
fi
fi
done < "test.txt"
|
Danke! Danke auch an rklm, mit awk hab ich so meine gepflegten Probleme, aber ich will mich noch daran versuchen.
|