mikrocontroller.net

Forum: PC-Programmierung Mini Bash Programm - wo steckt der Fehler ?


Autor: Jürgen W. (lovos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
test="0";
echo "Start"

echo -e "1\n2\n3\n4\n5" | while read line; do

  echo "1. test=$test" "line=$line"

  echo -e "a\nb\nc" | while read l; do
    if [ $line = 2 ] || [ $line = 4 ]; then
      test="1";
      echo "2. test=$test" "line=$line, l=$l"
    fi

  done

done

Die erste Schleife looped durch die Werte 1,2,3,4,5 (jeweils durch 
Newline getrennt, Variable "line"). Die Schleifenvariable "l" der 
inneren Schleife nimmt die Werte "a","b","c" an.
Die Variable "test" wird ganz am Anfang auf "0" gesetzt.
Nimmt "line" den Wert 2 oder 4 an, wird "test" auf 1 gesetzt.

Ich wuerde erwarten, dass "test" auf "1" bleibt bis zum Ende.
Aber die Ausgabe ist so:
Start
1. test=0 line=1
1. test=0 line=2
2. test=1 line=2, l=a
2. test=1 line=2, l=b
2. test=1 line=2, l=c
1. test=0 line=3
1. test=0 line=4
2. test=1 line=4, l=a
2. test=1 line=4, l=b
2. test=1 line=4, l=c
1. test=0 line=5

Nimmt "line" einen naechsten Wert an, dann wird auch "test" auf 0 
gesetzt. Aber warum nur? (keine Testaufgabe, ich bin wirklich ratlos)

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Variable "test" wird nicht auf null gesetzt. Was passiert ist, dass 
eine while Schleife in einer Untershell ausgefuehrt wird. Du kannst den 
Wert einer Variable eines Elternprozesses nicht in einem Kindprozess 
ueberschreiben, deshalb ist "test" beim Verlassen der inneren Schleife 
wieder (immer noch) auf dem alten Wert.

EDIT: vielleicht etwas verstaendlicher ausgedrueckt: in einer While 
Schleife arbeitest du mit Kopien der Variablen die vorher gesetzt 
wurden. Beim Verlassen einer Schleife werden diese Kopien verworfen und 
das Script arbeitet mit den alten Variablen weiter.

Thomas

Autor: Jürgen W. (lovos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke, das Raetsel ist geloest.
Aber wie kann ich im Inneren einer Schleife mir einen Zustand "merken", 
den ich am Schleifenende brauche?
Abruch kommt nicht in Frage, sie muss komplett abgearbeitet werden.

Es ginge natuerlich, den Zustand in eine temporaere Datei zu speichern, 
z.B.
echo "1" >/tmp/bashvariable

Und spaeter
if [ `cat /tmp/bashvariable` = "0" ]; then


Aber das erscheint mir keine saubere Loesung.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es wird nicht prinzipell eine Subshell für eine Schleife gestartet.
Aber hier geht es um eine Pipe:
echo -e "a\nb\nc" | while read l; do...
, daher die Subshell.

Es geht einfacher mit for:
for l in a b c; do...

Die andere Schleife entsprechend...

Autor: Jürgen W. (lovos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Es wird nicht prinzipell eine Subshell für eine Schleife gestartet.
>Aber hier geht es um eine Pipe:

Ja, auch mit Arrays geht es
array1=( "1" "2" "3" "4" )
array2=( "a" "b" "c" )
num1=${#array1[@]};
num2=${#array2[@]};
test="0";

for ((i=0;i<$num1;i++)); do
  for ((j=0;j<$num2;j++)); do
     if [ $i = 2 ]; then test="1"; fi
     echo ${array1[$i]} ${array2[$j]} "test=$test"
  done
done

Leider liegen meine Daten bereits als Textzeilen vor, die ich in ein 
Array umwandeln muesste.

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie Klaus geschrieben hat, kannst du eine for Schleife verwenden oder 
die Ausgabe mit ackticks in eine Variable schreiben:
res=`echo -e "1\n2\n3\n4\n5" | while read line; do
    # do something
    echo $test
done`
echo $res

oder mit einer Pipe an eine weiteres Programm oder Schleife 
weiterreichen.
echo -e "1\n2\n3\n4\n5" | while read line; do
    # do something
    echo $test
done | sed -e -'p'

Autor: Micky (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da ja "test" eine eingebaute Funktion der BASH ist, empfiehlt es sich 
generell, diesen Namen für eigene Zwecke NICHT zu benutzen. Benenn das 
mal um, zB in "Probe". Ich versprech hier keine Wunder, aber es könnte 
was nützen...

Autor: Jürgen W. (lovos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der uebersichtlichste Ansatz waere die Umwandlung der Textzeilen in ein 
Array. Das gleiche Problem. Funktioniert nicht, da wegen der Pipe eine 
Subshell angelegt wird.
i=0;
arr=()
echo -e "1\n2\n3" | while read line; do
  arr[$i]=$line
  echo "$i:" ${arr[$i]}
  i=$(($i+1))
done
echo "1.Elem:" ${arr[0]}
exit

Start
0: 1
1: 2
2: 3
1.Elem:

Hat jemand einen Vorschlag, um NL-separierte Daten in ein Array zu 
wandeln?

Autor: Jürgen W. (lovos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
schon was im Internet gefunden.
Ich finde die Sachen erst, NACHDEM ich eine Frage im Forum stelle.
declare -a array1
arr=($(echo -e "1\n2\n3" | tr '\n' ' '))
echo "1.Elem:" ${arr[0]}
exit

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bist du dir sicher dich nicht in etwas verrannt zu haben? Jedes mal wenn 
ich ein aehnliches Problem hatte konnte durch Nachdenken eine wesentlich 
einfachere und elegantere Loesung gefunden werden.

Und falls das Problem wirklich kompliziert ist, dann ist die Shell 
vielleicht nicht das geeignete Mittel zum Problem. sed/awk oder auch 
Python koennten besser dafuer geeignet sein.

Willst du uns dein eigentliches Problem schildern, dann koennte dir 
wahrscheinlich am ehesten geholfen werden.

Autor: Jürgen W. (lovos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was ganz banales.
Ich hole vom Internet eine Textdatei.
Dann sollen von dieser Textdatei alle Zeilen auf der konsole ausgegeben 
werden, die einen von mehreren Begriffen enthalten.
Diese Begriffe sind als Array vorgegeben.

Z.B.
Textdatei:
Muenchen
Berlin
Cuxhafen
Frankfurt am Main


Begriffe:
Nuernberg
Augsburg
Muenchen


Danach soll nur
Muenchen

ausgegeben werden.
Die Textdatei wie das Begriffe-Array kann Leerzeichen enthalten.
Es darf NICHT nach Leerzeichen separiert werden.
Deshalb funktioniert obiger Ansatz
arr=($(echo -e "1 1\n2\n3" | while read l; do echo "$l"; done ))

NICHT. Denn er macht aus der ersten Zeile "1 1" zwei Array-Elemente

EDIT:
Wenn Eintraege mehrfach vorhanden sind, soll nur eine Ausgabe erfolgen, 
an der Stelle des ersten Eintrages.
So ganz einfache Loesungen wie grep fallen dann schon weg.

Autor: Thomas Pircher (tpircher) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist die Reihenfolge wichtig? Wenn nicht, dann sollte so etwas 
funktionieren:
$ cat FILE1
Muenchen
Berlin
Cuxhafen
Frankfurt am Main

$ cat FILE2
Nuernberg
Augsburg
Muenchen

$ sort FILE1 FILE2 | uniq -d
Muenchen

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich denke, das ist einfach genug und schnell:
#!/bin/bash
#
# Time-stamp: "16.09.09 18:01 t.sh klaus?wachtler.de"

DATEI_AUS_INTERNET=dateiausinternet.txt
ARRAY_BEGRIFFE=( "Frankfurt am Main" "Nuernberg" )

# einmalig aus allen Begriffen einen String machen; darin die
# Begriffe durch | getrennt:
BEGRIFFE_ALS_STRING=${ARRAY_BEGRIFFE[0]}
for ((i=1;i<${#ARRAY_BEGRIFFE[@]};i++)); do
    BEGRIFFE_ALS_STRING=$BEGRIFFE_ALS_STRING"|"${ARRAY_BEGRIFFE[$i]}
done

# in $BEGRIFFE_ALS_STRING steht jetzt z.B. Frankfurt am Main|Nuernberg.
# Das ist ein (extended, nicht basic) regulärer Ausdruck, auf den 
# alle Zeilen passen, die "Frankfurt am Main" oder "Nuernberg"
# enthalten.

# Jetzt mit grep alle Zeilen der Datei ausgeben, die einen der Bergriffe enthalten:
grep -E "$BEGRIFFE_ALS_STRING" $DATEI_AUS_INTERNET

Wenn die Datei so aussieht:
Muenchen ist bayrisch
Berlin ist fett
nass ist es in Cuxhafen
und Frankfurt am Main ist hoch
Frankfurt an der Oder ist weit weg
Frankfurt am Rhein gibt es nicht
Nuernberg ist schoen
dann sieht die Ausgabe so aus:
klaus@i4a:~ > ./t.sh
und Frankfurt am Main ist hoch
Nuernberg ist schoen

Autor: Jürgen W. (lovos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, die Reihenfolge ist wichtig.

Ich habe jetzt an den IFS rumgebastelt und die Umwandlung in ein Array 
funktioniert jetzt, auch das uebrige Programm.

Allerdings braucht das Programm zur Ausfuehrung ein paar Sekunden und 
der Luefter meines Notebooks laeuft los. Das heisst richtige 
Rechenleistung.

Schon interessant, wie Rechenleistungs-Intensiv der Bash-Interpreter 
laeuft.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn man ihn mit Schleifen quält...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.