Seite 1 von 1

Pure Verzweiflung zur Syntax

Verfasst: Montag 3. Januar 2022, 15:26
von BlinkyBill
Hallo Zusammen,

nachdem ich nun schon gefühlte Ewigkeiten in ein Skript investiert habe (das auch nur von einer Seite auf einem Tutorial stammt, aber meiner Meinung nach dort so nichts verloren hat), muss ich nun einen Hilferuf starten.

Das Original sieht so aus:

Code: Alles auswählen

#!/bin/sh
 
ACCESSKEY="DER-ALARM-ACCESSKEY"
API_URL="https://www.xxxxxxxx?accesskey=${ACCESSKEY}"
IS_MONITOR_ACTIVE=true
 
while true; do
    HAS_ALARM=`curl -s ${API_URL} | jq -r -j '.success'`
 
    if [ $HAS_ALARM = true ] && [ $IS_MONITOR_ACTIVE = false ]; then
        echo "Turn display on"
        xscreensaver-command -deactivate
        IS_MONITOR_ACTIVE=true
    elif [ $HAS_ALARM = false ] && [ $IS_MONITOR_ACTIVE = true ]; then
        echo "Turn display off"
        xscreensaver-command -activate
        IS_MONITOR_ACTIVE=false
    fi
 
    sleep 20
    
    done
    
Nach all meinen Versuchen, das auf einem Raspberry so zu laufen zu bekommen und nach und nach die syntaxfehler auszumerzen, bin ich nun auf folgendem Stand angekommen:

Code: Alles auswählen

#!/bin/sh
 
ACCESSKEY="DER-ALARM-ACCESSKEY"
API_URL="https://wwwxxxxxxxxxxxxxxxxx.accesskey=${ACCESSKEY}"
IS_MONITOR_ACTIVE=True
 
while True:
#    HAS_ALARM=`curl -s ${API_URL} | jq -r -j '.success'`
    HAS_ALARM='curl -s "https://xxxxxxxxxxaccesskey=yyyyyyyyyyyyyyy" | jq -r -j'.success
    if [ HAS_ALARM == True ] and [ IS_MONITOR_ACTIVE == False ]: 
#    if  [ IS_MONITOR_ACTIVE == false] :
#        echo "Turn display on"
        xscreensaver-command -deactivate
        IS_MONITOR_ACTIVE=True
    elif [ HAS_ALARM == False ] and [ IS_MONITOR_ACTIVE == True ]:
#       echo "Turn display off"
        xscreensaver-command -activate
        IS_MONITOR_ACTIVE=False
    fi
 
    time.sleep(20)
done
Hier scheitere ich nun aber an der Zeile mit dem curl endgültig.

curl -s "https://www.xxxxxxxxxxxx?accesskey=yyyyyyyyy" | jq -r -j ergibt auf der Konsole korrekt ein "success": false - funktioniert also prinzipiell.

Ich vermute, dass in dem Skript noch etliche Syntaxfehler stecken, aber ich gebe schlicht und ergreifend auf.

Ist das Ursprüngliche Programm irgendwie lauffähig und für eine andere Version ? Bin ich nur zu doof? Oder ist das einfach tatsächlich Müll ? :-(

Ich würde mich freuen, wenn da einer von Euch geübten mit ein paar Handgriffen die übelsten Fehler ausmerzen kann, oder mir den entscheidenden Tip geben. Die Doku zu curl scheint extrem Umfangreich und für Einsteiger eher ungeeignet :-)

im Voraus besten Dank

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 3. Januar 2022, 15:30
von __deets__
Das ist ein shell-Skript, und hat mit Python nix zu tun. Du hast es jetzt mit Python-Syntax "angereichert", aber das macht es immer noch nicht zu Python, und auch nicht mehr zu Shellskript. Du solltest dich also fuer eine der beiden Sprachen entscheiden, und es dann in der Implementieren. Da es schon shellscript ist, und du sonst alles von vorne schreiben muesstest (und zB sowas wie curl benutzt man in Python nicht, das macht man mit urllib oder requests), ist es wohl eher Shellscript.

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 3. Januar 2022, 15:57
von BlinkyBill
Hau mir mal bitte einen Hammer auf den Kopf...

Vielen Dank!!! Ich war von den letzten Tagen noch so drauf, dass ich es immer mit python gestartet hab.

Wie war das mit dem Wald und den Bäumen ?

Dank Dir!

Re: Pure Verzweiflung zur Syntax

