Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A selected command from history is not executed on Ubuntu 24.04 #519

Open
filip26 opened this issue May 24, 2024 · 6 comments
Open

A selected command from history is not executed on Ubuntu 24.04 #519

filip26 opened this issue May 24, 2024 · 6 comments
Assignees

Comments

@filip26
Copy link

filip26 commented May 24, 2024

Hi,
after an upgrade on Ubuntu 24.04, hstr does not execute a selected command from history.

Steps to reproduce

  • open hstr, i.e. CTRL+R
  • select a command from history then press ENTER
  • the selected command is printed out but not executed
@RadioOperator
Copy link

same on my ubuntu.

@smahm006
Copy link

smahm006 commented May 27, 2024

Try this solution: #452 (comment)

@smahm006
Copy link

close the issue if it's been resolved btw

@filip26
Copy link
Author

filip26 commented May 30, 2024

This issue is not resolved, unless you want to force every Ubuntu user to play with sysctl.

@dvorka You are ok with this?

@martinszeltins
Copy link

martinszeltins commented Jan 16, 2025

This helped me

$ sudo sysctl -w dev.tty.legacy_tiocsti=1
$ sudo sh -c "echo 'dev.tty.legacy_tiocsti=1' > /etc/sysctl.d/9999-legacy-tiocsti.conf"

@rickardlindberg
Copy link

I repost my reply from #478 as I think it might be relevant here as well, and this issue is open.

I use my own selection program instead of hstr. I also ran in to the problem that TIOCSTI does not work on newer Linux kernel versions. After much searching and reading, I figured out how to fix it.

Even though I use my own selection program, I think I got the Bash integration from hstr initially. Here is the snipped I used in .bashrc:

if [[ $- =~ .*i.* ]]; then bind '"\C-r": "\C-a rlselect-history \C-j"'; fi

Here is how rlselect-history looked like:

set -e

result=$(tac ~/.bash_history | rlselect --tab --action -- "$@")

python - "$result" << EOF
import fcntl
import sys
import termios

action, selection = sys.argv[1].split("\n", 1)

if action != "tab":
    selection += "\n"

for ch in selection:
    fcntl.ioctl(sys.stdout.fileno(), termios.TIOCSTI, ch)
EOF

It calls rlselect, my selection program, and then has the Bash integration which I think I got the idea from from hstr. (rlselect outputs two lines. The first is the action taken within the program, and the second is the selection itself.) If enter was pressed, a newline is appended to the selection to simulate enter being pressed in the terminal which makes it execute the selection.

The new snippet that I came up with for .bashrc looks like this:

rlselect-history() {
    local action
    local selection
    {
        read action
        read selection
    } < <(tac ~/.bash_history | rlselect --tab --action -- "${READLINE_LINE}")
    if [ "$action" = "tab" ]; then
        READLINE_LINE="${selection}"
        READLINE_POINT=${#READLINE_LINE}
        bind '"\C-x2":' # Bind Ctrl+x+2 to do nothing
    elif [ "$action" = "enter" ]; then
        READLINE_LINE="${selection}"
        READLINE_POINT=${#READLINE_LINE}
        bind '"\C-x2": accept-line' # Bind Ctrl+x+2 to accept line
    else
        bind '"\C-x2":' # Bind Ctrl+x+2 to do nothing
    fi
}

if [[ $- =~ .*i.* ]]; then
    # Bind history commands to Ctrl+x+1 followed by Ctrl+x+2:
    bind '"\C-r": "\C-x1\C-x2"'
    # Bind Ctrl+x+1 to execute rlselect-history which does two things:
    # 1. Sets READLINE_*
    # 2. Binds Ctrl+x+2 to either accept line or do nothing.
    bind -x '"\C-x1": rlselect-history'
fi

The trick to finding this solution for me was understanding Bash key bindings. This answer on StackOverflow writes the following:

With bind, you can bind keys to do one of three things, but no combination of them:

  • Execute a readline command: bind '"key": command'
  • Execute a series of keystrokes: bind '"key":"keystrokes"'
  • Execute a shell command: bind -x '"key": shell-command'

The old key binding can then be explained as follows:

if [[ $- =~ .*i.* ]]; then bind '"\C-r": "\C-a rlselect-history \C-j"'; fi
  • Ctrl+R is bound to a series of keystrokes.
  • First, Ctrl+A is simulated which takes the cursor to the beginning of the line.
  • Then <space>rlselect-history<space> is typed.
  • Then Ctrl+J is simulated which means accept the current line. Or execute it. The initial space entered in the previous step ensures that the rlselect-history command does not end up in the history. The moving of the cursor to the beginning of the line ensures that anything typed at the prompt is passed as an argument to rlselect-history.

(This also makes the text rlselect-history ... appear in the terminal. The new solution makes that go away.)

Let's break down the new key bindings the same way:

if [[ $- =~ .*i.* ]]; then
    # Bind history commands to Ctrl+x+1 followed by Ctrl+x+2:
    bind '"\C-r": "\C-x1\C-x2"'
    # Bind Ctrl+x+1 to execute rlselect-history which does two things:
    # 1. Sets READLINE_*
    # 2. Binds Ctrl+x+2 to either accept line or do nothing.
    bind -x '"\C-x1": rlselect-history'
fi
  • Ctrl+R is bound to a series of keystrokes.

  • First Ctrl+X+1 is simualted.

  • Then Ctrl+X+2 is simulated.

  • Ctrl+X+1 is bound to execute the command rlselect-history. The -x to bind ensures that variables READLINE_* can be set. From man bash on set -x:

    Cause shell-command to be executed whenever keyseq is entered. When shell-command is executed, the shell sets the READLINE_LINE variable to the contents of the readline line buffer and the READLINE_POINT and READLINE_MARK variables [...] If the executed command changes the value of any of READLINE_LINE, READLINE_POINT, or READLINE_MARK, those new values will be reflected in the editing state.

  • rlselect-history is defined as a Bash function which allows it to reconfigure the key binding for Ctrl+X+2. Depending on if the current selection should be executed or not, it binds Ctrl+X+2 to either accept-line or nothing.

So the new mechanism relies on using two extra key bindings: Ctrl+X+1 and Ctrl+X+2. I chose them because I don't use them otherwise. But they can be any two key bindings.

Perhaps that makes it difficult to provide as a default config for hstr. But I thought I'd share my findings here in case it would help anyone.

I think a similar solution is presented in this thread and in other places. I didn't fully understand those examples when I first read them. I think now I do becasue of my new understanding of how Bash works. Hopefully my explanation can add value to someone.

@dvorka dvorka self-assigned this Jan 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants