kaukas

Print to bash input

This method of printing text to bash input is so good it is worth repeating.

Say you'd like to produce a kind of auto-completion tool to help you input complex bash commands. You could write a script to run your command. But in some cases the command to run is a complex pipeline that you start with and then continue tweaking and running. It's easier to generate the command, print it to the terminal, allow the user (yourself) to tweak it, and click Enter to run.

Take this hypothetical pipeline to get pieces of the current time:

LANG=en_US.UTF-8 date | cut -d' ' -f5

Let's assume that in your day-to-day work you need a different locale or a different piece of the time or some other kind of pre-processing. So it's easier to just start with the above, run, tweak, repeat.

How do you get this command to start with? You can pick it from shell history. But if you truly run it a lot your history will be full of similar variants. You could copy-paste it from some note, document, or file. But that is a context switch. You can use a script to print the command. It would be nice if the command got printed directly to the shell input (rather than to stdout) to avoid copy-pasting.

Here's the magic snippet:

print_to_bash_input() {
  # Sends characters directly to the terminal input.
  python -c '
import fcntl, sys, termios
for c in sys.argv[1]:
  fcntl.ioctl(0, termios.TIOCSTI, c)
  ' "$@"
}

With it you can create a script to print your command:

$ cat pick-date-cmd.sh
#!/bin/bash

print_to_bash_input() {
  # Sends characters directly to the terminal input.
  python -c '
import fcntl, sys, termios
for c in sys.argv[1]:
  fcntl.ioctl(0, termios.TIOCSTI, c)
  ' "$@"
}

print_to_bash_input "LANG=en_US.UTF-8 date | cut -d' ' -f5"

Now you're free to edit the command before running it.

$ ./pick-date-cmd.sh
LANG=en_US.UTF-8 date | cut -d' ' -f5$ LANG=en_US.UTF-8 date | cut -d' ' -f5
14:09:09

Why does the command get printed twice? The first print happens before the prompt gets processed. Then the prompt is printed. And then the shell repeats the command. Let me know if you know a way to prevent the first print.

Now, the above use case is very much contrived. Later we'll see how it can be put to better use.


This method also works for other shells (e.g. zsh, fish). Note that zsh has a native way to print the command to input: print -z echo test.