Skip to content

Instantly share code, notes, and snippets.

What would you like to do?

Injecting Terminal Input

Some examples demonstrating how to inject keystrokes into a terminal's input stream.

bind '"\e[0n": "ls -l"'; printf '\e[5n'
#include <stdlib.h>
#include <argp.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
const char *argp_program_version ="inject - see";
static char doc[] = "inject - write to terminal input stream";
static struct argp_option options[] = {
{ "tty", 't', "TTY", 0, "target tty (defaults to current)"},
{ "nonl", 'n', 0, 0, "do not output the trailing newline"},
{ 0 }
struct arguments
int fd, nl, next;
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct arguments *arguments = state->input;
switch (key)
case 't': arguments->fd = open(arg, O_WRONLY|O_NONBLOCK);
if (arguments->fd > 0)
return EINVAL;
case 'n': arguments->nl = 0; break;
case ARGP_KEY_ARGS: arguments->next = state->next; return 0;
default: return ARGP_ERR_UNKNOWN;
return 0;
static struct argp argp = { options, parse_opt, 0, doc };
static struct arguments arguments;
static void inject(char c)
ioctl(arguments.fd, TIOCSTI, &c);
int main(int argc, char *argv[])
if (argp_parse (&argp, argc, argv, 0, 0, &arguments))
int a,c;
for (, c=0; a< argc; c=0 )
while (argv[a][c])
inject (argv[a][c++]);
if (++a < argc) inject(' ');
if ( inject(;
return 0;
require "sys/";
ioctl(STDIN, &TIOCSTI, $_) for split "", join " ", @ARGV
import fcntl, sys, termios
del sys.argv[0]
for c in ' '.join(sys.argv):
fcntl.ioctl(sys.stdin, termios.TIOCSTI, c)
ARGV.join(' ').split('').each { |c| $stdin.ioctl(0x5412,c) }
# Example wrapper around injected terminal input to
# prevent premature echo in front of the prompt.
# A more basic solution is to have the prompt pre-clear its line
# which can be done by prefixing it with "\r\e[M"
inject() {
if [[ $0 == "$BASH_SOURCE" ]] # executed or sourced ?
# ioctl TIOCSTI method works best (it works if sourced or executed)
echo using ioctl method
perl -e 'ioctl(STDIN, 0x5412, $_) for split "", join " ", @ARGV' "$@" ""
# escape method only works if script is sourced
echo using bind method
bind "\"\e[0n\": '$*'"; printf '\e[5n'
saved_settings=$(stty -g)
stty -echo -icanon min 1 time 0
inject echo $*
until read -t0; do
sleep 0.02
stty "$saved_settings"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.