Verfasst: Freitag 10. Juni 2022, 12:33
von __blackjack__
@BlinkyBill: Anmerkungen zum Shell-Skript: Die Backtick-Syntax würde ich nicht mehr verwenden. ``$(…)`` ist lesbarer, man kann es verschachteln, und die Klammern werden von den meisten Editoren auch deutlich besser unterstützt, beispielsweise durch visualles hervorheben der jeweils anderen Klammer wenn man mit dem Cursor auf einer Klammer steht, oder Short-Cuts zum Springen zur anderen Klammer oder auswählen von allem zwischen den Klammern.

Bei ``jq`` ist die ``-r``-Option redundant, denn das ist in ``-j`` schon enthalten.

In Skripten verwende ich oft die Langform von Optionen, das ist lesbarer und man kann oft leichter in der Dokumentation danach suchen.

Die betroffene Zeile sähe dann so aus:

Code: Alles auswählen

    HAS_ALARM=$(curl --silent "${API_URL}" | jq --join-output '.success')
Und nun zum On-Topic-Teil: Die Portierung nach Python. 🤓

Initiale nahezu 1:1 Übersetzung (die davon ausgeht, das JSON enthält tatsächlich einen Wahrheitswert und nicht die Zeichenketten "true" und "false"):

Code: Alles auswählen

#!/usr/bin/env python3
from subprocess import run
from time import sleep

import requests

ACCESS_KEY = "DER-ALARM-ACCESSKEY"
API_URL = "https://www.xxxxxxxx"


def main():
    is_monitor_active = True
    while True:
        has_alarm = requests.get(
            API_URL, params={"accesskey": ACCESS_KEY}
        ).json()["success"]

        if has_alarm and not is_monitor_active:
            print("Turn display on")
            run(["xscreensaver-command", "-deactivate"])
            is_monitor_active = True
        elif not has_alarm and is_monitor_active:
            print("Turn display off")
            run(["xscreensaver-command", "-activate"])
            is_monitor_active = False

        sleep(20)


if __name__ == "__main__":
    main()
Da Python hier mit echten Wahrheitswerten arbeitet und nicht mit Zeichenketten "true" und "false" — oder eben auch was anderes wenn der Webserver was anderes sendet, lässt sich das mit etwas weniger Code-Wiederholung ausdrücken:

Code: Alles auswählen

        if has_alarm and not is_monitor_active:
            print("Turn display on")
            run(["xscreensaver-command", "-deactivate"])
            is_monitor_active = True
        elif not has_alarm and is_monitor_active:
            print("Turn display off")
            run(["xscreensaver-command", "-activate"])
            is_monitor_active = False

        # =>
        
        if has_alarm != is_monitor_active:
            print("Turn display", "on" if has_alarm else "off")
            run(
                [
                    "xscreensaver-command",
                    "-deactivate" if has_alarm else "-activate",
                ]
            )
            is_monitor_active = has_alarm
Der Code wäre insgesamt etwas lesbarer wenn man die beiden Aktionen zum prüfen des Alarms und zum setzen des Bildschirmschonerzustands jeweils in eine Funktion auslagert. Bei der Gelegenheit könnte man auch gleich eine Funktion zur Abfrage des Zustands vom Bildschirmschoner hinzufügen und nicht bedingungslos mit der Annahme der Monitor sein an starten.

Code: Alles auswählen

#!/usr/bin/env python3
from subprocess import PIPE, run
from time import sleep

import requests

ACCESS_KEY = "DER-ALARM-ACCESSKEY"
API_URL = "https://www.xxxxxxxx"
XSCREENSAVER_COMMAND = "xscreensaver-command"


def check_alarm():
    response = requests.get(API_URL, params={"accesskey": ACCESS_KEY})
    response.raise_for_status()
    return response.json()["success"]


def get_display_state():
    return (
        b"non-blanked"
        in run([XSCREENSAVER_COMMAND, "-time"], stdout=PIPE, check=True).stdout
    )


def set_display_state(state):
    run(
        [XSCREENSAVER_COMMAND, "-deactivate" if state else "-activate"],
        check=True,
    )


def main():
    is_display_active = get_display_state()
    while True:
        has_alarm = check_alarm()
        if has_alarm != is_display_active:
            print("Turn display", "on" if has_alarm else "off")
            set_display_state(has_alarm)
            is_display_active = has_alarm

        sleep(20)


if __name__ == "__main__":
    main()
