[gelöst] Powermanagement-Skript.sh für rofi

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
dasebastian
Beiträge: 2125
Registriert: 12.07.2020 11:21:17

[gelöst] Powermanagement-Skript.sh für rofi

Beitrag von dasebastian » 04.11.2020 15:08:26

Ich komme hier seit einiger Zeit nicht weiter. Habe hier ein Skript, dass ein paar einfache Powermanagement-Sachen via Debianrofi steuern soll.

Shutdown und Reboot funktionieren tadellos, aber Abmelden bzw. Bildschirmsperren (am liebsten via slock) funtkionieren nicht/zeitverzögert/mit plötzlichem Rootpasswortfenster (das mir dann aber keine Eingabe erlaubt), mal so mal so. Ich kann da nichts wirklich reproduzieren.

Mag vielleicht mal jemand einen Blick drüber werfen, was ich da falsch mache? Die - meiner Meinung nach - betreffenden Zeilen befinden sich eher am Anfang, ich habe sie GESPERRT KOMMENTIERT.

EDIT: Das Ganze soll aus einer Debianawesome-Session raus laufen!

Code: Alles auswählen

#!/usr/bin/env bash

#   jluttine / rofi-power-menu
#   rofi-power-menu.sh


set -e
set -u

# All supported choices
all=(ausloggen lscreen reboot shutdown)

# By default, show all (i.e., just copy the array)
show=("${all[@]}")

declare -A texts
texts[lscreen]="Bildschirm sperren"
texts[ausloggen]="Ausloggen"
texts[reboot]="Neustart"
texts[shutdown]="Runterfahren"

declare -A icons
icons[lscreen]="\u22a0"
icons[ausloggen]="\u21b6"
icons[reboot]="\u21BB"
icons[shutdown]="\u2296"
icons[cancel]="\u00d7"

declare -A actions
# actions[lscreen]="loginctl lock-session $XDG_SESSION_ID"              # ABSICHTLICH AUSKOMMENTIERT, FUNKTIONIERT NICHT
actions[lscreen]="slock"                                                # FUNKTIONIERT NICHT
actions[ausloggen]="loginctl terminate-session $XDG_SESSION_ID"         # FUNKTIONIERT NICHT
actions[reboot]="systemctl reboot"
actions[shutdown]="systemctl poweroff"

# By default, ask for confirmation for actions that are irreversible
confirmations=(ausloggen lscreen reboot shutdown)

# ------------------------------------ Bis hier wichtig? ---------------------------

# By default, no dry run
dryrun=false
showsymbols=true

function check_valid {
    option="$1"
    shift 1
    for entry in "${@}"
    do
        if [ -z "${actions[$entry]+x}" ]
        then
            echo "Invalid choice in $1: $entry" >&2
            exit 1
        fi
    done
}

# Parse command-line options
parsed=$(getopt --options=h --longoptions=help,dry-run,confirm:,choices:,choose:,symbols,no-symbols --name "$0" -- "$@")
if [ $? -ne 0 ]; then
    echo 'Terminating...' >&2
    exit 1
