ubuntuusers.de

[bash] Lokale Variablen in Schleifen

Status: Gelöst | Ubuntu-Version: Ubuntu 9.04 (Jaunty Jackalope)
Antworten |

fenris_kcf

Anmeldungsdatum:
27. August 2009

Beiträge: 249

Hy, Ich will die Zeilen einer Datei mit einer Schleife auslesen und in ein Array speichern:

1
2
3
4
5
6
7
8
...
nummer=0
cat $datei | while read zeile[nummer]; do
	echo $nummer". Zeile: "${zeile[nummer]}
	nummer=$(($nummer+1))
done
echo "3. Zeile: "${zeile[3]}
...

Außerhalb der Schleife ist aber jedes Array-Element leer (Ausgabe einer beliebigen - hier der dritten - Zeile zeigt nix an). Woran genau liegt das und wie kann man das ändern? Eigentlich will ich diesen umständlichen Weg gar nicht gehen (also erst Zeilen in ein Array speichern und dieses dann auslesen), sondern lieber die Zeilen direkt lesen (also wie bei bei C mit getline oder wie bei php mit fgets). Wenn einer weiß, wie das geht, würde das auch sehr helfen.

Fenris

Kinch

Anmeldungsdatum:
6. Oktober 2007

Beiträge: 1261

Sorry, ich verstehe dein Problem nicht ganz. Du kannst doch schon die Zeilen lesen, sonst könntest du sie nicht in ein Array-Speichern.

while read line
do
  echo $line
done < <datei>

In der Variable "line" ist nun die Zeile jeweils gespeichert.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4687

Wohnort: Berlin

@fenris kcf: Der Grund warum Dein Quelltext so nicht funktioniert, ist dass Deine while-Schleife durch die Pipe in einem eigenen Prozess gestartet wird. Dort wird dann auch das Array angelegt, welches nachdem der Prozess gelaufen ist, natürlich wieder verschwindet. Lösung hat Kinch schon gezeigt: Mit Umleitung statt mit Pipe arbeiten.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/bin/bash

datei='test.sh'
nummer=0
while read zeile[nummer]
do
        echo "$nummer. Zeile: ${zeile[nummer]}"
        nummer=$((nummer+1))

done < "$datei"
echo 
echo "4. Zeile: ${zeile[3]}"

Ansonsten möchte ich mich Kinch anschliessen: Wenn Du nicht alle Zeilen in ein Array lesen willst, dann tu das doch einfach nicht. Rate mal was das read in der Schleife tut…

fenris_kcf

(Themenstarter)

Anmeldungsdatum:
27. August 2009

Beiträge: 249

Ah, vielen Dank. Kinchs Variante funktioniert schon mal.

Dann nochmal zum direkten Auslesen: Ich dachte mir schon, dass read genau diese Funktion ausführt, nur muss ich ja erstmal eine Datei ansprechen. Etwas wie fopen bei C gibt es ja nicht, oder? Bin da absolut unerfahren... Ich poste mal das ganze Skript, vielleicht hilft ja der Zusammenhang:

 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
46
#!/bin/bash

# Definitionen
datei=speiseplan.tex
plan='speiseplan_kw'$(($(date +%W)+1))'.txt'
nummer=0
tag[0]='Montag'
tag[1]='Dienstag'
tag[2]='Mittwoch'
tag[3]='Donnerstag'
tag[4]='Freitag'

# LaTeX-Dokument anfertigen
echo -e '\documentclass{article}' > $datei
echo -e '\usepackage{rotating}' >> $datei
echo -e '\\begin{document}' >> $datei
echo -e '\t\\begin{sidewaystable}' >> $datei
while read zeile[nummer]
do
	nummer=$(($nummer+1))
