• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

Programm im Hintergrund ausführen

Onkel-Tom

Newbie
Hallo und guten Abend,

ich hätte ein kleines Problem mit einem Shell-Skript.
Zuerst einmal noch etwas zu mir; ich habe mal vor über 30Jahren Maschinenschlosser gelernt und bin kein Programmierer, aber stets bemüht ;-)
So nun zu meinem Problem:
Ich wollte aus einem Skript mosquitto mit & im Hintergrund starten und die Ausgabe in die Datei data.txt umleiten. Das ganze funktioniert, aber nach ein paar Sekunden schläft der Prozess wohl ein.
Zum Testen habe ich jetzt einfach ein Skript geschrieben:
Bash:
!/bin/bash
/home/tom/Temp/Test/Unterprog.sh > /home/tom/Temp/Test/data.txt &
sleep 1
while true
do
sleep 1
# Lese die erste Zeile der Datei in eine Variable
read -r erste_zeile < /home/tom/Temp/Test/data.txt
echo "$erste_zeile"
sleep 1
# Lösche die erste Zeile aus der Datei
sed -i '1d' /home/tom/Temp/Test/data.txt
done

welches dieses Skript aufruft:

Bash:
#!/bin/bash
i=1
while true
do
echo "$i" >> /home/tom/Temp/Test/data.txt
sleep 1
i=$((i+1))
done

Das Unterprogramm schreibt bis zur Nummer 4 in die Datei data.txt und hört dann auf.
Wenn ich in einer anderen Konsole die Prozesse mit ps aux anschaue läuft das Unterprogramm noch.
Das Startprogramm läuft immer weiter und zeigt dann nur noch leere Zeilen an.
Hat jemand eine Idee wo das Problem liegt und was ich tun kann damit das Unterprogramm weiter läuft?
Im Voraus schon mal danke an alle die schon mal bis hier hin gelesen haben ;-)

LG
Onkle-Tom
 

abgdf

Guru
Was möchtest Du mit dem Schreiben in die Datei überhaupt erreichen?

(Im zweiten Skript läuft "while true" endlos.)
 

josef-wien

Ultimate Guru
Sehr interessant! Bei meiner Bash (Version 5.2.21) funktionieren die beiden Skripte, in der Konsolausgabe wird der Zähler bis zum St. Nimmerleinstag erhöht. Welche Version und welche Distribution verwendest Du?

P. S. Es hat keinen Einfluß auf die Funktionalität, aber im ersten Skript fehlt am Anfang das Nummernzeichen.
 
OP
O

Onkel-Tom

Newbie
Guten Morgen und danke für die Schnellen Antworten :)

@abgdf
Das Skript ist nur zum Testen. Eigentlich soll das Unterprogramm per mosquitto Daten von einer Heizungssteuerung holen was separat funktioniert.
Danach werden im zweiten Skript aus einer Konfigurationsdatei Daten in ein Array geladen und mit den Werten von der Heizung verglichen.
Kommt nach einer in Konfigurationsdatei festgelegten Zeit ein bestimmter Wert nicht wird über ebusd der Wert aus der Heizungssteuerung abgefragt.
Es funktioniert alles bis auf den Umstand dass das Unterprogramm immer "einschläft"

@josef-wien
vielen Dank für diese Information. Bei mir läuft bash (5.1-6ubuntu1) jammy auf dem Laptop. Vielleicht hat es mit dem Powermanagment zu tun.
Da das Ganze sowieso später auf der NAS oder einem Raspi laufen soll werde ich es mal dort testen.
Auf die Idee dass es am Rechner liegt bin ich nach tagen des Testens und googelns noch nicht gekommen.
Manchmal ist die Lösung zu einfach ;-)

Vielen Dank noch mal für eure Unterstützung
LG
Onkel-Tom
 

abgdf

