Diese Website existiert nur weil wir Werbung mit AdSense ausliefern.
Bitte den AdBlocker daher auf dieser Website ausschalten! Danke.

Phänomen mit while

Alles rund um die verschiedenen Konsolen und shells sowie die Programmierung unter Linux

Moderator: Moderatoren

Antworten
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Phänomen mit while

Beitrag von regexer »

Code: Alles auswählen

#!/bin/bash

treffer=no

echo 'a' | while read string   # Versuch 1
# for string in a                # Versuch 2
# string=a ; while [ 1 = 1 ]     # Versuch 3
do
  if [ $string = "a" ]
  then
    treffer=yes
    echo $treffer
  fi
  break
done
echo $treffer

Anscheinend bewirkt die echo-while-Kombination, dass $treffer eine innerhalb der while-Schleife eine lokale Varialbe ist. Dieses Verhalten ist mir völlig neu. Versuch Nr. 3 zeigt, dass das while ansich nicht "schuld" ist. Die Korn-Shell unter SCO UnixWare verhält sich übrigens nicht so. Komisch!!! Hat jemand dafür eine Erklärung? Oder noch besser: Wie kann ich $treffer ganz normal als globale Variable behandeln?
gaw
Hacker
Hacker
Beiträge: 464
Registriert: 28. Sep 2004, 16:33

Beitrag von gaw »

Das ist aber so weil in Befehlen mit asynchronen Befehlen z.B mit einer pipe die Anweisung in einer Subshell ausgeführt wird. Steht auch in der man bash:

Code: Alles auswählen

A command invoked  in  this  separate  environment  cannot
       affect the shell's execution environment.

       Command substitution and asynchronous commands are invoked
       in a subshell environment that is a duplicate of the shell
       environment,  except  that  traps  caught by the shell are
       reset to the values that the shell inherited from its par­
       ent  at  invocation.  Builtin commands that are invoked as
       part of a pipeline are also executed in a  subshell  envi­
       ronment.   Changes made to the subshell environment cannot
       affect the shell's execution environment.
Es gibt keine globalen Variabeln in deinem Sinne so dass du in einer Bash von einer Subshell auf die Variabeln der aufrufenden Schell zugreifen kannst. Du kannst sie in eine temoräre Datei abpeichern oder ein alternatives Design anwenden, zum Beispiel:

Code: Alles auswählen

# 4 Zeilen String, 2 beginnen mit A
string="Aller \nAnfang \nist\nschwer"
# Welche haben A am Anfang?
pattern="^A"  

$COUNT_TARGET=`echo -e $string | grep $pattern | wc -l`
oder so ähnlich.
mfG
gaw
Zuletzt geändert von gaw am 27. Jan 2005, 15:40, insgesamt 3-mal geändert.
Benutzeravatar
robi
Moderator
Moderator
Beiträge: 3175
Registriert: 25. Aug 2004, 02:13

Beitrag von robi »

Deine Variable treffer ist innerhalb der whileschleife bekannt und auch ihr Wert gesetzt, aber es ist nicht die gleiche Variable (gleicher Speicherplatz im Speicher) sondern nur eine Kopie davon, also wenn du ihren Wert innerhalb der Schleife veränderst, dann ist nach Abschluss der Schleife wieder der alte Wert da. Das Ganze hat was mit Prozesskommunikation und Kommandogruppierung zu tun und wie die Shell die jeweiligen Prozesse organisiert, und wie du schon selbst bemerkt hast, nicht alle Shells stellen sich hier so widerspinstig an wie die bash unter LINUX. Versuche die while Schleife als erstes Kommando hinter ein ; oder neu Zeile anzuordnen dann sollte es gehen. Dieses kannst du erreichen indem du vorher wie bei Versuch 3 die Eingaben für die Whileschleife in eine Variable abspeicherst oder du legst die Eingaben in eine Datei zB eingabedatei und machst es folgend.

Code: Alles auswählen

#!/bin/bash
treffer=no

while read string                # Versuch 1
do
  if [ $string = "a" ]
  then
    treffer=yes
    echo $treffer
  fi
  break
done <   eingabedatei
echo $treffer
Mit ein wenig mehr Kenntnissen in der shellprogrammierung sind dann noch mehr Tricks und andere Umleitungen möglich.

hoffe das hilft dir erst einmal weiter.

robi
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

gaw hat geschrieben:mit einer pipe die Anweisung in einer Subshell ausgeführt wird
Dachte ich erst auch. Aber Subshell heisst Subprocess. Dann probiere mal folgendes:

Code: Alles auswählen

#!/bin/bash 

treffer=no 

echo 'a' | while read string   # Versuch 1 
# for string in a                # Versuch 2 
# string=a ; while [ 1 = 1 ]     # Versuch 3 
do 
  if [ $string = "a" ] 
  then 
    treffer=yes 
    echo $treffer 
    echo $$
  fi 
  break 
