per Skript readline-Kommandos an Bash absetzen

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 26.03.2025 17:20:49

Hallo,

ich suche einen Weg readline-Kommandos per Shellskript and die aktuelle Bash abzusetzen.
Im Grunde das selbe, was in Fish per commandline builtin möglich ist.

Beispielsweise kann ich in Fish per

Code: Alles auswählen

commandline -f backward-word
den Cursor um ein Wort zurück bewegen (was per

Code: Alles auswählen

Alt-b
interaktiv ausgelöst würde).

Hintergrund ist, dass iich das Tool re-search, das ich für die Search-History in Fish benutze, auch für Bash einsetzen. Das Tool stellt eine eigene Suche in der Shell-History bereit und befüllt anschließend die aktuelle Kommandozeile mit der gefundenen Befehlszeile. Im Anschluss sollen aber dann automatisiert (eben per Shell-Skript) readline-Befehle abgesetzt und die Kommandozeile ausgeführt werden.

TLDR: Gibt es ein Äquivalent zu Fishs "commandline" in Bash?

TuxPeter
Beiträge: 2030
Registriert: 19.11.2008 20:39:02
Lizenz eigener Beiträge: MIT Lizenz

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von TuxPeter » 26.03.2025 18:58:07

Meinst du so etwas?

Code: Alles auswählen

echo -n "Suchwort: "
read such
echo Suchwort:$such:

Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 26.03.2025 20:43:53

Nein.

Angenommen ich möchte die Kommandozeile

Code: Alles auswählen

ls /bin
erzeugen und den Cursor hinter den Slash setzen.

Das kann ich durch das manuelle Eintippen der folgenden Tasten(-kombinationen) erreichen:

Code: Alles auswählen

l
s
<space>
/
b
i
n
<alt-b>
Ich möchte das aber nicht eintippen, sondern aus einem Shellskript heraus auf der aktuellen Kommandozeile erzeugen.


Zum Vergleich: In fish erreiche ist das hierdurch:

Code: Alles auswählen

commandline -r "ls /bin" ; commandline -f backward-word

Benutzeravatar
Huo
Beiträge: 836
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von Huo » 27.03.2025 08:02:20

hupfdule hat geschrieben: ↑ zum Beitrag ↑
26.03.2025 20:43:53
Zum Vergleich: In fish erreiche ist das hierdurch:

Code: Alles auswählen

commandline -r "ls /bin" ; commandline -f backward-word
Ich denke nicht, dass etwas Äquivalentes mit bash builtins möglich ist. Mit dem Werkzeug Debianxdotool lässt es sich aber nachstellen:

Code: Alles auswählen

xdotool type "ls /bin"; xdotool key Alt+b

Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 27.03.2025 08:15:05

Ja, xdotool ist die einzige Möglichkeit, die ich bis jetzt gefunden habe, aber leider von X abhängig. Ohne X würde das nicht mehr klappen.

Benutzeravatar
hikaru
Moderator
Beiträge: 14040
Registriert: 09.04.2008 12:48:59

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hikaru » 27.03.2025 09:19:52

hupfdule hat geschrieben: ↑ zum Beitrag ↑
27.03.2025 08:15:05
Ja, xdotool ist die einzige Möglichkeit, die ich bis jetzt gefunden habe, aber leider von X abhängig. Ohne X würde das nicht mehr klappen.
Falls es dir hier um X vs Wayland geht, das Äquivalent zu Debianxdotool wäre Debianwlrctl.

Ich bin allerdings skeptisch ob der Ansatz zielführend ist, denn xdotool/wlrctl kann ja nicht kontextsensitiv arbeiten.
Du möchtest in deinem Beispiel zwar hinter den "/" springen, kannst dem Tool aber nur sagen: "Drücke dreimal CursorLinks!" Das funktioniert hier, weil du vorher abgezählt hast, dass "bin" drei Zeichen hat, aber wenn du stattdessen "ls /proc" eingegeben hättest, dann passt die 3 nicht mehr, und du müsstest deiner Lösung das Zählen beibringen.

Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 27.03.2025 10:06:15

