[gelöst] Bash: If-Konstrukt mit veränderbaren Variablen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
Tintom
Moderator
Beiträge: 3033
Registriert: 14.04.2006 20:55:15
Wohnort: Göttingen

[gelöst] Bash: If-Konstrukt mit veränderbaren Variablen

Beitrag von Tintom » 27.06.2020 10:31:46

Hallo zusammen,
ich habe wohl schon zu lange auf den Code geschaut, ich finde den Fehler einfach nicht. Wahrscheinlich ist er so offensichtlich, dass er direkt in Auge springt :facepalm:

Das folgende Programm liest die Daten eines Bewegungssensors von einem Tablet aus und vergleicht den ausgelesenen Wert mit einer Variable. Stimmen beide Werte überein passiert nichts, sind sie jedoch unterschiedlich sollen einige Befehle ausgeführt werden.

Wo liegt jetzt der Fehler?
Das Skript startet mit der Annahme, dass beide Werte übereinstimmen und tut erstmal nix. Bewege ich das Tablet dann gibt die Funktion state() korrekt den Wert 1 zurück. Weil die Funktion laststate() den Wert 0 zurückgibt, erwarte ich nun, dass im Skript die Bedingung if [[ laststate -ne state ]] erfüllt ist, ergo der Code zwischen then ... else ausgeführt wird.

Es passiert aber nichts, das Skript sieht weiterhin die else-Bedingung erfüllt (überprüft mit eingestreuten echos an den markanten Punkten im Skript).

Sieht jemand den Fehler?

Code: Alles auswählen

#! /bin/bash
#
SENSOR="/sys/bus/iio/devices/iio:device0/in_accel_y_raw"
#
STATE=/dev/shm/.sensorstate

POINTER='FTSC1000:00 2808:50AB'
TRANSFORM='Coordinate Transformation Matrix'

#For the sake of simplicity let's assume
# 0 = Panorama mode (Desktop)
# 1 = Tablet mode (Bookreader)

function state() {
read -r VALUE <$SENSOR
if [ $VALUE -gt 350 ]; then
	echo 0
else
	echo 1
fi
}

function laststate() {
if [ ! -e $LASTSTATE ]; then
echo 0 > $LASTSTATE
else
cat <$LASTSTATE
fi
}


while true; do

if [[ laststate -ne state ]]
then
#Display wurde gedreht
			if [[ state -eq 0 ]]
			then
			#Desktopmode
			xrandr --output eDP-1 --rotate inverted
			sleep 1 #xrandr needs some time to adjust
			xinput set-prop "$POINTER" "$TRANSFORM" 1 0 0 0 1 0 0 0 1
			echo 0 > $LASTSTATE
			else
			#Tabletmode
			xrandr --output eDP-1 --rotate right
			sleep 1 #xrandr needs some time to adjust
			xinput set-prop "$POINTER" "$TRANSFORM" 0 -1 1 1 0 0 0 0 1
			echo 1 > $LASTSTATE
			fi
else
#Keine Veränderung, gehe schlafen
sleep 3
fi

done
Zuletzt geändert von Tintom am 29.06.2020 08:25:41, insgesamt 1-mal geändert.

JTH
Moderator
Beiträge: 3023
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: Bash: If-Konstrukt mit veränderbaren Variablen

Beitrag von JTH » 27.06.2020 11:13:16

Tintom hat geschrieben: ↑ zum Beitrag ↑
27.06.2020 10:31:46
Sieht jemand den Fehler?

Ja. Du hast bei mehreren ifs das $ vergessen, um Variablen zu dereferenzieren.


Ähm, nevermind. Wer lesen kann :D

Drei Probleme:

Einerseits versucht du, die Ausgabe von zwei Funktionen zu vergleichen. Der Operator -ne vergleicht aber erstmal nur Variableninhalte. Wenn du dort die Ausgabe von Funktionen vergleichen willst, brauchst du eine Command Substitution:

Code: Alles auswählen

if [[ $(laststate) -ne $(state) ]]; then
Das gleiche ein paar Zeilen weiter unten.


