GUI-Requester aus Bashscripten erzeugen

Schnell mal ein Bashscript zusammen gebastelt, daß einen kleinen Job erledigt, machen wohl viele von uns. Blöd ist, daß man das Script vom Desktop aus startet, ohne dem Script irgend etwas mitgeben zu können. Das wäre es doch toll, wenn man den Benutzer elegant fragen könnte, oder?

GUI-Requester aus Bashscripten erzeugen

Im konkreten Fall geht es um ein Script, daß die Tonausgabe eines Programms umschaltet. Die Grundlagen dazu findet Ihr hier:

Twinkle, Twinkle little PVA …

Damit wir den Ton eines Programms umschalten können, müssen wir erstmal wissen, welches Programm gemeint ist und da setzt unser kleines Script an. Da wir vom Desktop reden, wird das tonausgebende Programm ein Fenster offen haben, damit man es bedienen kann.

Jetzt haben wir ein Bashscript gestartet, daß irgendwie mitbekommen muß, welches Fenster denn gemeint ist. Dazu nutzen wir „xprop“ . Xprop erzeugt einen Mauszeiger mit dem man auf das Fenster klicken kann, von dem man alles wissen will, und ich meine echt alles! Das geht sogar soweit, das Defaulticon des Prozesses anzuzeigen, daß zu dem Fenster gehört 😀  Sehr selbst:

XKLAVIER_STATE(INTEGER) = 0, 1654415104
_GTK_EDGE_CONSTRAINTS(CARDINAL) = 85
_NET_WM_STATE(ATOM) = _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_FOCUSED
WM_STATE(WM_STATE):
window state: Normal
icon window: 0x0
_NET_FRAME_EXTENTS(CARDINAL) = 0, 0, 31, 0
_NET_WM_DESKTOP(CARDINAL) = 0
_NET_WM_ALLOWED_ACTIONS(ATOM) = _NET_WM_ACTION_MOVE, _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_MINIMIZE, _NET_WM_ACTION_SHADE, _NET_WM_ACTION_MAXIMIZE_HORZ, _NET_WM_ACTION_MAXIMIZE_VERT, _NET_WM_ACTION_CHANGE_DESKTOP, _NET_WM_ACTION_CLOSE, _NET_WM_ACTION_ABOVE, _NET_WM_ACTION_BELOW
WM_HINTS(WM_HINTS):
Client accepts input or input focus: True
Initial state is Normal State.
bitmap id # to use for icon: 0x420235a
bitmap id # of mask for icon: 0x4202360
window id # of group leader: 0x4200001
_GTK_THEME_VARIANT(UTF8_STRING) = „dark“
XdndAware(ATOM) = BITMAP
_NET_WM_ICON(CARDINAL) = Icon (48 x 48):

▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓░ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▒░ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▒ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓░ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▒ ░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▒░░░░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒
░ ░
░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

_GTK_WINDOW_OBJECT_PATH(UTF8_STRING) = „/org/gnome/Terminal/window/3“
_GTK_APPLICATION_OBJECT_PATH(UTF8_STRING) = „/org/gnome/Terminal“
_GTK_UNIQUE_BUS_NAME(UTF8_STRING) = „:1.148“
_GTK_APPLICATION_ID(UTF8_STRING) = „org.gnome.Terminal“
_NET_WM_OPAQUE_REGION(CARDINAL) = 0, 0, 1920, 1021
WM_WINDOW_ROLE(STRING) = „gnome-terminal-window-00a05266-8865-460d-9efd-dd4e9d0164db“
_NET_WM_WINDOW_TYPE(ATOM) = _NET_WM_WINDOW_TYPE_NORMAL
_NET_WM_SYNC_REQUEST_COUNTER(CARDINAL) = 69215064, 69215065
_NET_WM_USER_TIME_WINDOW(WINDOW): window id # 0x4202357
WM_CLIENT_LEADER(WINDOW): window id # 0x4200001
_NET_WM_PID(CARDINAL) = 7153
WM_LOCALE_NAME(STRING) = „de_DE.UTF-8“
WM_CLIENT_MACHINE(STRING) = „eve.resellerdesktop.de“
WM_NORMAL_HINTS(WM_SIZE_HINTS):
program specified minimum size: 359 by 70
program specified resize increment: 7 by 15
program specified base size: 16 by 26
window gravity: NorthWest
WM_PROTOCOLS(ATOM): protocols WM_DELETE_WINDOW, WM_TAKE_FOCUS, _NET_WM_PING, _NET_WM_SYNC_REQUEST
WM_CLASS(STRING) = „gnome-terminal-server“, „Gnome-terminal“
WM_ICON_NAME(COMPOUND_TEXT) = „marius@eve:~ — xprop“
_NET_WM_ICON_NAME(UTF8_ST_NET_WM_PID(CARDINAL) = 7153RING) = „marius@eve:~ — xprop“
WM_NAME(COMPOUND_TEXT) = „marius@eve:~ — xprop“
_NET_WM_NAME(UTF8_STRING) = „marius@eve:~ — xprop“

