• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

Zähler nach einer while-Schleife behalten

Hallo,

da eine while-Schleife in einer eigenen Subshell läuft, ist der Zähler nachdem die Schleife durchlaufen ist wieder weg. Wie kann ich den weiter nutzen? Spontan fällt mir nur ein den Zähler in eine Textdatei zu exportieren.

bf
 

framp

Moderator
Teammitglied
Du kannst den Wert des Zählers in der Subshell zurückgeben.

Hast Du einen Codesnippet den Du uns zeigen kannst?
 
OP
B

bunter fisch

Member
Ich will ihn ja nicht in der Subshell wiedergeben, ich will ihn danach weiter nutzen.

Code:
i=0
while [ $i -lt $var1 ]; do
 echo "$i"
 i=`expr $i + 1`
done

i=0
while [ $i -lt $var2 ]; do
 echo "$i"
 i=`expr $i + 1`
done

echo "$summe"

Zum einen soll jede Schleife wieder bei i=0 beginnen mit ihrem Durchlauf und entsprechend oft laufen. WEnn also var1=1 und var2=2 sind, würde die erste Schleife einen und die zweite Schleife 2 Durchläufe machen. Am Ende soll zusätzlich noch eine Variable $summe angeben wieviel Durchläufe insgesamt durchlaufen sind. Nach Schleife 1 müsste $summe also 1 und nach Schleife 2 3 sein.
 
Das sieht seltsam aus, was du da machst!

Code:
#!/bin/bash

x_test()
{
	local i s=0 va="$1" vb="$2"
	for((i=0; i < $va; i++,s++)); do
 	     		printf "i = %d\n" "$i"
	done
	for((i=0; i < $vb; i++,s++)); do
	    			printf "i = %d\n" "$i"
	done
	printf "s = %d\n" "$s"
	return 0
}

x_test 1 2

exit 0
Ich habe es nicht getestet
 
A

Anonymous

Gast
bunter fisch schrieb:
Am Ende soll zusätzlich noch eine Variable $summe angeben wieviel Durchläufe insgesamt durchlaufen sind. Nach Schleife 1 müsste $summe also 1 und nach Schleife 2 3 sein.
entsprechend deinem geschilderten Ablaufarithmetik im Script ist bei dir summe immer (va + vb), warum willst du das unnötig verkomplizieren in dem du unnötig versucht Variablen über Subshellgrenzen zu transponieren?

robi
 

abgdf

Guru
bunter fisch schrieb:
Ich will ihn ja nicht in der Subshell wiedergeben, ich will ihn danach weiter nutzen.
Manchmal helfen da geschleifte Klammern.

Aber insgesamt ist das sicher ein Punkt, an dem die Shell mir nur wenig kontrollierbar scheint (und selbst Seiten wie der "Advanced Bash-Scripting Guide" helfen da nicht wirklich weiter).
Ich würde Dir daher raten, Dir mal Perl anzugucken. Das verhält sich in dem Punkt nämlich konsequent vorhersehbar.
Code:
#!/usr/bin/perl
use warnings;
use strict;

my $var1 = 1;
my $var2 = 2;
my $summe = 0;

my $i = 0;
while ($i < $var1) {
 print "$i\n";
 $i++;
 $summe++;
}

$i = 0;
while ($i < $var2) {
 print "$i\n";
 $i++;
 $summe++;
}

print "$summe\n";
Und mir hat es auch Spaß gemacht, es zu lernen. Hab's nie bereut.
 
A

Anonymous

Gast
bunter fisch schrieb:
da eine while-Schleife in einer eigenen Subshell läuft, ist der Zähler nachdem die Schleife durchlaufen ist wieder weg.

Das stimmt so pauschal erstmal nicht.
Code:
i=0; while [ $i -lt 9 ]; do echo "intern = $i"; i=$(($i+1)); done  ; echo "extern = $i"
intern = 0
intern = 1
intern = 2
intern = 3
intern = 4
intern = 5
intern = 6
intern = 7
intern = 8
extern = 9
Das ist eine Schleife von 0 bis <9, und nach der while-Schleife hat die Schleifen Variable den Wert 9, das ist normal weil das der Abbruchwert von der Schleife ist. Die while-Schleife ist hier auch gar nicht in einer Subshell gelaufen.

Allerdings, kleine Abwandlung, ich starte die while-Schleife in einer Pipe und übergebe ihr in der Pipe die Ausgabe von "echo hallo"
Code:
i=0; echo hallo | while [ $i -lt 9 ]; do read D ; echo "intern = $i $D "; i=$(($i+1)); done ; echo "extern = $i"
intern = 0 hallo 
intern = 1  
intern = 2  
intern = 3  
intern = 4  
intern = 5  
intern = 6  
intern = 7  
intern = 8  
extern = 0
Jetzt ist hinter der while-Schleife die Variable nicht mehr gesetzt, weil die while-Schleife jetzt in der Pipe in einer eigenen Subshell gelaufen ist. Das hat aber nichts mit der While-Schleife an sich zu tun, die Pipe hat die Subshell eröffnet.