Andererseits hat die Funktion laststate im Moment keine/eine leere Ausgabe, wenn deine Datei mit Pfad in LASTSTATE noch nicht existiert. Evtl. willst du (habe den Ablauf nicht weiter angeschaut) immer deren Inhalt ausgeben?

Code: Alles auswählen

function laststate() {
    if [ ! -e $LASTSTATE ]; then
        echo 0 > $LASTSTATE
    fi

    cat $LASTSTATE
}

Außerdem weist du LASTSTATE nie einen Pfad zu – Absicht?
Manchmal bekannt als Just (another) Terminal Hacker.

TomL

Re: Bash: If-Konstrukt mit veränderbaren Variablen

Beitrag von TomL » 27.06.2020 11:53:18

Tintom hat geschrieben: ↑ zum Beitrag ↑
27.06.2020 10:31:46
Wo liegt jetzt der Fehler?
Tja... ich habe aus Spass mal dran rumgeschraubt.... allerdings, weil ich keine solche Sensorik habe, muss VALUE an der Tastatur von Hand eingegeben werden... aber ich denke, daran versteht mal den Ablauf trotzdem. Und statt echt was zu 'tun', gebe ich nur Debug-Nachrichten aus, damit man sieht, was wann passieren würde. Vielleicht hilfts Dir:

Code: Alles auswählen

#! /bin/bash

function getstate() 
{
    read -r VALUE
    if [ $VALUE -gt 350 ]; then
        return 0
    fi
 
    return 1
}

laststate=-1
while true; do
    getstate
    currstate=$?

    echo "deb1: cs=$currstate   ls=$laststate"

    if [[ $laststate -ne $currstate ]]; then
        if [[ $currstate -eq 0 ]]; then
            echo "deb2: 0"
            sleep 1 
        else
            echo "deb3: 1"
            sleep 1 
        fi
        laststate=$currstate
    else
        #Keine Veränderung, gehe schlafen
        echo "deb4: nix"
        sleep 3
    fi
done

exit 0

Benutzeravatar
whisper
Beiträge: 3192
Registriert: 23.09.2002 14:32:21
Lizenz eigener Beiträge: GNU Free Documentation License
Kontaktdaten:

Re: Bash: If-Konstrukt mit veränderbaren Variablen

Beitrag von whisper » 27.06.2020 12:12:13

Ohne jetzt dein Script näher untersucht zu haben ein Tipp:

Ganz allgemein hilft auch Debianshellcheck ganz gut.


Benutzeravatar
Tintom
Moderator
Beiträge: 3033
Registriert: 14.04.2006 20:55:15
Wohnort: Göttingen

Re: Bash: If-Konstrukt mit veränderbaren Variablen

Beitrag von Tintom » 27.06.2020 13:36:20

Vielen Dank an euch für die Antworten!
whisper hat geschrieben: ↑ zum Beitrag ↑
27.06.2020 12:12:13
Ohne jetzt dein Script näher untersucht zu haben ein Tipp:

Ganz allgemein hilft auch Debianshellcheck ganz gut.
Danke, das kannte ich noch aus meinen Anfangstagen mit Debian, aber irgendwie ist es bei mir in Vergessenheit geraten. Das Programm meckert auch gleich die markanten Stellen an, wie z.B.:
JTH hat geschrieben: ↑ zum Beitrag ↑
27.06.2020 11:13:16
Einerseits versucht du, die Ausgabe von zwei Funktionen zu vergleichen. Der Operator -ne vergleicht aber erstmal nur Variableninhalte. Wenn du dort die Ausgabe von Funktionen vergleichen willst, brauchst du eine Command Substitution:

Code: Alles auswählen

