How to key press detection on a Linux terminal, low level style in python
By default the standard input is buffered and uses canonical mode. This allows you to edit your input. When you press the enter key, the input can be read by Python.
If you want a lower level access to the input you can use tty.setraw()
on the standard input file descriptor. This allows you to read one character at a time using sys.stdin.read(1)
. Note that in this case the Python script will be responsible for handling special characters, and you will lose some of the functionality like character echoing and deleting. For more information take a look at termios(3).
You can read about escape sequences which are used for up and down keys on Wikipedia.
You should be able to replicate the standard shell behavior if you handle everything in one process.
You may also want to try using a subprocess (not referring to the module - you can use fork()
or popen()
). You would parse the unbuffered input in the main process and send it to stdin (which can be buffered) of the subprocess. You will probably need to have some inter-process communication to share history with the main process.
Here is an example of the code needed to capture the input this way. Note that it is only doing some basic processing and needs more work in order to fit your use-case.
import sys
import tty
import termios
def getchar():
fd = sys.stdin.fileno()
attr = termios.tcgetattr(fd)
try:
tty.setraw(fd)
return sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSANOW, attr)
EOT = '\x04' # CTRL+D
ESC = '\x1b'
CSI = '['
line = ''
while True:
c = getchar()
if c == EOT:
print('exit')
break
elif c == ESC:
if getchar() == CSI:
x = getchar()
if x == 'A':
print('UP')
elif x == 'B':
print('DOWN')
elif c == '\r':
print([line])
line = ''
else:
line += c
Python has a keyboard module with many features. Install it, perhaps with this command:
pip install keyboard
then use it in code like this:
import keyboard
keyboard.add_hotkey('up', lambda: keyboard.write('write command retrieved from the history of your shell here'))
keyboard.wait('esc')
or you could use the function on_press_key Using the function on_press_key:
keyboard.on_press_key("p", lambda _:print("You pressed p"))
It needs a callback function. I used _ because the keyboard function returns the keyboard event to that function.
Once executed, it will run the function when the key is pressed. You can stop all hooks by running this line:
keyboard.unhook_all()
for detailed information you can see similar post on stackoverflow, hope this helps detect key press in python?
On side Note: you mentioned fork() method above In python we can use the
subprocess builtin module here, so import subprocess and we’re good to go. The run function in particular is used here to execute commands in a subshell. For those coming from C, this saves us from going about forking and creating a child process and then waiting for the child to finish execution, let Python take care of that this one time.
sample code for execute the commands entered by the user
def execute_commands(command):
try:
subprocess.run(command.split())
except Exception:
print("psh: command not found: {}".format(command))