Sowas kann man in der Bash versuchen zu umgehen, indem man versucht das in der Subshell zu startet, von wo man später zB keine Variable mehr braucht. Also in unseren Fall nicht das "echo hallo" in der aktuellen Shell und die While-Schleife in der Subshell, sondern genau anders herum.
Die While-Schleife in der aktuellen Shell und das "echo hallo" ausgelagert in eine Subshell , zB.:
Code:
i=0; while [ $i -lt 9 ]; do read D ; echo "intern = $i $D "; i=$(($i+1)); done < <(echo hallo) ; echo "extern = $i"
intern = 0 hallo 
intern = 1  
intern = 2  
intern = 3  
intern = 4  
intern = 5  
intern = 6  
intern = 7  
intern = 8  
extern = 9
und schon hat man hinter der Schleife in der Variable wieder den Wert der innerhalb der while-Schleife gelaufen ist. (Achtung, diese Kommand-Substitution geht wohl nur in der Bash und ksh, die orginal (einfache) sh kennt dieses wahrscheinlich nicht)

robi
 

abgdf

Guru
robi schrieb:
Das stimmt so pauschal erstmal nicht.
Und das ist genau das Problem: In manchen Situationen sind solche Variablen dann noch da, und manchmal sind sie weg. Ein exaktes Muster, wann das so und wann das anders passiert, kann ich nicht erkennen.
Wenn Du das Muster dafür beschreiben kannst, wäre ich ganz Ohr.
 

framp

Moderator
Teammitglied
@abgdf Es ist in der bash definiert: Eine Subshell kann Variablen der Parentshell nicht aendern.

@robi hat Beispiele gegeben in dem
1) Das Beispiel des TEs etwas erweitert und es so funktioniert wie Du es in perl tust da keine Subshell benutzt wird
2) Ein Beispiel gegeben wo eine Subshell benutzt wird und es deshalb nicht funktioniert (eine Pipe läuft in einer Subshell)
3) Ein Beispiel in bash gegeben wie man das Problem in (2) umgeben kann
 

abgdf

Guru
framp schrieb:
robi hat Beispiele gegeben in dem
1) Das Beispiel des TEs etwas erweitert und es so funktioniert wie Du es in perl tust da keine Subshell benutzt wird
2) Ein Beispiel gegeben wo eine Subshell benutzt wird und es deshalb nicht funktioniert (eine Pipe läuft in einer Subshell)
3) Ein Beispiel in bash gegeben wie man das Problem in (2) umgeben kann
Beispiele sind schonmal gut. Dann würde mich noch die allgemeine Regel interessieren, wo das Problem auftritt und wo nicht.
In welchen Fällen kommt es zur Eröffnung einer Subshell?
Was passiert, wenn sie schließt?
Kann man Variablen aus der Subshell zur Weiterverarbeitung in die übergeordnete Shell übertragen (was ja wohl auch die Frage des TE war)?
Was ist eine Subshell überhaupt?
Wie steuert die Hauptshell die Subshell?
Wie funktioniert die Übergabe von Variablen aus der Hauptshell an die Subshell?
Kann man auch Arrays übertragen (in die Subshell, aus der Subshell)?
Oder vielleicht Referenzen auf Datenstrukturen? ;)
Erzeugt jeder Schleifendurchlauf eine eigene Subshell? Oder eine while-Schleife insgesamt nur eine Subshell?
Gibt es eine maximale Anzahl von Subshells?
Kann man Kommandos an die Subshell übertragen, während sie läuft (vgl. subprocess-Modul in Python)?
Was hat es mit den geschweiften Klammern in bash auf sich, und warum kann man damit manchmal Subshells unterbinden, obwohl sie doch eigentlich erforderlich sein müßten (sonst wären sie ja überflüssig)?
Es bleibt also noch Vieles, was noch nicht so ganz klar ist.
 
A

Anonymous

Gast
abgdf schrieb:
In welchen Fällen kommt es zur Eröffnung einer Subshell?
Was passiert, wenn sie schließt?
...?
...?
Es bleibt also noch Vieles, was noch nicht so ganz klar ist.
Die einfache Antwort wird dir nicht gefallen : 42

Alles andere ist gar nicht so schrecklich kompliziert, aber bestimmt alles andere als einfach zu erklären und wird wohl auch nicht in wenigen Zeilen zu erledigen sein.
Lasst mir mal ein bisschen Zeit, ich versuch mal einen Wiki-Beitrag zu dem Thema zu erstellen, ich denke das ist Allgemein und als Referenz durchaus von Interesse.

Das soll aber nicht heißen dass ihr solange warten müsst, bitte hier im Forum weiter diskutieren und auch schon versuchen Teile davon zu beantworten damit ich noch ein bisschen mehr Stoff und Anregungen für den Zusammenbau des Wiki-Beitrags bekomme. Wer jetzt im Forum schon Teilantworten hat, oder weiteres hierzu brauchbares, Halbfertiges zuliefern kann immer her damit, auch weitere Fragen in diesem Zusammenhang sind noch willkommen.
Notfalls schneiden wir das dann hier aus dem Thread raus und machen einen eigenen Thread daraus, damit es übersichtlich bleibt.