fi
eval set -- "$parsed"
unset parsed
while true; do
    case "$1" in
        "-h"|"--help")
            echo "rofi-power-menu - a power menu mode for Rofi"
            echo
            echo "Usage: rofi-power-menu [--choices CHOICES] [--confirm CHOICES]"
            echo "                       [--choose CHOICE] [--dry-run] [--symbols|--no-symbols]"
            echo
            echo "Use with Rofi in script mode. For instance, to ask for shutdown or reboot:"
            echo
            echo "  rofi -show menu -modi \"menu:rofi-power-menu --choices=shutdown/reboot\""
            echo
            echo "Available options:"
            echo "  --dry-run          Don't perform the selected action but print it to stderr."
            echo "  --choices CHOICES  Show only the selected choices in the given order. Use / "
            echo "                     as the separator. Available choices are lockscreen, logout,"
            echo "                     suspend, hibernate, reboot and shutdown. By default, all"
            echo "                     available choices are shown."
            echo "  --confirm CHOICES  Require confirmation for the gives choices only. Use / as"
            echo "                     the separator. Available choices are lockscreen, logout,"
            echo "                     suspend, hibernate, reboot and shutdown. By default, only"
            echo "                     irreversible actions logout, reboot and shutdown require"
            echo "                     confirmation."
            echo "  --choose CHOICE    Preselect the given choice and only ask for a confirmation"
            echo "                     (if confirmation is set to be requested). It is strongly"
            echo "                     recommended to combine this option with --confirm=CHOICE"
            echo "                     if the choice wouldn't require confirmation by default."
            echo "                     Available choices are lockscreen, logout, suspend,"
            echo "                     hibernate, reboot and shutdown."
            echo "  --[no-]symbols     Show Unicode symbols or not. Requires a font with support"
            echo "                     for the symbols. Use, for instance, fonts from the"
            echo "                     Nerdfonts collection. By default, they are shown"
            echo "  -h,--help          Show this help text."
            exit 0
            ;;
        "--dry-run")
            dryrun=true
            shift 1
            ;;
        "--confirm")
            IFS='/' read -ra confirmations <<< "$2"
            check_valid "$1" "${confirmations[@]}"
            shift 2
            ;;
        "--choices")
            IFS='/' read -ra show <<< "$2"
            check_valid "$1" "${show[@]}"
            shift 2
            ;;
        "--choose")
            # Check that the choice is valid
            check_valid "$1" "$2"
            selectionID="$2"
            shift 2
            ;;
        "--symbols")
            showsymbols=true
            shift 1
            ;;
        "--no-symbols")
            showsymbols=false
            shift 1
            ;;
        "--")
            shift
            break
            ;;
        *)
            echo "Internal error" >&2
            exit 1
            ;;
    esac
done

# Define the messages after parsing the CLI options so that it is possible to
# configure them in the future.

function write_message {
    icon="<span font_size=\"medium\">$1</span>"
    text="<span font_size=\"medium\">$2</span>"
    if [ "$showsymbols" = "true" ]
    then
        echo -n "\u200e$icon \u2068$text\u2069"
    else
        echo -n "$text"
    fi
}

function print_selection {
    echo -e "$1" | $(read -r -d '' entry; echo "echo $entry")
}

declare -A messages
declare -A confirmationMessages
for entry in "${all[@]}"
do
    messages[$entry]=$(write_message "${icons[$entry]}" "${texts[$entry]^}")
done
for entry in "${all[@]}"
do
    confirmationMessages[$entry]=$(write_message "${icons[$entry]}" "JA, ${texts[$entry]}")
done
confirmationMessages[cancel]=$(write_message "${icons[cancel]}" "Nein, abbrechen")