done < $plan
echo -e '\t\t\\begin{tabular}{|p{2.0cm}|p{8.0cm}|p{1.0cm}|}' >> $datei
echo -e '\t\t\t\\hline' >> $datei
echo -e '\t\t\tTag & Gericht & Preis \\\\' >> $datei
echo -e '\t\t\t\\hline' >> $datei
for i in {0..4}; do
	n1=$((7*$i+0))
	n2=$((7*$i+1))
	n3=$((7*$i+2))
	n4=$((7*$i+3))
	n5=$((7*$i+4))
	n6=$((7*$i+5))
	echo -e '\t\t\t'${tag[i]}' & '${zeile[n1]}' & '${zeile[n2]}' \\\\' >> $datei
	echo -e '\t\t\t & '${zeile[n3]}' & '${zeile[n4]}' \\\\' >> $datei
	echo -e '\t\t\t & '${zeile[n5]}' & '${zeile[n6]}' \\\\' >> $datei
	echo -e '\t\t\t\\hline' >> $datei
done
echo -e '\t\t\\end{tabular}' >> $datei
echo -e '\t\\end{sidewaystable}' >> $datei
echo -e '\\end{document}' >> $datei

# pdf-Datei erstellen und überflüssige Datein löschen
pdflatex $datei
rm speiseplan.log
rm speiseplan.aux
rm speiseplan.tex

Fenris

Kinch

Anmeldungsdatum:
6. Oktober 2007

Beiträge: 1261

Du, vielleicht sagst du erstmal, was du vorhast. Es ist in der Regel immer hilfreicher, wenn man zuerst schildert, was man tun will, wie die Bedingungen sind und dann erst, was man schon getan hat.

fenris_kcf

(Themenstarter)

Anmeldungsdatum:
27. August 2009

Beiträge: 249

Gut, gut: Ich hab Textdatein der Form "speiseplan_kw##.txt" und möchte ein tex-Dokument erstellen, bei dem in einer Tabelle die Einträge stehen. Das funktioniert so gesehen auch alles einwandfrei, nur fand ich die Art des Auslesens als nicht sehr zufriedenstellend. Ich schreib mal, wie der betreffende Teil in einem php-Skript aussähe:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
	...
	$f2 = fopen('speise/speiseplan_kw' . date('W') . '.txt', 'r');
	for ($i = 0; $i <= 4; $i++)
	{
		fprintf($f1, "\t\t\t" . $tag[$i] . ' & ' . str_replace("\n", '', fgets($f2)) . ' & ' . str_replace("\n", '', fgets($f2)) . ' \\\\');
		fprintf($f1, "\t\t\t" . ' & ' . str_replace("\n", '', fgets($f2)) . ' & ' . str_replace("\n", '', fgets($f2)) . ' \\\\');
		fprintf($f1, "\t\t\t" . ' & ' . str_replace("\n", '', fgets($f2)) . ' & ' . str_replace("\n", '', fgets($f2)) . ' \\\\');
		fprintf($f1, "\t\t\t" . '\hline' . "\n");
		fgets($f2);
	}
	fclose($f2);
	...
?>

Dass man das nicht einfach übersetzen kann, ist mir klar.

Marc_BlackJack_Rintsch Team-Icon

Ehemalige
Avatar von Marc_BlackJack_Rintsch

Anmeldungsdatum:
16. Juni 2006

Beiträge: 4687

Wohnort: Berlin

Für so etwas würde ich ja schon eine andere Programmiersprache als Bash verwenden. Bei mir wär's Python. Ungetestet:

 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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#!/usr/bin/env python
from __future__ import with_statement
import os
import subprocess
import time
from itertools import islice, izip


PLAN_FILENAME_TEMPLATE = 'speiseplan_kw%s.txt'

TEX_BASENAME = 'speiseplan'

TEMPORARY_EXTENSIONS = ['.tex', '.aux', '.log']

PDF_LATEX_COMMAND = 'pdflatex'

DAY_NAMES = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag']