robi
 

framp

Moderator
Teammitglied
Whow ... das sind eine Menge Fragen.
Kann man Variablen aus der Subshell zur Weiterverarbeitung in die übergeordnete Shell übertragen?
Ich kenne zwei Wege:
1) per echo
2) per printf -v Option

Code:
#!/bin/bash

add1() { # add two numbers and return result via echo
        echo $(( $1 + $2 ))
}

add2() { # add two numbers and return result in variable, name passed in first parm
        local r
        (( r = $2 + $3 ))
        printf -v $1 "%s" $r
}

testData=("1 2" "3 4" "5 6")
result=""
for d in "${testData[@]}"; do
        read -r a b <<< "$d"
        echo "add1: $a + $b = $(add1 $a $b)"
        add2 result $a $b
        echo "add2: $a + $b = $result"
done
 
A

Anonymous

Gast
framp schrieb:
Kann man Variablen aus der Subshell zur Weiterverarbeitung in die übergeordnete Shell übertragen?
Ich kenne zwei Wege:
1) per echo
2) per printf -v Option

Wenn du aufzeigen willst wie man Variablen aus einer Subshell in der übergeordneten Shell weiterverarbeite kann, musst du aber wenigstens eine übergeordenet Shell und eine Subshell zur Demonstration verwenden ;)
Dein Beispielscript arbeitet alles in einer einzigen Shell ab auch die Funktionen, du sagst zwar die Variable r, soll nur innerhalb der Funktion gültig sein, das ist etwas anderes als eine Subshell. locale und globale Variablen gibts in fast jeder Programmiersprache.

einfach mal die "local r" Deklaration aus der add2() auskommentieren,
und schon kannst du ganz am Ende vom Script mit "echo $r" auf die Variable zugreifen die in der Funktion add2() gesetzt wird und wirst bei deinem Beispieldaten 11 erhalten. Das wäre nicht möglich, wenn die Funktion in einer Subshell abgearbeitet wird, weil dann die Variableinhalte aus der Subshell nicht mehr vorhanden sind, sobald die Subshell endet.

Ich sage nicht um sonst, es ist nicht ganz so leicht es einigermaßen verständlich zu erklären. ;)

robi
 

framp

Moderator
Teammitglied
robi schrieb:
Ich sage nicht um sonst, es ist nicht ganz so leicht es einigermaßen verständlich zu erklären. ;)
Offensichtlich hatte ich ein falsches Verstaendnis von subshells :eek:ps: . Nach Lesen von http://www.tldp.org/LDP/abs/html/subshells.html habe ich ( ) bei add1 und add2 benutzt um eine Subshell zu erzeugen und bei add2 kommt jetzt nichts mehr zurueck :eek:0: Demnach funktioniert der printf -v Ansatz nur bei Funktionen aber nicht bei Subprocesses. Bin mal gespannt welche Methoden Du zusammentraegst ausser mit echo :)
 
Ihr verwechselt hier etwas Grundsätzliches.
Beiliegend ein Beispiel von reinen Subshells.
main() ist die Konsole selbst, in der der user eine SHELL aufruft.
Diese SHELL startet EINE sub_shell, die ihrerseits jedesmal eine NEUE sub_shell erzeugt. (nested sub_shell)
Bitte auf pid und parent_pid achten, die sich auch bei nested sub_shells immer auf main() als parent UND das PID auf die SHELL beziehen.
Es findet kein fork statt, $BASHPID ist nur ein künstliches Konstrukt und so auch jede sub_shell. Jede andere Form sind sub_processes mit
dem Resultat eines eigenen PIDs und parent auf SHELL und nicht main(). Das kann sein ein fork(), ein thread() oder ein simpler call nach system();
Am Beispiel TE wird unnötigerweise ein sub_process gestartet. Auch read() tut das, selbst wenn als "internes Kommando" deklariert.
Dieser Unterschied sollte beachtet werden.
Code:
#!/bin/bash

printf "main(%d): spawn shell %d\n" "$PPID" "$BASHPID"
printf "  shell(%d): start subshells ..\n" "$$"

(
i=0
while((i++ < 3)); do
	printf "    subshell(%d): number %d, PID %d, parent %d\n" "$BASHPID" "$BASH_SUBSHELL" "$$" "$PPID"
	sleep 1
(
	while true; do
		printf "    subshell(%d): number %d, PID %d, parent %d\n" "$BASHPID" "$BASH_SUBSHELL" "$$" "$PPID"
		sleep 1
		break
	done
	)
done
)

printf "  shell(%d): exit to main %d ..\n" "$$" "$PPID"
printf "EXIT\n"
exit 0

Gruß
Gräfin Klara
 
Oben