stdin: processing characters

K

Kevin Simmons

I have seen this question asked a few times but have not seen a
clear answer...

I have a python script that prompts the user for input from stdin via a
menu. I want to process that input when the user types in two characters
and not have to have the user press <CR>. As a comparison, in the bash
shell one can use (read -n 2 -p "-->" CHOICE; case $CHOICE in...). Works
great and is very
straightforward. I have searched the python library and have not found
an answer to this simply task. raw_input(n) won't do it. I have looked
at the curses stuff but that is a bit overkill for what I need. I have
tried a while statement reading in characters from sys.stdin but no luck
there either. I am looking for an efficient way to do this simple task.

Here is the bash code I want to do in python. I am only looking for help
with the read of stdin portions, not the case statement:

while [ "$1" == "" ]; do
(echo " ---------")
(echo " Functions")
(echo " ---------")
(echo " option")
(echo " ------ ---- Scanning ----------")
(echo " ss Scan the System, Run the scan tool.")
(echo " rs Result of Scan, Show the result of the last
scan.")

read -e -n 2 -p "-->" CHOICE
case "$CHOICE" in
## scanning
"ss" | "SS" )
(echo "Scanning the system may take a very long time. Do you
wish to proceed?")
read -e -n 1 -p "[y/n]-->" INPUT
if [ "$INPUT" == "y" ]; then
sh scan -s
cd - &>/dev/null
else
(echo)
(echo "Scan aborted.")
fi
;;
... rest of case statement
esac
done
 
E

Edward Elliott

Kevin said:
I have a python script that prompts the user for input from stdin via a
menu. I want to process that input when the user types in two characters
and not have to have the user press <CR>. As a comparison, in the bash
shell one can use (read -n 2 -p "-->" CHOICE; case $CHOICE in...). Works
great and is very

I did something like this a couple years ago, curses was the easiest way I
found to do it. It's pretty painless with the wrapper function, which
restores your terminal on error/exit.
 
C

Cameron Laird

I did something like this a couple years ago, curses was the easiest way I
found to do it. It's pretty painless with the wrapper function, which
restores your terminal on error/exit.

Kevin, the bad news is that curses() is as good as Python gets in
this regard. For better or worse, to the best of my knowledge,
unextended Python caNOT implement bash's read. Here's the closest
small approximation I know:

import curses
import os

msvcrt = curses.initscr()
msvcrt.addstr("-->")
first = msvcrt.getch()
second = msvcrt.getch()
os.system("stty echo -nl")
print "\nThe two characters are '%s' and '%s'." % (first, second)

I hope someone proves me wrong.
 
S

Serge Orlov

Cameron said:
Kevin, the bad news is that curses() is as good as Python gets in
this regard. For better or worse, to the best of my knowledge,
unextended Python caNOT implement bash's read. Here's the closest
small approximation I know:

import curses
import os

msvcrt = curses.initscr()
msvcrt.addstr("-->")
first = msvcrt.getch()
second = msvcrt.getch()
os.system("stty echo -nl")
print "\nThe two characters are '%s' and '%s'." % (first, second)

I hope someone proves me wrong.

I'm not sure what "unextended Python" means, but on POSIX platforms
termios module can disable echo and command line option -u can disable
buffering. I think there should be a way to disable buffering after
program started. Probably fcntl module.
 
K

Kevin Simmons

Serge said:
I'm not sure what "unextended Python" means, but on POSIX platforms
termios module can disable echo and command line option -u can disable
buffering. I think there should be a way to disable buffering after
program started. Probably fcntl module.
Thanks for your input. I found an answer that suits my needs, not curses
:), but stty settings and sys.stdin.read(n) :

import os, sys

while 1:
os.system("stty -icanon min 1 time 0")
print """
Radio computer control program.
------------------------------
Choose a function:
po) Power toggle
fq) Change frequency
cm) Change mode
vo) Change volume
re) Reset
qu) Quit
-->""",
func = sys.stdin.read(2)
if func == "po":
...
... rest of menu actions ...
elif func = "qu":
os.system("stty cooked")
sys.exit()

Thanks again,
Kevin
 
S

Serge Orlov

Kevin said:
Thanks for your input. I found an answer that suits my needs, not curses
:), but stty settings and sys.stdin.read(n) :

import os, sys

while 1:
os.system("stty -icanon min 1 time 0")
print """
Radio computer control program.
------------------------------
Choose a function:
po) Power toggle
fq) Change frequency
cm) Change mode
vo) Change volume
re) Reset
qu) Quit
-->""",
func = sys.stdin.read(2)
if func == "po":
...
... rest of menu actions ...
elif func = "qu":
os.system("stty cooked")
sys.exit()

Looks reasonable if you don't need portability. But you may want to
refactor it a little bit to make sure terminal setting are always
restored:

try:
do_all_the_work()
finally:
os.system("stty cooked")

P.S. Maybe its me, but when I see call sys.exit() I always have a gut
feeling this function never returns. But in fact my I'm wrong and
sys.exit is more reasonable: it raises exception. So you can call
sys.exit() inside do_all_the_work and you can still be sure that
os.system("stty cooked") is always executed at the end.
 
R

Russ Salsbury

Serge said:
Kevin Simmons wrote:


Looks reasonable if you don't need portability. But you may want to
refactor it a little bit to make sure terminal setting are always
restored:

try:
do_all_the_work()
finally:
os.system("stty cooked")

IIRC You need to do "stty raw" first. The last time I had to do this
was in the '80's, so take for what it's worth.

Russ
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top