if [[ $(laststate) -ne $(state) ]]; then
Das gleiche ein paar Zeilen weiter unten.
Wieder was gelernt, danke! Ich hatte gedacht, wenn ich die Funktionen auf die Ausgabe von 0 und 1 reduziere, kann ich ohne eine Subshell arbeiten. Wenn ich die Funktionen so modifiziere läuft es ohne Probleme.
JTH hat geschrieben: ↑ zum Beitrag ↑
27.06.2020 11:13:16
Andererseits hat die Funktion laststate im Moment keine/eine leere Ausgabe, wenn deine Datei mit Pfad in LASTSTATE noch nicht existiert. Evtl. willst du (habe den Ablauf nicht weiter angeschaut) immer deren Inhalt ausgeben?
Ist mir auch schon aufgefallen, aber beim Debugging gibt die Funktion nur beim ersten Durchlauf einen leeren Rückgabewert zurück, sonst gab es keine Fehler. Ich habe das dann erstmal zurückgestellt.
JTH hat geschrieben: ↑ zum Beitrag ↑
27.06.2020 11:13:16
Außerdem weist du LASTSTATE nie einen Pfad zu – Absicht?
Ha, erwischt! :)
LASTSTATE hieß ursprünglich STATE und der besseren Übersichtlichkeit wegen habe ich vor dem Posten hier den Namen per Hand geändert. Bis auf eine Ausnahme habe ich alle erwischt :mrgreen:

TomL hat geschrieben: ↑ zum Beitrag ↑
27.06.2020 11:53:18
Tja... ich habe aus Spass mal dran rumgeschraubt....
Danke, das gefällt mir richtig gut!
Ich wollte es erst ähnlich wie du mit return lösen, aber dann hat mich das Handbuch der bash etwas stutzig gemacht:
man bash hat geschrieben:return [n]
Causes a function to stop executing and return the value specified by n to its caller.
Mich hat das stop executing wohl nervös gemacht, deswegen bin ich dann auf die Alternative mit zwei unterschiedlichen Funktionen gekommen. Aber wenn ich mir deinen Code ansehe ist es ja in dem Fall genau was ich will.

Vor allem gefällt mir an deiner Lösung aber, dass ich ohne externe Datei auskomme.

Das nun funktionierende Endprodukt sieht nun so aus:

Code: Alles auswählen

#! /bin/bash
#
SENSOR="/sys/bus/iio/devices/iio:device0/in_accel_y_raw"
#
LASTSTATE=-1

POINTER='FTSC1000:00 2808:50AB'
TRANSFORM='Coordinate Transformation Matrix'

#For the sake of simplicity let's assume
# 0 = Panorama mode (Desktop)
# 1 = Tablet mode (Bookreader)

function getstate() {
read -r VALUE <$SENSOR
if [ $VALUE -gt 350 ]; then
	return 0
fi
	return 1
}

while true; do
getstate
CURRSTATE=$?

if [[ $LASTSTATE -ne $CURRSTATE ]]
then

#Display wurde gedreht
			if [[ $CURRSTATE -eq 0 ]]
			then
			#Desktopmode
			xrandr --output eDP-1 --rotate inverted
			sleep 1 #xrandr needs some time to adjust
			xinput set-prop "$POINTER" "$TRANSFORM" 1 0 0 0 1 0 0 0 1
			LASTSTATE=$CURRSTATE
			else
			#Tabletmode
			xrandr --output eDP-1 --rotate right
			sleep 1 #xrandr needs some time to adjust
			xinput set-prop "$POINTER" "$TRANSFORM" 0 -1 1 1 0 0 0 0 1
			LASTSTATE=$CURRSTATE
			fi
else
#Keine Veränderung, gehe schlafen
sleep 3
fi

done

JTH
Moderator
Beiträge: 3023
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: Bash: If-Konstrukt mit veränderbaren Variablen

Beitrag von JTH » 27.06.2020 14:04:32

Wenn du noch ein bisschen mehr mit Funktionen rumspielen willst:

Der Return-/Exitcode einer Funktion ist gleich dem Ergebnis des letzten in ihr ausgeführten Kommandos, wenn kein explizites return vorkommt. So ein Konstrukt mit return 0 oder return 1 ist immer überflüssig. Folgendes macht dasselbe:

Code: Alles auswählen

getstate() {
    read -r VALUE <"$SENSOR"
    [[ $VALUE -gt 350 ]]
}

Und evtl. könnt man auch nen Einzeiler draus machen ;)

Code: Alles auswählen

getstate() {
    [[ $(<"$SENSOR") -gt 350 ]]
}
Manchmal bekannt als Just (another) Terminal Hacker.

Antworten