done 
echo $treffer
echo $$
Gut, gell? Oder stehe ich einfach auf dem Schlauch...
gaw
Hacker
Hacker
Beiträge: 464
Registriert: 28. Sep 2004, 16:33

Beitrag von gaw »

Ich würde mich nicht darauf verlassen dass die echo $$ Anweisung zeitkritisch ohne Pufferung ausgeführt wird. Außerdem ist nicht immer gesagt wann und wie die bash eine Subshell ausführt, das hängt damit zusammen wie die Befehle intern gruppiert werden. Zeitkritische Multiprozess- oder Mulithreadabläufe werden auch nicht unbedingt mit Skripten programmiert. Vorsicht ist auf jeden Fall geboten.
Eine Subshell wird auf jeden Fall dann ausgeführt wenn sich die Pipe innerhalb der Schleife befindet. Bei komplexen Befehlen würde ich davon ausgehen, das dies passieren kann. Ob dann echo $$ so rasch gepuffert wird, ist halt die Frage. Dein Zähler funktioniert zum Beispiel so:

Code: Alles auswählen

#!/bin/bash

treffer=1

echo -e "a\na\na\na\nb\na\nb" | while read i;
do
  if [ "$i" == "a" ];
  then
    treffer=$((treffer +1));
    echo $treffer;
  fi
done

echo $treffer
ganz zufriedenstellend, obwohl ich wc -l bevorzuge. Dadurch spare ich mir die zeitaufwendige while Schleife mit einzelnen Subshellanweisungen. Hier wird die ganze Schleife nicht als lokale Subshell angelegt.

Ansonsten ist mir nicht ganz klar was das mit dem echo soll und worauf du hinauswillst. Warum sollte man eine einzelne Zeile per echo durch eine pipe schicken und mit while read auffangen?

mfG
gaw
Benutzeravatar
oc2pus
Ultimate Guru
Ultimate Guru
Beiträge: 6506
Registriert: 21. Jun 2004, 13:01

Beitrag von oc2pus »

so ginge es ...
notoxp hat geschrieben:

Code: Alles auswählen

#!/bin/bash 
treffer=no 

echo 'a' | while read string   # Versuch 1 
# for string in a                # Versuch 2 
# string=a ; while [ 1 = 1 ]     # Versuch 3 
do 
  if [ $string = "a" ] 
  then 
    treffer2=yes 
    echo $treffer 
    echo $treffer2
    treffer=$treffer2  # damit wird der "äussere Wert geschrieben" 
    echo $$
  fi 
  break 
done 
echo $treffer
echo $$
http://www.tldp.org/LDP/Bash-Beginners- ... 03_02.html
Kapitel 3.2.3
tell people what you want to do, and they'll probably help you to do it.
PackMan
LinWiki : Das Wiki für Linux User
Benutzeravatar
oc2pus
Ultimate Guru
Ultimate Guru
Beiträge: 6506
Registriert: 21. Jun 2004, 13:01

Beitrag von oc2pus »

gaw hat geschrieben:Ansonsten ist mir nicht ganz klar was das mit dem echo soll und worauf du hinauswillst. Warum sollte man eine einzelne Zeile per echo durch eine pipe schicken und mit while read auffangen?
ich denke ihm geht es nur um das Problem, wie die Variablen sichtbar werden in den einzelnen Blöcken. Und um es zu vereinfachen hat er es auf das "wesentliche" reduziert ...
tell people what you want to do, and they'll probably help you to do it.
PackMan
LinWiki : Das Wiki für Linux User
reini123

Beitrag von reini123 »

[quote="oc2pus]ich denke ihm geht es nur um das Problem, wie die Variablen sichtbar werden in den einzelnen Blöcken.[/quote]

Hi,
nimmer so ganz neu die Problmematik ;-)
http://www.linux-forum.biz/forum.php?st ... hreadid=53
Ich glaub dort hab ich nicht zum ersten, aber zum letzen mal das Problem gesehen #fg#
http://www.google.com/search?hl=de&q=lo ... uche&meta=
Da sind mindestens 20 Treffer zu dem Thema LOL
Hatt schon ein bisserl nen Bart #grins#

Mƒg ®êïñï
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

oc2pus hat geschrieben: Und um es zu vereinfachen hat er es auf das "wesentliche" reduziert ...
So ist es. Die eigentliche Aufgabe wurde mitlerweile auf eine andere Weise gelöst. Nur verstanden hatte ich es nicht.
nimmer so ganz neu die Problmematik ;-)
Für mich schon, weil ich die Shell-Programmierung unter DEC UNIX erlernt habe.
Was mich stutzig machte, ist dass die Kornshells der verschiedenen Unix-Derivate unterschiedlich reagierten. Aber das ist nicht der erste Unterschied, den ich zwischen Linux, SCO und TRU64 entdeckt habe ...
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

oc2pus hat geschrieben:

Code: Alles auswählen

#!/bin/bash 
treffer=no 