hikaru hat geschrieben: ↑ zum Beitrag ↑
27.03.2025 09:19:52
hupfdule hat geschrieben: ↑ zum Beitrag ↑
27.03.2025 08:15:05
Ja, xdotool ist die einzige Möglichkeit, die ich bis jetzt gefunden habe, aber leider von X abhängig. Ohne X würde das nicht mehr klappen.
Falls es dir hier um X vs Wayland geht, das Äquivalent zu Debianxdotool wäre Debianwlrctl.
Nein, ich will gänzlich unabhängig davon sein. Das soll auch ganz ohne grafische Oberfläche funktionieren. Also bspw. eine Bash per SSH session.
Ich bin allerdings skeptisch ob der Ansatz zielführend ist, denn xdotool/wlrctl kann ja nicht kontextsensitiv arbeiten.
Ja, ich bin da auch noch skeptisch. Aber aus anderen Gründen.
Du möchtest in deinem Beispiel zwar hinter den "/" springen, kannst dem Tool aber nur sagen: "Drücke dreimal CursorLinks!" Das funktioniert hier, weil du vorher abgezählt hast, dass "bin" drei Zeichen hat, aber wenn du stattdessen "ls /proc" eingegeben hättest, dann passt die 3 nicht mehr, und du müsstest deiner Lösung das Zählen beibringen.
Das sollte nicht das Problem sein. Denn ich muss nicht dreimal CursorLinks drücken, sondern nur einmal Alt-b. Das löst ja die Readline-Funktion backward-word in Bash aus. Ist aber trotzdem keine schöne Lösung, falls diese Tastaturkürzel angepasst wurden. Dann läuft das ins Leere.

Und weiter: Was ich da momentan bei Fish tue, ist noch etwas mehr, als ich oben beschrieben habe (um das Thema nicht zu sehr zu verkomplizieren). Im Grunde könnte es so aussehen:

Code: Alles auswählen

# 1. Fülle die Kommandozeile mit dem Befehl "ls /bin /tmp"
commandline -r "ls /bin /tmp"
# 2. Setze den Cursor auf das Leerzeichen hinter dem /bin (Cursor 7 Positionen nach rechts verschieben)"
commandline -C 7
# 3. Springe ein Wort zurück (Readline Funktion "backward-word")
commandline -f "backward-word"
# 4. Füge ein "s" an der Cursorposition ein
commandline -i "s"
# 5. Führe die Kommandozeile aus
commandline -f execute
Punkt 1 ist auch mit Bash machbar, indem die Variablen READLINE_LINE und READLINE_POINT gesetzt werden.
Die anderen Punkte sind theoretisch mit xdotool lösbar, aber recht unschön. Bei Punkt 2 müsste dann tatsächlich 7 mal die Taste CursorRechts betätigt werden, bei Punkt 3 Alt-b (was nur klappt, wenn das Tastaturkürzel nicht ungepasst wurde), Punkt 4 müsste die Taste "s" gedrückt werden und bei Punkt 5 "Enter".

Schöner wäre es, wenn es ein Tool gäbe, das ähnlich Fishs commandline funktioniert. Scheint aber nicht der Fall zu sein. Die Abhängigkeit von X bei der Verwendung von xdotool ist jedoch ein No-go.

Benutzeravatar
Huo
Beiträge: 836
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von Huo » 27.03.2025 13:55:10

hupfdule hat geschrieben: ↑ zum Beitrag ↑
27.03.2025 10:06:15
Die Abhängigkeit von X bei der Verwendung von xdotool ist jedoch ein No-go.
Es gibt die Alternative Debianydotool, die völlig ohne graphische Oberfläche auskommt. Aus verschiedenen Gründen scheue ich mich allerdings das Tool zu empfehlen:
  • Es benötigt den Daemon Debianydotoold, den zumindest ich unter Debian nie korrekt zum Laufen bekam (unter Manjaro allerdings schon).
  • Die zu simulierenden Tastenkürzel müssen kryptisch kodiert an ydotool übergeben werden (56:1 bedeutet z.B. Alt-Taste drücken, 56:0 Alt-Taste loslassen).
  • Ich konnte ydotool nicht dazu bringen, das Keyboard-Layout zu erkennen, weshalb teilweise mit type falsche Zeichen ausgegeben werden.

rodney
Beiträge: 406
Registriert: 09.12.2016 04:15:59

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von rodney » 27.03.2025 18:54:42

Eine Debiantmux-Session nutzen und mittels tmux send-keys... die gewuenschten Tasten "senden" waere eine Moeglichkeit die ohne X oder Wayland funktionieren wuerde.

Benutzeravatar
heisenberg
Beiträge: 4241
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von heisenberg » 27.03.2025 19:19:35

Code: Alles auswählen

# 1. Fülle die Kommandozeile mit dem Befehl "ls /bin /tmp"
commandline -r "ls /bin /tmp"
# 2. Setze den Cursor auf das Leerzeichen hinter dem /bin (Cursor 7 Positionen nach rechts verschieben)"
commandline -C 7
Das in die .bashrc + ausführen (source) und dann wird bei [Ctrl]+[o] das Kommando in die Zeile gesetzt und der Cursor am Ende des ersten Verzeichnisses positioniert.