Jetzt haben wir noch den recht grossen Unterschied zwischen dem Shell-Skript und dem Python-Programm im Umgang mit Problemen: Das Shell-Skript macht immer einfach weiter als wäre nichts passiert. Das Python-Programm bricht mit einer Ausnahme ab. Um das möglichst leicht zu lösen, kann man `loguru` benutzen um Ausnahmen in einem ``with``-Block einfach zu protokollieren. Da kann man dann auch gleich von `print()` auf Logging umstellen.

Und um Strg+C zum Abbrechen des Programms könnte man sich noch explizit kümmern.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
from subprocess import PIPE, run
from time import sleep

import requests
from loguru import logger

ACCESS_KEY = "DER-ALARM-ACCESSKEY"
API_URL = "https://www.xxxxxxxx"
XSCREENSAVER_COMMAND = "xscreensaver-command"


def check_alarm():
    response = requests.get(API_URL, params={"accesskey": ACCESS_KEY})
    response.raise_for_status()
    result = response.json()["success"]
    if not isinstance(result, bool):
        raise ValueError(
            f"expected boolean value, got {result!r} of type {type(result)}"
        )
    return result


def get_display_state():
    return (
        b"non-blanked"
        in run([XSCREENSAVER_COMMAND, "-time"], stdout=PIPE, check=True).stdout
    )


def set_display_state(state):
    run(
        [XSCREENSAVER_COMMAND, "-deactivate" if state else "-activate"],
        check=True,
    )


def main():
    try:
        is_display_active = get_display_state()
        while True:
            with logger.catch():
                has_alarm = check_alarm()
                if has_alarm != is_display_active:
                    logger.info(
                        "Turn display {}.", "on" if has_alarm else "off"
                    )
                    set_display_state(has_alarm)
                    is_display_active = has_alarm

            sleep(20)

    except KeyboardInterrupt:
        logger.info("Strg+C / SIGINT caught.")


if __name__ == "__main__":
    main()

Re: Pure Verzweiflung zur Syntax

Verfasst: Sonntag 12. Juni 2022, 21:29
von Dennis89
Hallo,

@__blackjack__ kann es nicht sein, das ich von dir gelernt habe das man anstatt '!=' in Python lieber 'not' verwendet?


Grüße
Dennis

Re: Pure Verzweiflung zur Syntax

Verfasst: Sonntag 12. Juni 2022, 22:16
von ThomasL
Wie willst du denn das elegant mit not realisieren?

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 13. Juni 2022, 11:35
von Dennis89
Hallo,

Wäre

Code: Alles auswählen

if not has_alarm == is_display_active:
falsch? Wenn ja, was übersehe ich da?

Grüße
Dennis

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 13. Juni 2022, 12:39
von Sirius3
"falsch" nicht, nur deutlich umständlicher als `has_alarm != is_display_active`.

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 13. Juni 2022, 12:41
von __deets__
Na „falsch“ ist es nicht. Aber schlecht. Denn eine solche Negierung bedeutet immer mehr kognitive Last, wenn man einen Ausdruck verstehen will. Du verwechselst das wahrscheinlich gerade mit dem „is not“, das aber eben auch nur für den is-Operator gilt & das ja auch innen steht. Nicht davor.

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 13. Juni 2022, 14:36
von Dennis89
Okay ich verstehe was ihr meint. Danke für die Erklärung. 👍🏼

Verwechselt habe ich das nicht, aber irgendwie hatte ich mir da wohl was falsch gemerkt oder es aus dem Zusammenhang gerissen.

Grüße
Dennis

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 13. Juni 2022, 17:01
von nezzcarth
__blackjack__ hat geschrieben: Freitag 10. Juni 2022, 12:33

Code: Alles auswählen

    HAS_ALARM=$(curl --silent "${API_URL}" | jq --join-output '.success')
Nebenbemerkung: Wenn man sich nicht 150% sicher ist, dass da kein Whitespace vorkommen kann, oder das wirklich ist, was man will, würde ich auch die Substitution als Ganzes noch mal in doppelte Anführungszeichen packen. Das ist nach meinem Eindruck der Stil, der heute oft empfohlen wird.

Re: Pure Verzweiflung zur Syntax

Verfasst: Montag 13. Juni 2022, 18:30
von __blackjack__
@nezzcarth: Da wird kein „word splitting“ mit dem Ergebnis betrieben und führende/folgende Whitespaces werden auch nicht entfernt:

Code: Alles auswählen

$ A=$(echo "   a   b\n   c   d   ")
$ echo ">$A<"
>   a   b
   c   d   <
Das war die Dash; bei der Bash muss man für das \n noch ``-e`` als Option an ``echo`` übergeben.