PRE_DAYS_TEMPLATE = """\
\\documentclass{article}
\\usepackage{rotating}
\\begin{document}
    \\begin{sidewaystable}
        \\begin{tabular}{|p{2.0cm}|p{8.0cm}|p{1.0cm}|}
            \\hline
            Tag & Gericht & Preis \\\\
            \\hline
"""

DAY_TEMPLATE = """\
            %s & %s & %s \\\\
             & %s & %s \\\\
             & %s & %s \\\\
            \\hline
"""

POST_DAYS_TEMPLATE = """\
        \\end{tabular}
    \\end{sidewaystable}
\\end{document}
"""


def iter_data(lines, lines_per_day=7):
    lines = iter(lines)
    while True:
        data = tuple(s.rstrip() for s in islice(lines, lines_per_day))
        if not data:
            break
        if len(data) != lines_per_day:
            raise Exception('premature end of data')
        yield data


def convert(lines):
    yield PRE_DAYS_TEMPLATE
    for day_name, data in izip(DAY_NAMES, iter_data(lines)):
        yield DAY_TEMPLATE % ((day_name,) + data)
    yield POST_DAYS_TEMPLATE


def main():
    tex_filename = TEX_BASENAME + '.tex'
    with open(PLAN_FILENAME_TEMPLATE % time.strftime('%W'), 'r') as lines:
        with open(tex_filename, 'w') as tex_file:
            tex_file.writelines(convert(lines))
    
    subprocess.call([PDF_LATEX_COMMAND, tex_filename])
    
    for extension in TEMPORARY_EXTENSIONS:
        os.remove(TEX_BASENAME + extension)


if __name__ == '__main__':
    main()

Aber es geht natürlich auch jede andere Sprache die etwas besser als Bash ist, also zum Beispiel Perl oder Ruby. Und natürlich könntest Du auch einfach bei PHP bleiben.

fenris_kcf

(Themenstarter)

Anmeldungsdatum:
27. August 2009

Beiträge: 249

Ja, mit php oder anderen Skript-Sprachen ist es schon bequemer - stimmt. Ich wollte halt'n bisschen bash lernen und ich mag es auch nicht so mit System-Calls zu arbeiten, obwohl das eben häufig ganz praktisch ist. Dann werd ich wohl einfach über bash das php-Skript ausführen.

Danke!

Kinch

Anmeldungsdatum:
6. Oktober 2007

Beiträge: 1261

Also ich persönlich mache es bei Perl so, dass ich die ganzen System-Calls die ich brauche in Module kapsle. Im fertigen Skript kann man die externen Programme dann genauso wie in einem Shellskript benutzen.

Hello_World

Anmeldungsdatum:
13. Juni 2006

Beiträge: 3620

Ich erlaube mir mal, darauf hinzuweisen, dass ein System Call etwas völlig anderes ist als ein Aufruf der Funktion system in PHP oder Perl...

Kinch

Anmeldungsdatum:
6. Oktober 2007

Beiträge: 1261

Ja, war sprachlich etwas ungenau.

fenris_kcf

(Themenstarter)

Anmeldungsdatum:
27. August 2009

Beiträge: 249

Ja, dann nennen wir es eben Aufruf der php/perl-system-Funktion. In wie fern es etwas völlig anderes ist, würde mich aber mal interessieren.

Hello_World

Anmeldungsdatum:
13. Juni 2006

Beiträge: 3620

Es ist etwas völlig anderes. Ein System Call ist eine Schnittstelle zwischen User- und Kernel-Space, mit der man den Kernel auffordern kann, irgendwas zu tun, z. B. eine Anzahl Bytes über eine Netzwerkverbindung zu schicken. Eine Liste von System Calls gibt es in man 2 syscalls.

Die Funktion system in Perl oder PHP dagegen startet eine Shell und führt einen Befehl darin aus, das ist also etwas völlig anderes (und nebenbei etwas, das man ohnehin fast nie verwenden sollte).

Antworten |