Ich habe die Pos jetzt in Abhängigkeit von dem ersten Verzeichnis berechnet. Man kann die Variable natürlich auch einfach fest auf 7 setzen. Ich möchte mit der aufwändigeren Berechnung eine dynamische Bearbeitung der Kommandozeile demonstrieren.

Code: Alles auswählen

enter_command_and_place_cursor() {

  CMD="ls /irgendwas /tmp"

  # regex: /-Zeichen, gefolgt von nicht-/-Zeichen, gefolgt von einem Leerzeichen, also das Muster des ersten Pfad-Elementes
  regex="/[^/]+ "
  
  # finde den regexp, lese die Position und den gefundenen Text in zwei Variablen ein
  read startpos finding < <( \
  	grep --only-matching --byte-offset --perl-regexp "$regex"  <<<"$CMD" \
        | awk -F: '{print $1,$2}')

  # berechne die Position nach dem ersten Verzeichnis
  ((pos=$startpos+${#finding}))

  # Fülle Ausgabezeile
  READLINE_LINE="$CMD"
  
  # Setze Cursor
  READLINE_POINT=$pos
}

# Tastenkombination definieren
bind -x '"\C-o":enter_command_and_place_cursor'

Das sollte als Vorlage für die restlichen Punkte helfen.

Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 01.04.2025 20:07:53

Huo hat geschrieben: ↑ zum Beitrag ↑
27.03.2025 13:55:10
hupfdule hat geschrieben: ↑ zum Beitrag ↑
27.03.2025 10:06:15
Die Abhängigkeit von X bei der Verwendung von xdotool ist jedoch ein No-go.
Es gibt die Alternative Debianydotool, die völlig ohne graphische Oberfläche auskommt. Aus verschiedenen Gründen scheue ich mich allerdings das Tool zu empfehlen:
[…]
Danke. Aus den von dir genannten Gründen scheint das auch für mich keine Alternative zu sein.

Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 01.04.2025 20:12:35

heisenberg hat geschrieben: ↑ zum Beitrag ↑
27.03.2025 19:19:35
Das in die .bashrc + ausführen (source) und dann wird bei [Ctrl]+[o] das Kommando in die Zeile gesetzt und der Cursor am Ende des ersten Verzeichnisses positioniert.

Ich habe die Pos jetzt in Abhängigkeit von dem ersten Verzeichnis berechnet. Man kann die Variable natürlich auch einfach fest auf 7 setzen. Ich möchte mit der aufwändigeren Berechnung eine dynamische Bearbeitung der Kommandozeile demonstrieren.

Code: Alles auswählen

[…]
Tatsächlich sind diese ersten beiden Punkte kein Problem bei der Benutzung von READLINE_LINE und READLINE_POINT (in obigem Post hatte ich beide Punkt 1 zugeschrieben, aber natürlich decken sie Punkt 1 und 2 ab.

Und die tatsächliche Position des Cursors wird bereits in re-search berechnet. In der Bash muss ich also dafür nichts noch mal berechnen..
Das sollte als Vorlage für die restlichen Punkte helfen.
Tut es nicht. Die restlichen Punkte sind damit nicht umsetzbar.

Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 01.04.2025 20:14:43

Ich gebe hier auf. Es scheint keine vernünftige Lösung zu geben, da Bash selbst so etwas nicht unterstützt.
Und selbst die Abhängigkeit von externen, teils obskuren, Tools ist für mich nicht akzeptabel.

Danke euch allen für die Unterstützung und Tipps!

Benutzeravatar
heisenberg
Beiträge: 4241
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von heisenberg » 02.04.2025 02:07:16

hupfdule hat geschrieben: ↑ zum Beitrag ↑
01.04.2025 20:12:35
Das sollte als Vorlage für die restlichen Punkte helfen.
Tut es nicht. Die restlichen Punkte sind damit nicht umsetzbar.
Natürlich geht das. Hier nochmal ein Beispiel für den 3. Punkt von Dir:
# 3. Springe ein Wort zurück (Readline Funktion "backward-word")
commandline -f "backward-word"
Das implementiert das gleiche Verhalten, was auch mit [Strg]-[Cursor-Links] passiert: An den Wortanfang des aktuellen/vorherigen Wortes springen.

Code: Alles auswählen

# function 
myrev() {
        for ((i=$#; i>0; i--)); do
        echo -n "${!i} "
        done
        echo
}

get_word_starts() {

        local text="$1"
        local i
        local last_char_is_word_char=0
        local word_has_begun=0
        local word_starts=""

        for ((i=0;i<=${#text};i++)) ; do

                # is current char a word-char?
                # echo "DEBUG: current char: ${text:$i:1} last_char_is_word_char: $last_char_is_word_char word_has_begun=$word_has_begun"
                if [[ ${text:$i:1} =~ [\ \t] ]]; then
                        last_char_is_word_char=0
                        word_has_begun=0
                else
                        # was last char a non-word char? => beginning pos of a word now
                        if [ $last_char_is_word_char -eq 0 ] ;then
                                word_has_begun=1
                        else
                                word_has_begun=0
                        fi
                        last_char_is_word_char=1
                fi
                if [ $word_has_begun -eq 1 ] ;then
                        word_starts="$word_starts $i"
                fi
        done
        myrev $word_starts

}

word_backward() {
        local CURRENT_LINE="$READLINE_LINE"
        local CURRENT_POINT="$READLINE_POINT"


        # echo "DEBUG: current line: $CURRENT_LINE"
        # echo "DEBUG: current pos:  $CURRENT_POINT"
        for word_start in $(get_word_starts "$CURRENT_LINE");do
                        # set cursor to nearest previous word start
                        if [ $CURRENT_POINT -gt $word_start ]; then
                                READLINE_POINT=$word_start
                                return
                        fi
        done

}

# Tastenkombination definieren
bind -x '"\C-o":word_backward'

Benutzeravatar
Huo
Beiträge: 836
Registriert: 26.11.2017 14:03:31
Wohnort: Freiburg

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von Huo » 02.04.2025 09:19:07

heisenberg hat geschrieben: ↑ zum Beitrag ↑
02.04.2025 02:07:16

Code: Alles auswählen

[...]

# Tastenkombination definieren
bind -x '"\C-o":word_backward'
Mir scheint, Du hast das Anliegen von @hupfdule nicht ganz richtig verstanden. Er möchte Readline-Funktionen ohne Tastenkombination aus einem Skript heraus ausführen. Das gibt Bash meines Erachtens schlicht nicht her.

Benutzeravatar
heisenberg
Beiträge: 4241
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von heisenberg » 02.04.2025 10:36:32

Huo hat geschrieben: ↑ zum Beitrag ↑
02.04.2025 09:19:07
heisenberg hat geschrieben: ↑ zum Beitrag ↑
02.04.2025 02:07:16

Code: Alles auswählen

[...]

# Tastenkombination definieren
bind -x '"\C-o":word_backward'
Mir scheint, Du hast das Anliegen von @hupfdule nicht ganz richtig verstanden. Er möchte Readline-Funktionen ohne Tastenkombination aus einem Skript heraus ausführen. Das gibt Bash meines Erachtens schlicht nicht her.
Das kann gut sein. Aber auch mit Deinen Worten verstehe ich das nicht.

Heisst das, er möchte eine Funktion "word-backwards" aufrufen, die dann in einer beliebigen Eingabezeile, in der das Script selbst per Read einen Wert einliest. navigieren? (Also nicht in der Bash-Eingabeaufforderung?) Wenn ja, verstehe ich nicht, warum hier überhaupt READLINE_LINE und READLINE_POINT verwendet werden sollen.

Benutzeravatar
hupfdule
Beiträge: 1875
Registriert: 09.12.2002 15:04:37
Wohnort: Berlin
Kontaktdaten:

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von hupfdule » 02.04.2025 20:47:23

heisenberg hat geschrieben: ↑ zum Beitrag ↑
02.04.2025 10:36:32
Heisst das, er möchte eine Funktion "word-backwards" aufrufen, die dann in einer beliebigen Eingabezeile, in der das Script selbst per Read einen Wert einliest. navigieren? (Also nicht in der Bash-Eingabeaufforderung?) Wenn ja, verstehe ich nicht, warum hier überhaupt READLINE_LINE und READLINE_POINT verwendet werden sollen.
Nein, es geht um die Bash-Eingabeaufforderung. Der Cursor befindet sich an einer bestimmten Stelle in dieser. Von dort will ich die readline-Funktion „backward-word“ (oder beliebige andere Readline-Funktionen) aufrufen.

Bash unterstützt das nicht. Daher ist meine Suche hier zu Ende.

Benutzeravatar
heisenberg
Beiträge: 4241
Registriert: 04.06.2015 01:17:27
Lizenz eigener Beiträge: MIT Lizenz

Re: per Skript readline-Kommandos an Bash absetzen

Beitrag von heisenberg » 02.04.2025 22:48:51

Yep. Dem stimme ich zu.

Antworten