bash while -> do -> done mit Variablen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
s837ubc
Beiträge: 133
Registriert: 23.07.2013 14:17:01

bash while -> do -> done mit Variablen

Beitrag von s837ubc » 21.06.2016 17:10:05

Hallo,

in einem bash-Script wird eine Variable mit Typ Integer auf den Wert 0 initialisiert. Innerhalb der while-Schleife wird die auf 1 gesetzt. Dennoch wird am Ende ene 0 ausgegeben:

Code: Alles auswählen

#!/bin/bash

typeset -i varA
varA=0
echo $varA
rsync 192.168.188.140::hosts/  | rev | cut -d' ' -f1 | rev | while read line; do

varA=1
echo $varA
break;

done;

echo $varA
Beim Ausführen wird ausgegeben:

0
1
0


Erwartet hätte ich folgende Ausgabe:

0
1
1


Kann mir jemand mitteilen, warum die Variable $varA am Ende immer den Wert 0 aufweist?

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: bash while -> do -> done mit Variablen

Beitrag von heisenberg » 21.06.2016 17:20:08

Dieses Konstrukt ist speziell:

Code: Alles auswählen

var=PRE
echo "pre: $var"
command | while read var ; do
   echo "in loop: $var"
done
echo "after: $var"
Aufgrund der Art und Weise wie die Kommandos hier aufgeführt werden, ist var eine lokale Variable, die nur während der while-Schleife gültig ist, da die While-Schleife in einem Sub-Prozess ausgeführt wird. Der Hauptprozess verwendet eine andere Kopie der Variablen.

Das was Du willst, erreichst Du so:

Code: Alles auswählen

var=PRE
echo "pre: $var"
while read var ; do
   echo "in loop: $var"
done < <( command )
echo "after: $var"
Damit ist die while-Schleife im aktuellen Prozess und die Variablenzuweisung bleibt erhalten.

s837ubc
Beiträge: 133
Registriert: 23.07.2013 14:17:01

Re: bash while -> do -> done mit Variablen

Beitrag von s837ubc » 21.06.2016 17:28:12

Hallo,

in der Ausgabe von rsync 192.168.188.140::hosts/ kommt ein einfaches Verzeichnis Inhalt heraus, wie dieser auch mit ls -l erzeugt werden kann.

Damit die Situation besser nachvollziehbar ist, hier noch ein allgemeineres Beispiel, in dem das gleiche Fehlerverhalten ebenfalls vorkommt:

Code: Alles auswählen

#!/bin/bash

typeset -i varA
varA=0
echo $varA
ls -l / | rev | cut -d' ' -f1 | rev | while read line; do

varA=1
echo $varA
break;

done;

echo $varA
Im Verzeichnis / muss mindestens ein Eintrag vorhanden sein, damit das Script funktioniert.

Ausgabe:

0
1
0


Erwartet hätte ich:

0
1
1

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: bash while -> do -> done mit Variablen

Beitrag von heisenberg » 21.06.2016 17:34:22

Habe meinen Post nochmal editiert, da ich erst später die Ursache erkannt habe. Also: Siehe oben.

s837ubc
Beiträge: 133
Registriert: 23.07.2013 14:17:01

Re: bash while -> do -> done mit Variablen

Beitrag von s837ubc » 21.06.2016 17:39:39

Hallo heisenberg,

viele Dank für die Info.

Auf dieses Konstrukt wäre ich nie gekommen.

Nachtrag
=======
Es scheint doch nicht so einfach zu sein, die Methode umzusetzen:

Code: Alles auswählen

#!/bin/bash

typeset -i varA
varA=0
echo $varA

lines=`ls / | rev | cut -d' ' -f1 | rev`

while read "$lines" ; do

varA=1
echo $varA
break;

done;

echo $varA
Dieser Code läuft auf einen Fehler in Zeile 11 (while read "$lines") auf:

'Inhalt von lines' ist kein gültiger Bezeichner.

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: bash while -> do -> done mit Variablen

Beitrag von heisenberg » 21.06.2016 18:07:52

Code: Alles auswählen

    #!/bin/bash

    typeset -i varA
    varA=0
    echo $varA

    while read LINE ; do
          
    echo "$LINE"
    varA=1
    echo $varA
    break;

    done < <( ls / | rev | cut -d' ' -f1 | rev )

    echo $varA

s837ubc
Beiträge: 133
Registriert: 23.07.2013 14:17:01

Re: bash while -> do -> done mit Variablen

Beitrag von s837ubc » 21.06.2016 18:15:55

So geht es auch... :-)

Habe zwischenzeitlich auch eine for - Schleife gefunden, die ebenfalls funktioniert:

Code: Alles auswählen

#!/bin/bash

typeset -i varA
varA=0
echo $varA

lines=`ls / | rev | cut -d' ' -f1 | rev`

IFS=$'\n'
for line in $lines;
do
        echo "$line"
        varA=1
        echo $varA
        break;
done;

echo $varA
Aber Deine Lösung ist besser nachvollziehbar.

Vielen Dank nochmals für Deine Unterstützung.

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: bash while -> do -> done mit Variablen

Beitrag von heisenberg » 21.06.2016 18:18:07

Habe zwischenzeitlich auch eine for - Schleife gefunden, die ebenfalls funktioniert:
Ja, solange Du keine (eingeschlossenen) Leerzeichen oder Tabs innerhalb der Zeile/Dateinamen hast, funktioniert Deine Lösung auch.

Ups. Sehe gerade, dass Du ja IFS setzt. Deine Lösung funktioniert also auch mit Leerzeichen/Tabs. Praktischerweise sichert man IFS und setzt es anschliessend wieder zurueck um unerwünschte Seiteneffekte im restl. Code zu vermeiden.

