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.

  • if youve used iornix's solution, you should probably switch accepted answers. – mikeserv Jul 18 '14 at 13:15

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.

  • 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
  • @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 clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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