if [ $# -gt 0 ]
then
    # If arguments given, use those as the selection
    selection="${@}"
else
    # Otherwise, use the CLI passed choice if given
    if [ -n "${selectionID+x}" ]
    then
        selection="${messages[$selectionID]}"
    fi
fi

# Don't allow custom entries
echo -e "\0no-custom\x1ftrue"
# Use markup
echo -e "\0markup-rows\x1ftrue"

if [ -z "${selection+x}" ]
then
    echo -e "\0prompt\x1fpower"
    for entry in "${show[@]}"
    do
        echo -e "${messages[$entry]}\0icon\x1f${icons[$entry]}"
    done
else
    for entry in "${show[@]}"
    do
        if [ "$selection" = "$(print_selection "${messages[$entry]}")" ]
        then
            # Check if the selected entry is listed in confirmation requirements
            for confirmation in "${confirmations[@]}"
            do
                if [ "$entry" = "$confirmation" ]
                then
                    # Ask for confirmation
                    echo -e "\0prompt\x1fBist du sicher"
                    echo -e "${confirmationMessages[$entry]}\0icon\x1f${icons[$entry]}"
                    echo -e "${confirmationMessages[cancel]}\0icon\x1f${icons[cancel]}"
                    exit 0
                fi
            done
            # If not, then no confirmation is required, so mark confirmed
            selection=$(print_selection "${confirmationMessages[$entry]}")
        fi
        if [ "$selection" = "$(print_selection "${confirmationMessages[$entry]}")" ]
        then
            if [ $dryrun = true ]
            then
                # Tell what would have been done
                echo "Selected: $entry" >&2
            else
                # Perform the action
                ${actions[$entry]}
            fi
            exit 0
        fi
        if [ "$selection" = "$(print_selection "${confirmationMessages[cancel]}")" ]
        then
            # Do nothing
            exit 0
        fi
    done
    # The selection didn't match anything, so raise an error
    echo "Invalid selection: $selection" >&2
    exit 1
fi
Zuletzt geändert von dasebastian am 14.11.2020 14:28:12, insgesamt 1-mal geändert.

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: Powermanagement-Skript.sh für rofi

Beitrag von smutbert » 13.11.2020 13:34:23

Mit slock kenne ich mich nicht aus (das hat bei mir unter xfce nie zufriedenstellend funktioniert, aber das ist Jahre her), aber wäre für das Abmelden nicht vielleicht ein awesome-spezifischer Befehl sinnvoller, damit systemd/loginctl nicht erst schlimmstenfalls beim Beenden jeden Prozesses bis zum timeout wartet. Ich kenne awesome nicht wirklich gut, aber kurzes Googlen legt in der Shell einen Befehl wie

Code: Alles auswählen

echo 'awesome.quit()' | awesome-client
nahe.

dasebastian
Beiträge: 2125
Registriert: 12.07.2020 11:21:17

Re: Powermanagement-Skript.sh für rofi

Beitrag von dasebastian » 14.11.2020 08:36:28

smutbert hat geschrieben: ↑ zum Beitrag ↑
13.11.2020 13:34:23
Mit slock kenne ich mich nicht aus (das hat bei mir unter xfce nie zufriedenstellend funktioniert, aber das ist Jahre her), ...
Debianslock funktioniert prinzipiell tadellos hier, nur in diesem Powermenü-Skript nicht.
...wäre für das Abmelden nicht vielleicht ein awesome-spezifischer Befehl sinnvoller, damit systemd/loginctl nicht erst schlimmstenfalls beim Beenden jeden Prozesses bis zum timeout wartet.
Auf diese Idee bin ich überhaupt nicht gekommen! Wahrscheinlich ist genau das der Fall (bis timeout warten) und deshalb ist das immer unterschiedlich.

Code: Alles auswählen

echo 'awesome.quit()' | awesome-client
Hmm. Aus dem Terminal raus funktioniert das, aus dem Skript raus wieder nicht. Aber danke für die Spur, die du mir da mal gelegt hast, dem werde ich weiter nachgehen! :THX:

dasebastian
Beiträge: 2125
Registriert: 12.07.2020 11:21:17

Re: Powermanagement-Skript.sh für rofi

Beitrag von dasebastian » 14.11.2020 12:57:01

OK, Update, die Bildschirmsperre mittels slock funktioniert jetzt durch folgendes Kommando in der entsprechenden Zeile:

Code: Alles auswählen

actions[lscreen]="xautolock -locknow -locker slock"
Immerhin.

NACHTRAG 2: das Ausloggen funktioniert jetzt auch, dank deiner Hilfe. Ich habe die Befehlszeile, die du gefunden hast, in ein Shellskript gepackt und auf dieses dann in der entsprechenden Zeile verwiesen.

Code: Alles auswählen

actions[ausloggen]="bash /home/sebastian/irgend/wo/skript.sh"
Das ist jetzt nicht wahnsinnig elegant, aber funktioniert. Danke dir! :THX:

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von smutbert » 14.11.2020 15:15:16

Wenn es darum geht, dass es nur funktioniert, wenn es in einer Shell aufgerufen wird (das ist mir bei so etwas auch schon, ich glaube mit enlightenment/Debiane17 untergekommen), dann kommst du auch ohne Skript aus, wenn du die Shell im Befehl startest, also etwa

Code: Alles auswählen

/bin/sh -c "/bin/echo 'awesome.quit()' | /usr/bin/awesome-client"
(sicherheitshalber mit kompletten Pfaden, obwohl PATH ja auf jeden Fall gesetzt sein sollte)

dasebastian
Beiträge: 2125
Registriert: 12.07.2020 11:21:17

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von dasebastian » 14.11.2020 16:48:01

Okay, ich habe das nochmal probiert:

Code: Alles auswählen

actions[ausloggen]="/bin/sh -c "/bin/echo 'awesome.quit()' | /usr/bin/awesome-client""
Da tut sich dann wieder nichts. Mir macht das jetzt kein großes Kopfzerbrechen, aber was mache ich da jetzt falsch?

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von smutbert » 14.11.2020 17:12:50

Ah, so da musst dir was wegen den Anführungsstrichen überlegen, zB

Code: Alles auswählen

actions[ausloggen]="/bin/sh -c '/bin/echo awesome.quit\(\) | /usr/bin/awesome-client'"

dasebastian
Beiträge: 2125
Registriert: 12.07.2020 11:21:17

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von dasebastian » 14.11.2020 17:30:36

Hm, nein, das funktioniert leider auch nicht, keine Reaktion.

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von smutbert » 14.11.2020 21:32:01

im Terminal funktioniert der letzte Befehl aber schon (ich meine alles richtig gemacht zu haben, kann es aber mangels awesome nicht testen)?

Code: Alles auswählen

/bin/sh -c '/bin/echo awesome.quit\(\) | /usr/bin/awesome-client'

dasebastian
Beiträge: 2125
Registriert: 12.07.2020 11:21:17

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von dasebastian » 15.11.2020 09:21:09

smutbert hat geschrieben: ↑ zum Beitrag ↑
14.11.2020 21:32:01
im Terminal funktioniert der letzte Befehl aber schon (ich meine alles richtig gemacht zu haben, kann es aber mangels awesome nicht testen)?

Code: Alles auswählen

/bin/sh -c '/bin/echo awesome.quit\(\) | /usr/bin/awesome-client'
Ja, funktioniert tadellos, ist seltsam.

Wenn ich es richtig verstehe, besagt der Befehl im Grunde: öffne eine shell und schreibe (echo) awesome.quit in den awesome.client (pipe)?

Kurze Nachfrage für mein Verständnis: warum bei obigem Befehl jetzt 2 Backslashs und vorher nicht? (Es funktioniert auch ohne die 2 Backslashs nicht, ich hab das einfach mal ausprobiert ;) )