Code: Alles auswählen

IFS_OLD="$IFS"
# fancy code here <----
IFS="$IFS_OLD"
... und dieses hier erzeugt auch nur zufällig keine Fehler(Das Dollarzeichen muss weg):

Code: Alles auswählen

IFS=$'\n'

Benutzeravatar
hikaru
Moderator
Beiträge: 13904
Registriert: 09.04.2008 12:48:59

Re: bash while -> do -> done mit Variablen

Beitrag von hikaru » 21.06.2016 21:49:06

Verstehe ich es richtig, dass das ursprüngliche Script (mit rsync) prüfen soll, ob ein Verzeichnis einen Inhalt hat?
Dann ist die Lösung mit rsync und einer Schleife in einem Script sicher "kreativ", aber das geht auch deutlich einfacher. wc könnte z.B. dein Freund werden.

Benutzeravatar
Phineas
Beiträge: 354
Registriert: 20.06.2012 20:26:19

Re: bash while -> do -> done mit Variablen

Beitrag von Phineas » 22.06.2016 04:40:32

heisenberg hat geschrieben: ... und dieses hier erzeugt auch nur zufällig keine Fehler(Das Dollarzeichen muss weg):

Code: Alles auswählen

IFS=$'\n'
Ohne das Dollarzeichen wird das kein Newline, sondern ein schnödes n. Bash-Spezial.

Benutzeravatar
heisenberg
Beiträge: 4123
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: bash while -> do -> done mit Variablen

Beitrag von heisenberg » 22.06.2016 12:10:56

Phineas hat geschrieben:
heisenberg hat geschrieben: ... und dieses hier erzeugt auch nur zufällig keine Fehler(Das Dollarzeichen muss weg):

Code: Alles auswählen

IFS=$'\n'
Ohne das Dollarzeichen wird das kein Newline, sondern ein schnödes n. Bash-Spezial.
Asche auf mein Haupt für das bemängeln von richtigem Code + Danke für diese Information.

Benutzeravatar
honeywell
Beiträge: 21
Registriert: 06.03.2016 12:52:45

Re: bash while -> do -> done mit Variablen

Beitrag von honeywell » 06.08.2016 08:23:19

Phineas hat geschrieben: Ohne das Dollarzeichen wird das kein Newline, sondern ein schnödes n. Bash-Spezial.
Sorry, wenn ich mich in diese Diskussion hinein hänge. Ich bin auf der Suche nach einer Erklärung für die Schreibweise IFS=$...

Auf TLDP bin ich auf den Kommentar "Per suggestion of Anton Filippov" gestoßen. Aber auch eine Suche nach diesem Namen im Zusammenhang mit IFS findet nur viele Webseiten, die die Zeile von TLDP nur kopiert haben. Aber bei der Suche bin ich auch auf diesen Beitrag gestoßen.

Ich habe folgendes probiert:

Code: Alles auswählen

IFS='\n'
echo $IFS | od -xc
Ausgabe;

Code: Alles auswählen

0000000    000a
         \n
0000001
Demnach bewirkt das $-Zeichen gar nichts. Es kommt also doch '\n' heraus, und nicht nur "ein schnödes n".

Warum soll man also dir Syntax mit dem $-Zeichen wählen? Warum suggeriert uns Anton Filippov das?

Benutzeravatar
Phineas
Beiträge: 354
Registriert: 20.06.2012 20:26:19

Re: bash while -> do -> done mit Variablen

Beitrag von Phineas » 06.08.2016 18:24:35

honeywell hat geschrieben:Ich habe folgendes probiert:

Code: Alles auswählen

IFS='\n'
echo $IFS | od -xc
Das echo erzeugt ein eigenes Newline und es fehlen doppelte Anführungszeichen, Dein Test ist Fehlerhaft. So gehts:

Code: Alles auswählen

echo -n "$IFS" | od -xc
Statt echo -n geht natürlich auch printf.

Benutzeravatar
honeywell
Beiträge: 21
Registriert: 06.03.2016 12:52:45

Re: bash while -> do -> done mit Variablen

Beitrag von honeywell » 06.08.2016 18:42:36

Danke für den Tipp. (Hätte ich auch selbst drauf kommen können.)

Nun habe ich probiert:

Code: Alles auswählen

S='\n' # gleiches Ergebnis mit S="\n" und S=\\n und S=$"\n"
echo -n "$S" | od -cx
0000000   \   n
           6e5c
0000002

S=$'\n'
echo -n "$S" | od -cx
0000000  \n
           000a
0000001
Heißt das, dass die Bash nur dann Sonderzeichen bei der Variablenexpansion richtig interpretiert, wenn die letzte Syntax verwendet wird? Auf die Bourne-Shell trifft das nämlich nicht zu, habe ich auch probiert.

Benutzeravatar
Phineas
Beiträge: 354
Registriert: 20.06.2012 20:26:19

Re: bash while -> do -> done mit Variablen

Beitrag von Phineas » 06.08.2016 19:36:37

Auf die Schnelle finde ich folgenden Abschnitt in der Manpage:
man bash hat geschrieben:Words of the form $'string' are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. Backslash escape sequences,
if present, are decoded as follows
...
Es sollte also darum gehen, die Escape-Sequenz \n zu interpretieren.

Benutzeravatar
honeywell
Beiträge: 21
Registriert: 06.03.2016 12:52:45

Re: bash while -> do -> done mit Variablen

Beitrag von honeywell » 06.08.2016 20:55:51

OK, Danke! Wieder etwas dazu gelernt. :THX:

Antworten