Was wir davon für den Job brauchen, habe ich mal blau markiert. Da steht die PID( ProzessID ) des Programms, das das Fenster aufgemacht hat. Mit folgender Anweisung kann man das im Script auslesen:

pid=$(xprop | grep _NET_WM_PID | grep -o [0-9]*)

Das erste Grep besorgt uns die Zeile, das zweite Grep lässt nur unsere gesuchten Zahlen übrig. Damit haben wir einen Teil der Aufgabe gelöst. Teil Zwei besteht darin, das neue Ausgabegerät für das Programm zu ermitteln.

Damit Ihr versteht, was da als Argumente benutzt wird, ist ein Blick in den anderen Artikel hilfreich. Kurzfassung: Ich habe zwei Ausgänge: Lautsprecher und HDMI(Kopfhörer) .

Mit Zenity können wir beliebige GUI-Requester bauen und das ausgewählte Ergebnis auslesen. Vermutlich ist das Programm bei Euch schon vorinstalliert.

out=$(zenity –list –radiolist –text „Set audio output for window:“ –column „Select“ –column „Output“ FALSE „Lautsprecher“ FALSE „HDMI“ FALSE „Kopfhörer“ )

Das sieht in Real dann so aus:

Mit „–column“ gibt man an, welche Spalten man haben will, dann kommt die Liste der Optionen. Für ein Radiobutton, also eine Liste, wo man nur ein Element ausgewählt haben kann, sind die Optionen so aufgebaut:

FALSE/TRUE Elementname
FALSE/TRUE Elementname
FALSE/TRUE Elementname
FALSE/TRUE Elementname

Wobei TRUE vorausgewählt meint, und FALSE nicht ausgewählt. Im Bashscript stehen die Optionen einfach hintereinander. Hat man etwas ausgewählt, wird es vom Prozess einfach als Text ausgegeben:

[marius@eve ~]$ zenity –list –radiolist –text „Set audio output for window:“ –column „Select“ –column „Output“ FALSE „Lautsprecher“ FALSE „HDMI“ FALSE „Kopfhörer“
Kopfhörer
[marius@eve ~]$

Ich hatte Kopfhörer ausgewählt 😉 Jetzt noch den Prozessnamen:

pname=$( cat /proc/$pid/stat | awk ‚{print $2};‘ | grep -o [a-zA-Z]* )

bauen wir es zusammen:

#!/bin/bash

pid=$(xprop | grep _NET_WM_PID | grep -o [0-9]*)
out=$(zenity –list –radiolist –text „Set audio output for window:“ –column „Select“ –column „Output“ FALSE „Lautsprecher“ FALSE „HDMI“ FALSE „Kopfhörer“ )
pname=$( cat /proc/$pid/stat | awk ‚{print $2};‘ | grep -o [a-zA-Z]* )
pulse.out $pname $out

Fertig. Aber, mit Sonderzeichen im Prozessnamen wird es wohl Probleme geben.

Damit kann man jetzt ein Script starten, das lässt einen ein Fenster auswählen, dann fragt man nach der Tonausgabe und dann wechselt der Ton. Das geht natürlich viel einfacher, wenn man einen Sprachassistenten hat 😀

command:“schalte .* auf .* um“,“EXEC:pulse.outx:x%0x:x%1″

Damit kann man jeden namentlich korrekt erkannten Prozess auf jedes konfigurierte Ausgabegerät umlenken. Hört meine Wort: Das wird Eure Zukunft.

 

 

Linux am Dienstag: Programm für den 7.6.2022

  • Es ist Freitag und wir haben schon die Vortragslots für das nächste Linux am Dienstag voll \o/ Diesmal geht es u.a. darum wie man PeerTube installiert, wie man aus Bashscripten Benutzerdialoge baut und im mythischen Ü-Ei Teil wird es wieder abgedrehtes zu bestaunen geben 😀 Der Titel verrät allen mehr, die ständig bei Linux am Dienstag dabei sind 😉 Laßt Euch überraschen was geht 😀

Linux am Dienstag: Programm für den 7.6.2022

Ab 19 Uhr geht es u.a. um folgendes:

  • Linux – Apples M1 GPU malt zum ersten mal mit Linux
  • Security – Sicherheitslücke in UNISOC’s Android-SOC
  • Vortrag – „User-UI-Dialoge in Bash“ (Marius)
  • Vortrag – „PeerTube Installation mit docker-compose“ (Mario)
  • Vortrag – „Twinkle, Twinkle little PVA“ (Marius)
  • Mozilla – Sicherheitslücken in Thunderbird & Firefox

Wie jede Woche per Videokonferenz auf https://meet.cloud-foo.de/Linux .

Hinweis: Die bisherigen Vorträge findet man unter https://linux-am-dienstag.de/archiv/ .

Zeilen mit Bash zusammenfügen