Benutzeravatar
smutbert
Beiträge: 8342
Registriert: 24.07.2011 13:27:39
Wohnort: Graz

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von smutbert » 15.11.2020 10:50:50

Das ist schnell erklärt. Die Klammern haben in der Shell eine besondere Bedeutung, weshalb, wie du leicht probieren kannst,

Code: Alles auswählen

echo awesome.quit()
nicht funktioniert. In der bash und dash (=sh normalerweise in Debian) gibt es einen Syntaxfehler und für die zsh beginnt damit die Definition einer Funktion.
Deshalb haben wir

Code: Alles auswählen

echo 'awesome.quit()' | awesome-client
daraus gemacht.

Code: Alles auswählen

echo "awesome.quit()" | awesome-client
hätte genauso funktioniert. Um es gleich in einer Shell aufzurufen ist darauf

Code: Alles auswählen

/bin/sh -c "/bin/echo 'awesome.quit()' | /usr/bin/awesome-client"
geworden.

Bei deinem Actions-Eintrag werden die doppelten Anführungsstriche aber bereits verwendet um den Befehl festzulegen (actions[xy]="..."), das heißt die doppelten Anführungstriche können wir so einfach nicht mehr verwenden und die einfachen brauchen wir für den von der Shell auszuführenden Befehl.
Damit die Shell die Klammern trotz fehlender Anführungsstriche nicht als besonderes Zeichen interpretiert, haben wir den verkehrten Schrägstrich vorangestellt, sie also „escaped“. Ich bin kein Shellprofi, aber das hat in etwa dieselbe Wirkung wie die Anführungszeichen, nur eben nur für das darauffolgendes Zeichen.
Es gibt bestimmt auch noch zahllose andere Varianten dasselbe zu erreichen, aber ich fürchte daran scheitert es gar nicht sondern an irgendetwas anderem was ich nicht auf dem Schirm habe.

dasebastian
Beiträge: 2125
Registriert: 12.07.2020 11:21:17

Re: [gelöst] Powermanagement-Skript.sh für rofi

Beitrag von dasebastian » 15.11.2020 10:59:30

Verstehe, danke dir für die ausführliche Erklärung! Wieder was gelernt (überhaupt hier in diesem Thread) :THX:

Ansonsten haben wir ja erreicht, was ich wollte! Mich macht dieses kleine Extraskript (148bytes) jetzt nicht fertig. Ich habe da auch keine Idee, woran es beim direkten Weg hapert, vielleicht doch irgendein (TM) Konstruktionsfehler im ursprünglichen Powermanagement-Skript. Das Debian selber hier sollte eigentlich (TM) nicht verbastelt sein...

Antworten