Linux „Recall in Bash“ benutzerfreundlicher als der Windows-Clone

Ich durfte leider diese Meldung zur Recall Funktion in  Windows 11 lesen:

„Wir sind uns eines Fehlers bewusst, durch den Recall inkorrekt im Menü Windows-Features an- oder ausschalten in der Systemsteuerung gelistet wurde“, sagte Microsoft-Product-Manager Brandon Leblanc. „Das wird in einem kommenden Update behoben.“ Recall wird also nicht einfach so von einem System entfernt werden können, was wohl einige User verärgern wird. Zitatquelle: https://www.golem.de/news/desktop-screenshots-user-werden-recall-in-windows-11-nicht-deinstallieren-koennen-2409-188650.html

Den Benutzern von Linuxdesktops die „Recall in Bash“ installiert haben, können das natürlich auch direkt wieder entfernen, wenn sie möchten. Es kommt sogar noch besser, noch viel datenschutzfreundlicher kann sich jeder Benutzer sogar frei entscheiden, ob er das überhaupt jemals auf seinem PC installieren möchte 😉 und natürlich ist unser Recall-Tray auch zehnmal besser als ein Systemststeuerungselement 😀

Nur für den Fall, daß Ihr mehr wissen wollt:

https://github.com/Cyborgscode/Linux-Recall-in-bash/

Linux am Dienstag baut Recall nach

Fedora Downloadlink: http://repo.linux-am-dienstag.de/x86_64/fedora/39/recall-0-8.noarch.rpm

Linux am Dienstag baut Recall nach

Aus einer Laune heraus, haben wir heute Abend einfach mal eine RECALL Funktion für den Linux Desktop gebaut 😀

Linux am Dienstag baut Recall nach

Jemand sagte: „Das sind nur drei Zeilen Bashcode…“ und das stimmt auch, es sind sogar nur zwei, aber die alleine reichen leider nicht um eine vernünftige Recall Funktion zu bauen. Es waren dann doch 20 Zeilen Bash 😉

Wir brauchen 3 Teile, damit das funktioniert:

1. einen Systemd.Timer
2. das Recall Script selbst
3. die Recallsuche
4. einige Tools: flameshot, systemd, tesseract und zenity .

Die Tools einfach aus dem Repo installieren.

Dazu legen wir erst einmal .local/systemd/user/recall.service an:

[Unit]
DefaultDependencies=no
Description=Linux am Dienstag RECALL Experiment

[Service]
Type=oneshot
ExecStart=/home/<username>/.local/bin/recall

dann legen wir .local/systemd/user/recall.timer

[Unit]
Description=Timer für RECALL

[Timer]
OnCalendar=*-*-* *:*:0/10

[Install]
WantedBy=timers.target

Dann die beiden Scripte .local/bin/recall und .local/bin/recall-ui an:

#!/bin/bash

BILDER=$(cat $HOME/.config/recall/path)

if [ "$BILDER" == "" ]; then 
exit 1;
fi

mkdir -p $BILDER

SIZE=$(du -s $BILDER/|awk '{print $1;}')

if [ $SIZE -gt 1000000 ]; then 
DNAME=$(ls -d $BILDER/*png 2>&1 | head -n 1 )
if [[ "$DNAME" =~ ^/ ]]; then 
rm -f $DNAME
fi
fi

find $BILDER -iname "*txt" -ctime +180 -delete

WLISTE=$(wmctrl -l | grep -c -E -f $HOME/.config/recall/exclude )

if [ $WLISTE -eq 0 ]; then

FILENAME=$(flameshot full -p $BILDER/ 2>&1 | sed -e "s/flameshot: info: Capture saved as //g")
tesseract $FILENAME $FILENAME

fi

und die Such-UI:

#!/bin/bash

BILDER=$(cat $HOME/.config/recall/path)

if [ "$BILDER" == "" ]; then 
exit 1;
fi

SUCHE=$(zenity --entry --text="RECALL Suchbegriff" --entry-text="" --title="Suchbegriff")

TREFFER=$(grep -l -i "$SUCHE" $BILDER/*txt| sed -e "s/\.txt$//g")

eog -n -w -s $TREFFER

Führen „chmod 700 .local/bin/recall*“  aus und legen kurz die config an:

mkdir -p .config/recall
echo „/home/<username>/Bilder/recall“ > .config/recall/path
touch .config/recall/exclude

Jetzt starten wir den Timer:

systemctl –user enable –now recall.timer

Wenn man nicht möchte, daß bestimmt Inhalte erfasst werden, z.b. die Bankseite, dann tragt Ihr den Fenstertitle aus folgender Ausgabe:

[~]$ wmctrl -l
0x02e00007 0 linux-am-dienstag.de Desktop
0x02e0000f 0 linux-am-dienstag.de nemo-desktop
0x0460003a 0 linux-am-dienstag.de Bugzilla – Mozilla Thunderbird
0x0200002c 0 linux-am-dienstag.de Google News – Mozilla Firefox
0x0aa00004 0 linux-am-dienstag.de Easy Effects
0x020012e3 0 linux-am-dienstag.de Firefox – Share-Notice

Die beiden grün markierten Fenstertitle dienen jetzt mal als Beispiel. die tragt Ihr ohne irgendwas anderes in die Exclude-Datei ein z.b. mit echo „Easy Effects“ >> .config/recall/exclude oder einfach einem Texteditor 😉  Wichtig ist, das da nur zeilenweise ein Titel nach dem anderen drin steht.

Jetzt braucht es für due Suchmaske nur noch einen Shortcut und wir sind fertig:

Und das wars schon 😀 100% Lokal.

Jetzt noch ein Wort zum Platzverbrauch:

1. wenn das Recall Verzeichnis größer als 1 GB ist, werden die ersten Bilder gelöscht.
2. die erzeugten OCR Texte werden nach 180 Tagen automatisch gelöscht.

Beide Werte könnt Ihr in den Scripten einfach ändern, wie Ihr wollt.

Viel Spaß 😉

GitHub:

https://github.com/Cyborgscode/Linux-Recall-in-bash/

 

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.