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

Zähler nach einer while-Schleife behalten

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

Moderator: Moderatoren

Antworten
bunter fisch
Member
Member
Beiträge: 106
Registriert: 12. Sep 2017, 10:25

Zähler nach einer while-Schleife behalten

Beitrag von bunter fisch » 21. Jun 2019, 14:48

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

Werbung:
Benutzeravatar
framp
Moderator
Moderator
Beiträge: 4303
Registriert: 6. Jun 2004, 20:57
Wohnort: bei Stuttgart
Kontaktdaten:

Re: Zähler nach einer while-Schleife behalten

Beitrag von framp » 21. Jun 2019, 15:31

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

Hast Du einen Codesnippet den Du uns zeigen kannst?

bunter fisch
Member
Member
Beiträge: 106
Registriert: 12. Sep 2017, 10:25

Re: Zähler nach einer while-Schleife behalten

Beitrag von bunter fisch » 21. Jun 2019, 18:57

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

Code: Alles auswählen

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.

Gräfin Klara
Hacker
Hacker
Beiträge: 413
Registriert: 23. Jun 2008, 20:51

Re: Zähler nach einer while-Schleife behalten

Beitrag von Gräfin Klara » 21. Jun 2019, 19:56

Das sieht seltsam aus, was du da machst!

Code: Alles auswählen

#!/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

Benutzeravatar
robi
Moderator
Moderator
Beiträge: 3169
Registriert: 25. Aug 2004, 02:13

Re: Zähler nach einer while-Schleife behalten

Beitrag von robi » 21. Jun 2019, 21:27

bunter fisch hat geschrieben:
21. Jun 2019, 18:57
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
Guru
Beiträge: 3193
Registriert: 13. Apr 2004, 21:15

Re: Zähler nach einer while-Schleife behalten

Beitrag von abgdf » 22. Jun 2019, 00:09

bunter fisch hat geschrieben:
21. Jun 2019, 18:57
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: Alles auswählen

#!/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.

Benutzeravatar
robi
Moderator
Moderator
Beiträge: 3169
Registriert: 25. Aug 2004, 02:13

Re: Zähler nach einer while-Schleife behalten

Beitrag von robi » 22. Jun 2019, 01:47

bunter fisch hat geschrieben:
21. Jun 2019, 14:48
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: Alles auswählen

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: Alles auswählen

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: Alles auswählen

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
Guru
Beiträge: 3193
Registriert: 13. Apr 2004, 21:15

Re: Zähler nach einer while-Schleife behalten

Beitrag von abgdf » 22. Jun 2019, 19:06

robi hat geschrieben: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.

Benutzeravatar
framp
Moderator
Moderator
Beiträge: 4303
Registriert: 6. Jun 2004, 20:57
Wohnort: bei Stuttgart
Kontaktdaten:

Re: Zähler nach einer while-Schleife behalten

Beitrag von framp » 22. Jun 2019, 20:10

@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
Guru
Beiträge: 3193
Registriert: 13. Apr 2004, 21:15

Re: Zähler nach einer while-Schleife behalten

Beitrag von abgdf » 23. Jun 2019, 01:43

framp hat geschrieben:
22. Jun 2019, 20:10
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.

Benutzeravatar
robi
Moderator
Moderator
Beiträge: 3169
Registriert: 25. Aug 2004, 02:13

Re: Zähler nach einer while-Schleife behalten

Beitrag von robi » 23. Jun 2019, 09:39

abgdf hat geschrieben:
23. Jun 2019, 01:43
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

Benutzeravatar
framp
Moderator
Moderator
Beiträge: 4303
Registriert: 6. Jun 2004, 20:57
Wohnort: bei Stuttgart
Kontaktdaten:

Re: Zähler nach einer while-Schleife behalten

Beitrag von framp » 23. Jun 2019, 10:48

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: Alles auswählen

#!/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

Benutzeravatar
robi
Moderator
Moderator
Beiträge: 3169
Registriert: 25. Aug 2004, 02:13

Re: Zähler nach einer while-Schleife behalten

Beitrag von robi » 23. Jun 2019, 11:28

framp hat geschrieben:
23. Jun 2019, 10:48
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

Benutzeravatar
framp
Moderator
Moderator
Beiträge: 4303
Registriert: 6. Jun 2004, 20:57
Wohnort: bei Stuttgart
Kontaktdaten:

Re: Zähler nach einer while-Schleife behalten

Beitrag von framp » 23. Jun 2019, 12:44

robi hat geschrieben:
23. Jun 2019, 11:28
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 :ops: . 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 :o0: Demnach funktioniert der printf -v Ansatz nur bei Funktionen aber nicht bei Subprocesses. Bin mal gespannt welche Methoden Du zusammentraegst ausser mit echo :)

Gräfin Klara
Hacker
Hacker
Beiträge: 413
Registriert: 23. Jun 2008, 20:51

Re: Zähler nach einer while-Schleife behalten

Beitrag von Gräfin Klara » 23. Jun 2019, 13:41

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: Alles auswählen

#!/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

Antworten