Guru
Das Skript ist nur zum Testen. Eigentlich soll das Unterprogramm per mosquitto Daten von einer Heizungssteuerung holen was separat funktioniert.
Danach werden im zweiten Skript aus einer Konfigurationsdatei Daten in ein Array geladen und mit den Werten von der Heizung verglichen.
Kommt nach einer in Konfigurationsdatei festgelegten Zeit ein bestimmter Wert nicht wird über ebusd der Wert aus der Heizungssteuerung abgefragt.
Ich würde ein Skript machen. Das so lange läuft, wie es nötig ist.
Es zieht Daten von "mosquitto", es zieht Daten aus der Konfigurationsdatei (oder die Daten sind gleich im Skript selbst gespeichert, wenn das möglich ist). Dann vergleicht das Skript jeweils und reagiert entsprechend.

Und ich würde es nicht in bash schreiben. Weil bash sich oft nicht so verhält, wie man es erwartet - hast Du ja gerade gemerkt. Ich würde Perl lernen und es darin schreiben. Du würdest sehen, damit ist alles viel einfacher. Und so ein Problem wie hier tritt damit gar nicht auf. (Meine Seite zu Perl wäre hier, ist allerdings auf Englisch).
 
OP
O

Onkel-Tom

Newbie
Ich werde, wenn ich herausgefunden habe warum der Prozess stoppt, wieder alles in ein Skript stecken. Das mit der Auslagerung war nur zum Testen. Die Daten welche ich einlese sollen aber in der Konfigurationsdatei bleiben, das ist für mich übersichtlicher und leichter anzupassen.
Mit Perl hatte ich mich auch schon mal beschäftigt. Für das bisschen was ich mache ist mir das dann zu stressig das auch noch zu lernen. Eigentlich komme ich mit Skripten immer da hin wo ich will ;-)
Danke noch mal für eure Unterstützung. Mit den neuen Erkenntnisseen werde ich heute mal weiter testen :)
LG
Onkel-Tom
 
Hat jemand eine Idee wo das Problem liegt und was ich tun kann damit das Unterprogramm weiter läuft?
...
P - Hauptprogramm (Parent)
C - Unterprogramm (Child)
IPC - Inter Prozess Kommunikation
FS - Filesystem

Du versuchst, eine IPC zwischen Prozess P und Prozess C mit Hilfe eines files zu realisieren.
Das funktioniert nicht!
In deinem Fall passiert folgendes:
sed in P ist ein Programm, dass auf das FS eingeht,
echo in C nimmt auf das FS keine Rücksicht. echo schreibt überall hin, egal welcher Zustand herrscht.
sed in P öffnet das file exklusiv mit einem file_lock. Damit ist der Zugriff auf dieses file für andere Prozesse gesperrt.
Ist sed fertig, gibt es das file wieder frei.
Wenn echo in C zeitgleich mit sed in P zusammenfällt, dann bleibt für den Prozess C dieses file auf immer gesperrt.
C läuft weiter, echo kann aber nicht mehr schreiben, liefert aber auch keinen Fehler zurück.
Du könntest das Problem umgehen mit z.B. flock --exclusive --wait 2 ... usw.
ABER, das Verhalten bei unterschiedlichen FS und unterschiedlicher Hardware ist unterschiedlich.
btrfs auf einer SSD kann durchaus funktionieren, ext4 auf microSD funktioniert sicher nicht mehr.
Wie auch immer, IPC über file ist keine Lösung.
In deinem Fall würde ich named pipes als IPC verwenden.
Möchtest du es professionell, dann nimmst du Pseudo TTY - ptm und pts.

Gruß
Gräfin Klara
 
Zuletzt bearbeitet:
OP
O

Onkel-Tom

Newbie
Wow, vielen vielen Dank für die ausführliche Information!!!
Das war sehr interessant und da habe ich wieder was gelernt.
Der Test mit dem Unterprogramm hat jetzt einwandfrei funktioniert.
Nach dem erfolgreichen Test habe ich im Unterprogramm wieder mosquitto gestartet und habe leider wieder den selben Effekt.
Das Programm läuft kurz an und nach wenigen Sekunden kommen keine Daten mehr :-(
Hat da noch jemand so eine super Hinweis wie der mit der named Pipe?

Gruß

Onkel-Tom

PS: der mosquitto-Befehl den ich verwende ist:
mosquitto_sub -h 192.168.1.41 -t ebusd-heat/# -v -u admin -P admin -F "%t" >> /home/tom/data.pipe
 
OP
O

Onkel-Tom

Newbie
IPC über file ist keine Lösung.
sorry, mein Fehler. Ich hatte eine named Pipe angelegt und dann hatte es funktioniert. Dann hatte ich sie gelöscht und aus Versehen wieder eine Datei mit dem Namen angelegt :-/
Ich werde morgen nach der Arbeit noch mal testen.

LG
Onkel-Tom
 
OP
O

Onkel-Tom

Newbie
Moin,

mit der named Pipe habe ich mich jetzt mal eingelesen und es ausprobiert. Das funktioniert auch nicht da beide Seiten der Pipe geöffnet sein müssen.
Wenn das Programm nach der Abfrage weiter läuft wird wohl die lesende Seite geschlossen und dann kann Mosquitto auf der anderen Seite auch nichts mehr hinein schreiben ( wenn ich das alles richtig verstanden habe ).
Ich glaube mit einem Shell-Skript komme ich da nicht weiter zumindest habe ich im Moment keine Idee wie.
Zu dem Pseudoterminal habe ich keine Anleitung auf deutsch gefunden und mein englisch ist not the yello from the egg ;-)
Trotzdem möchte ich mich für eure Unterstützung danken und ich habe wieder einiges dazu gelernt, was ja auch mit ein Sinn der Übung ist.
Vielen Dank auch noch mal @Gräfin Klara für die ausführliche Erklärung. Das war sehr interessant und lehrreich. Manchmal braucht man nur ein paar Stichworte um bei Google weiter zu kommen.
Noch einen schönen Sonntag
Thomas
 

abgdf

Guru
Man könnte sicher was zusammenschrauben, nur wird das schwierig, ohne sehen zu können, welche Daten wann von Deiner Heizungsanlage kommen.
 
.. und es ausprobiert. Das funktioniert auch nicht da beide Seiten der Pipe geöffnet sein müssen.
Das Ist falsch

... die lesende Seite geschlossen und dann kann Mosquitto auf der anderen Seite auch nichts mehr hinein schreiben

Ich denke, du unterliegst einem Denkfehler. Dein Test mit dem "Programm im Hintergrund" scheint das falsche Ziel zu verfolgen.
Mosquitto ist ein server, der mit dem mode "daemon" ausgestattet ist.
Warum willst du ihn mit & in einem script starten? Das entspricht doch nicht der Philosophie einer
Client / Server Architektur. Selbst als Test ist das nicht sinnvoll.
Was du brauchst ist ein client, der sich mit Mosquitto verbindet und Daten abholt oder sendet.
Mit einem client macht man solche Tests wie du das planst.
Oder irre ich mich?

Weißt du was du willst?
Wenn ja und der Test mit dem "Programm im Hintergrund" ist sinnvoll, dann kann ich dir mit den pipes gerne weiterhelfen.
Wenn unsicher oder gar nein, dann wäre es von Vorteil, wenn du dein geplantes Konzept hier verständlich darstellst.
Nur so kann man dein Projekt hilfreich Unterstützen.
 
Zuletzt bearbeitet:
OP
O

Onkel-Tom