Bei Linux am Dienstag hatten wir diesmal zwar keine größeren Vorträge zu Linuxprogrammen, aber dennoch etwas neues für die meisten Shellfanatiker: paste .

Zeilen mit Bash zusammenfügen

Nehmen wir mal diese Daten einer beliebigen Datei oder Shellausgabe:

Username:		User1
	0.00 MB
Username:		User#2
	0.07 MB
Username:		User3
	4630.98 MB
Username:		User#4
	209.16 MB
Username:		User5
	583.70 MB
Username:		User#6
	896.46 MB
Username:		User7
	809.58 MB
Username:		User#8
	902.74 MB
Username:		User9
	136.84 MB
Username:		User#10
	9156.09 MB
Username:		User11
	372.55 MB
Username:		User#12
	77.75 MB

Wir haben also in den ungerade Zeilen 1,3,5… usw. den Text „Username:<TAB>UserX<LF>“ stehen und in den geraden Zeilen „<TAB>2134 MB<LF>“. Diese Zeilen sollen zusammengefügt werden, daß Username und Anzahl der MB in einer Zeile mit 2(3) Spalten steht:

Username:		User1		0.00 MB
Username:		User#2		0.07 MB
Username:		User3		4630.98 MB
Username:		User#4		209.16 MB
Username:		User5		583.70 MB
Username:		User#6		896.46 MB
Username:		User7		809.58 MB
Username:		User#8		902.74 MB
Username:		User9		136.84 MB
Username:		User#10		9156.09 MB
Username:		User11		372.55 MB
Username:		User#12		77.75 MB

Wie bekommt man das hin?

Da geb es erst einmal einen kleinen Dämpfer, als wir versuchten die <LF> mit sed zu ersetzen, was nicht funktionierte und selbst wenn es funktioniert hätte, nicht die Lösung gewesen wäre, weil dann alles in einer Zeile gestanden hätte: sed -e „s/\n//g“ können wir also vom Plan streichen 🙂

Eine kreative Lösung kam dann von Matthias:

$ cat daten.txt | xargs echo | sed -e "s/MB /MB\n/g" | sed -e "s/ /\t/g"
Username:	User1	0.00	MB
Username:	User#2	0.07	MB
Username:	User3	4630.98	MB
Username:	User#4	209.16	MB
Username:	User5	583.70	MB
Username:	User#6	896.46	MB
Username:	User7	809.58	MB
Username:	User#8	902.74	MB
Username:	User9	136.84	MB
Username:	User#10	9156.09	MB
Username:	User11	372.55	MB
Username:	User#12	77.75	MB

Weil es einfacher war damit umzugehen, standen die Ausgangsdaten in einer Textdatei „daten.txt“.

„xargs echo“  gibt jede Zeile der Datei mit echo einzeln aus. Darauf hin wird sed so ausgeführt, daß der resultierende Textblock „…MB “ zu „…MB<LF>“ wird und alle Leerzeichen zu Tabulatoren <TAB>.

Das ist schon einmal eine Möglichkeit, aber es geht einfacher:

$ cat daten.txt | paste - -
Username:		User1		0.00 MB
Username:		User#2		0.07 MB
Username:		User3		4630.98 MB
Username:		User#4		209.16 MB
Username:		User5		583.70 MB
Username:		User#6		896.46 MB
Username:		User7		809.58 MB
Username:		User#8		902.74 MB
Username:		User9		136.84 MB
Username:		User#10		9156.09 MB
Username:		User11		372.55 MB
Username:		User#12		77.75 MB

Es gab keine Vorgabe, daß alle Leerzeichen verschwinden sollten. Wäre das eine Vorgabe gewesen, hätte man am Ende noch das | sed -e „s/ /\t/g“ dranhängen müssen.

$ cat daten.txt | paste - - |  sed -e "s/ /\t/g"
Username:		User1		0.00	MB
Username:		User#2		0.07	MB
Username:		User3		4630.98	MB
Username:		User#4		209.16	MB
Username:		User5		583.70	MB
Username:		User#6		896.46	MB
Username:		User7		809.58	MB
Username:		User#8		902.74	MB
Username:		User9		136.84	MB
Username:		User#10		9156.09	MB
Username:		User11		372.55	MB
Username:		User#12		77.75	MB

oder etwas schlanker:

$ cat daten.txt | paste - - |  sed -e "s/\t\t/ /g" -e "s/ /\t/g"
Username:	User1	0.00	MB
Username:	User#2	0.07	MB
Username:	User3	4630.98	MB
Username:	User#4	209.16	MB
Username:	User5	583.70	MB
Username:	User#6	896.46	MB
Username:	User7	809.58	MB
Username:	User#8	902.74	MB
Username:	User9	136.84	MB
Username:	User#10	9156.09	MB
Username:	User11	372.55	MB
Username:	User#12	77.75	MB

Merke: paste aus den CoreUtilities kann zwei oder mehr Zeilen einer Ausgabe zusammenfügen.