Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I want to know which keys are pressed on my keyboard and print the information to stdout.

A tool that can do this is showkey. However, if I want to pass the data of showkey to read:

while read line; do
  echo "$line" | otherprog
done <`showkey -a`


showkey -a | while read line; do
  echo "$line" | otherprog

Then showkey waits until a sum of 140 characters is typed in and then sends the buffered information to read.

showkey -a prints the pressed keys line by line, without any buffering.

  1. Why does it buffer?
  2. How do I avoid this buffering, so that I can read showkey's output truly line by line?
  3. Is there an alternative to showkey?
  4. Is there a file I can read the pressed keys directly from?
  5. What is the correct way to pass data to read?


I've used lornix's solution and included it into my simple keyboard keyboard :D!

stdbuf -o0 showkey -a | while read line; do
  perl -e 'print sprintf "%030s\n",shift' "$line" | aplay &> /dev/null &

Lasership version:

MP3=(); for i in mp3/*.mp3; do MP3+=("$i"); done
stdbuf -o0 showkey -a 2>/dev/null | while read line; do
    [ -z "$line" ] || ! [[ $line =~ ^[0-9] ]] && continue
    NUM="$(echo "$line" | awk '{print $2}')"
    mplayer "${MP3[$(($NUM % $NMP3))]}" &>/dev/null &

In the same folder, download some laser mp3 files into a folder called mp3.

share|improve this question
if youve used iornix's solution, you should probably switch accepted answers. – mikeserv Jul 18 '14 at 13:15
up vote 4 down vote accepted

Try setting showkey output to non-buffering with the stdbuf command:

stdbuf -o0 showkey -a | cat -

Will show the output as keys are pressed, rather than buffering a line.

stdbuf can adjust the buffering of stdin, stdout and stderr, setting them to none, line buffered, or block buffered, with a choosable block size. Very handy.

share|improve this answer
Thank you that worked very well :). If I only had two accept answer buttons :(. – polym Jul 18 '14 at 11:51

It buffers because your terminal is set to a line-oriented line-discipline. You need stty raw. Try this:

state=$(stty -g) 
key=$( (stty raw ; dd bs=1 count=1; stty $state) </dev/tty 2>/dev/null) 

But that will only work for single-byte keypresses. It might be a good idea to doLC_ALL=C first if there's a chance that the input could contain multi-byte kepresses. A more sophisticated example might look more like this:

{   exit=$(printf '\003')
    tty_state=$(stty -g)
    stty raw istrip
    while key=$(
        dd bs=1 count=1
    ) ; do : "${key:=
}";     printf " %03o %03d %#x\n\r" \
            "'$key" "'$key" "'$key"
        [ -z "${key#"$exit"}" ] && {
            stty "$tty_state"
    done 2>/dev/null
} </dev/tty
share|improve this answer
@polym - I found this answer from a month or two ago that deals with a lot of the same stuff. There are others there, too, that are pretty good. – mikeserv Jul 14 '14 at 21:50
Unfortunately this doesn't work when pressing CTRL and similar :(. – polym Jul 18 '14 at 9:43
@polym - Hmm, I think that is strange, as it does for me. Can you try the update? Quit with CTRL+C. – mikeserv Jul 18 '14 at 11:01

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.