Newbie
Hi,
mit Mosquitto hatte ich den client gemeint, also mosquitto_sub.
Die ganze Aktion kam dadurch, dass ich mir ein Skript gebaut hatte, welches ca.370 Werte per ebusd von der Heizung in bestimmten Zeitabständen abruft. Ein Teil der Daten wird alle 20 Sekunde andere Daten alle 100 Sekunde andere alle 1000 Sekunden und nach 10000 Sekunden werden dann alle Daten aktualisiert. Das hat so weit funktioniert aber auf dem Bus der Heizungssteuerung kommt es dann zu dem Fehler 'ERR: arbitration lost, retry'.
Meine Vermutung ist, dass der Bus schlichtweg durch die Anzahl der Abfragen überlastet ist. Da die Steuerung der Heizung von sich aus gelegentlich die Daten aktualisiert, bin ich auf die Idee gekommen nur die Daten abzufragen die nach einer gewissen Zeit nicht von selbst aktualisiert wurden.
Das Programm ebusd sendet alle Daten die von der Heizungssteuerung empfangen werden per MQTT an meinen MQTT-Server weiter.
Durch ein 'mosquitto_sub -h 192.168.1.41 -t ebusd-heat/# -v -u admin -P admin -F "%t"' rufe ich die Heizungsdaten vom MQTT-Server ab.
Jetzt habe ich aus einer Konfigurationsdatei welche in der ersten Zeile das MQTT-Topic danach den Aktualisierungsintervall und dann den ebusd-Befehl um den gewünschten Wert von der Steuerung abzufragen in ein Array eingelesen. Als letztes steht im Array die Zeit wann der Befehl zum letzten mal per mosquitto_sub empfangen wurde was durch eine Schleife immer aktuell gehalten wird. In einer weiteren Schleife sollten jetzt alle werte die nach der gewünschten Zeit nicht aktualisiert sind per ebusd an die Heizung gesendet werden.
Ich hoffe das war halbwegs verständlich.
Wenn etwas nicht funktioniert versuche ich immer die Skripte so weit zu vereinfachen um den Fehler zu finden. Da mosquitto_sub die Abarbeitung blockiert, bin ich auf die Idee gekommen das in eine Sub-shell auszulagern, aber das war ja der Anfang hier ;-)
Gruß
Thomas
 
OP
O

Onkel-Tom

Newbie
Man könnte sicher was zusammenschrauben, nur wird das schwierig, ohne sehen zu können, welche Daten wann von Deiner Heizungsanlage kommen.
Sorry abgdf, ich hatte deinen Post übersehen.

Das ist ein Ausschnitt der Daten welche in das Array geladen werden:
Abrufdaten je Zeile -> der Wert welcher aktualisiert wurde und per MQTT gelesen wurde ; Aktualisierungszeit ; ebusd-Befehl
Code:
#Abrufdaten Heizung
ebusd-heat/cc/Mode;30;ebusctl -p 8888 r -m 20 -c cc Mode;0
ebusd-heat/cc/Params;30;ebusctl -p 8888 r -m 20 -c cc Params;0
ebusd-heat/cc/Status0a;30;ebusctl -p 8888 r -m 20 -c cc Status0a;0
ebusd-heat/cc/Status16;30;ebusctl -p 8888 r -m 20 -c cc Status16;0
ebusd-heat/cc/Status;30;ebusctl -p 8888 r -21m 20 -c cc Status;0

Daten die per MQTT kommen:

Code:
pi@raspberrypi:~ $ mosquitto_sub -h 192.168.1.41 -t ebusd-heat/# -v -u admin -P admin -F "%t"
ebusd-heat/global/signal
ebusd-heat/global/version
ebusd-heat/global/running
ebusd-heat/global/updatecheck
ebusd-heat/global/scan
ebusd-heat/global/uptime

Gruß
Thomas
 
Hi,
mit Mosquitto hatte ich den client gemeint, also mosquitto_sub.
Dann ist das geklärt und dein Test verständlich

Nachfolgend die Arbeitsweise von pipes.
Es zeigt eine bidirektionale Kommunikation - sofern benötigt.

A = Applikation
D = Daemon, also das "Programm im Hintergrund"
AD = schreiben von A nach D
DA = umgekehrt

Um in beide Richtungen zu kommunizieren, braucht es 2 named pipes
Eine wird erzeugt von D, die zweite von A.
Der Erzeuger öffnet IMMER nur eine pipe für sich selbst zum Lesen, die der anderen Seite zum Schreiben dient.

Im Grunde simpel
Code:
#!/bin/bash