echo 'a' | while read string   # Versuch 1 
# for string in a                # Versuch 2 
# string=a ; while [ 1 = 1 ]     # Versuch 3 
do 
  if [ $string = "a" ] 
  then 
    treffer2=yes 
    echo $treffer 
    echo $treffer2
    treffer=$treffer2  # damit wird der "äussere Wert geschrieben" 
    echo $$
  fi 
  break 
done 
echo $treffer
echo $$
Sorry, aber das funktioniert bei mir (SLES8 ) nicht.
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

gaw hat geschrieben:

Code: Alles auswählen

#!/bin/bash

treffer=1

echo -e "a\na\na\na\nb\na\nb" | while read i;
do
  if [ "$i" == "a" ];
  then
    treffer=$((treffer +1));
    echo $treffer;
  fi
done

echo $treffer
Funktioniert leider auch nicht ...
Benutzeravatar
oc2pus
Ultimate Guru
Ultimate Guru
Beiträge: 6506
Registriert: 21. Jun 2004, 13:01

Beitrag von oc2pus »

hm, strange ...
ich habe es auf einer SuSE 9.2 mit bash-3.0-8.2 ausprobiert.

SuSE-9.1 und SuSE-8.2 mit
bash --version
GNU bash, version 2.05b.0(1)-release (i586-suse-linux)
Copyright (C) 2002 Free Software Foundation, Inc.
geht ebenfalls nicht ...
Zuletzt geändert von oc2pus am 1. Feb 2005, 22:01, insgesamt 1-mal geändert.
tell people what you want to do, and they'll probably help you to do it.
PackMan
LinWiki : Das Wiki für Linux User
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

[quote="oc2pus"]hm, strange ...
ich habe es auf einer SuSE 9.2 mit bash-3.0-8.2 ausprobiert.[quote]"man bash" sagt mir am Ende "GNU Bash-2.05b"
Benutzeravatar
oc2pus
Ultimate Guru
Ultimate Guru
Beiträge: 6506
Registriert: 21. Jun 2004, 13:01

Beitrag von oc2pus »

man-pages sind immer "Stiefkinder" und evtl nicht up-to-date

probier mal bash --version

meine Version (auf der 9.2)
bash --version
GNU bash, version 3.00.0(1)-release (i586-suse-linux)
Copyright (C) 2004 Free Software Foundation, Inc.
tell people what you want to do, and they'll probably help you to do it.
PackMan
LinWiki : Das Wiki für Linux User
reini123

Beitrag von reini123 »

Ohne Wort ;-)
-----------------------------schnipp---------------------------
#!/bin/bash
treffer=1
echo $treffer out $$
ps -C `basename $0`
echo 'a' | while read string
do
if [ $string = "a" ]
then
treffer=2
echo $treffer in $$
ps -C `basename $0`
fi
break
done
echo $treffer nacher $$
ps -C `basename $0`
------------------------------------schnapp------------------------------

Mƒg ®êïñï
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

oc2pus hat geschrieben:probier mal bash --version
bash --version
GNU bash, version 2.05b.0(1)-release (i586-suse-linux)
Copyright (C) 2002 Free Software Foundation, Inc.
Benutzeravatar
oc2pus
Ultimate Guru
Ultimate Guru
Beiträge: 6506
Registriert: 21. Jun 2004, 13:01

Beitrag von oc2pus »

jo, siehe oben meinen Nachtrag, sowohl SuSE-8.2 als auch 9.1 mit bash 2.05b "fressen" meine gepostete Version nicht, die bash-3.x nimmt es.

Toll, wie immer steckt der Teufel im Versionsdetail :)
tell people what you want to do, and they'll probably help you to do it.
PackMan
LinWiki : Das Wiki für Linux User
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

reini123 hat geschrieben:Ohne Wort ;-)
-----------------------------schnipp---------------------------
#!/bin/bash
treffer=1
echo $treffer out $$
ps -C `basename $0`
echo 'a' | while read string
do
if [ $string = "a" ]
then
treffer=2
echo $treffer in $$
ps -C `basename $0`
fi
break
done
echo $treffer nacher $$
ps -C `basename $0`
------------------------------------schnapp------------------------------

Mƒg ®êïñï
Na sowas! Ich dachte ich hatte das abgecheckt. Mit sleep 10 und zeitgleichem ps ... Na dann ist alles klar eigentlich ...
Benutzeravatar
regexer
Advanced Hacker
Advanced Hacker
Beiträge: 1005
Registriert: 3. Dez 2004, 09:29
Wohnort: $_

Beitrag von regexer »

So geht es übrigens auf jeden Fall - eigentlich einfach:

Code: Alles auswählen

#!/bin/bash
echo 'a' | while read string
do
  if [ $string = "a" ]
  then
    echo treffer
    break
  fi
done | grep treffer >/dev/null
if [ $? = 0 ]
then
  echo TREFFER
else
  echo KEIN TREFFER
fi
Antworten