# code für daemon (c) Gräfin Klara
PIPE_AD="/tmp/ad_pipe"
PIPE_DA="/tmp/da_pipe"
PIPE_FD=0
MSG_RD=""
#-----------------------------------------------------------
# bei exit alles abbauen
sig_exit()
{
    [[ -p "$PIPE_AD" ]] || exit 0
    exec {PIPE_FD}<&-
    rm -f "$PIPE_AD"
    PIPE_FD=0
    printf "** pipe %s entfernt\n" "$PIPE_AD"
    exit 0
}
# erzeuge pipe zum Lesen von A
open_rd_pipe()
{
    [[ -p "$PIPE_AD" ]] && rm -f "$PIPE_AD"
    mkfifo "$PIPE_AD" || return 1
    exec {PIPE_FD}<>"$PIPE_AD"
    trap "sig_exit" EXIT
    return 0
}
# Lesen von A
# in: Lese Zeitablauf in Sekunden
# Resultat in MSG_RD
read_pipe()
{
    local tmo="$1"
    [ $PIPE_FD -eq 0 ] && return 1
# lese blockiert mit timeout
    read -t $tmo -u $PIPE_FD || return 1
    MSG_RD="$REPLY"
    return 0
}
# Schreiben nach A
# in: was geschrieben werden soll
write_pipe()
{
    local fd str="$1"
    [[ -p "$PIPE_DA" ]] || return 1
    exec {fd}<>"$PIPE_DA"
    printf "$str" >&$fd
    exec {fd}<&-
    return 0
}
#-----------------------------------------------------------
open_rd_pipe || exit 1

while true; do
    if read_pipe 2; then
        printf "** Von A: %s\n" "$MSG_RD"
    else
        printf "** Lesezeit abgelaufen\n"
    fi
    if ! write_pipe "Hallo, hier ist Daemon\n"; then
        printf "** pipe %s nicht vorhanden\n" "$PIPE_DA"
    fi
done

exit 0

Das war der code für den daemon.
Einfach starten und beobachten.

Die Applikation, also die andere Seite A, wird auf der Konsole nachgebildet:

Erzeugen der pipe A zum Lesen von D
# mkfifo "/tmp/da_pipe"
Nun kannst du von D lesen mit
# tail -f /tmp/da_pipe
Und nach D schreiben mit:
# echo "Hallo, hier ist A" > /tmp/ad_pipe
Entfernen der A pipe, D bemerkt das
# rm "/tmp/da_pipe"

Das ist es
Probieren, verstehen, anpassen und danach anwenden
Ich habe es nicht getestet, sollte aber passen.

Gruß
Gräfin Klara
 
Zuletzt bearbeitet:
Nachtrag
Erst wenn du das verstehst und die pipes zu deiner Zufriedenheit arbeiten,
erst dann beschäftigen wir uns mit dem "Programm im Hintergrund",
also wie der client gestartet werden soll, wie er im Hintergrund läuft und wie die pipes dort eingebaut werden.
Womöglich ist dieser client mosquitto_sub gar nicht notwendig und ein script als daemon genügt.
Bash kann viel mehr als die meisten glauben.
 
Zuletzt bearbeitet:
OP
O

Onkel-Tom

Newbie
Vielen Dank für deinen Code.
Da werde ich mich mal dran setzen und Testen und versuchen zu verstehen. Das wird aber sicher etwas dauern ;-)
Morgen Abend geht es weiter.
LG
Thomas
 

abgdf

Guru
Sorry abgdf, ich hatte deinen Post übersehen.

Das ist ein Ausschnitt der Daten welche in das Array geladen werden
Danke für's Posten, aber es geht ja nicht nur darum, wie die Daten aussehen, sondern auch zu welchem Zeitpunkt sie eingehen und wie die sendenden Programme sich dann verhalten. Das kann man nur entweder mit den sendenden Programme selbst testen, oder mit Skripten, die deren Verhalten simulieren (so ähnlich wie Du es im ersten Posting ohne "mosquitto_sub" versucht hast). Alles nicht so einfach.
 
OP
O

Onkel-Tom

Newbie
Bash kann viel mehr als die meisten glauben.
und vermutlich viel mehr als in meinen kleinen Kopf rein passt :ROFLMAO:
Nachdem ich jetzt ein paar Tage mit dem Code gespielt habe und fast einen Knoten im Hirn habe, weiß ich jetzt was der Code macht.
Es hat zwar etwas gedauert, aber mit Geduld und Tante Google habe ich es hin bekommen.
Ich muss sagen der Code war schon cool und es hat mir Spaß gemach die Funktionen die noch nicht kante zu erforschen.
Meine Skripte sind da doch etwas simpler gestrickt ;)
Bin mal gespannt wie es weiter geht... :)
 
Oben