Why does this always detect as true, even if the keycode is not right-arrow-key?

stty_state=`stty -g`
stty raw; stty -echo
keycode=`dd bs=1 count=1 2>/dev/null`
stty "$stty_state"  

echo $keycode

if [ "$keycode"=39 ]; then
echo "Right Arrow Key Pressed!"

1 Answer 1


You (likely) read first out of two+ bytes. $keycode in your script would be ESC when arrow key is pressed.

Arrow keys can be:

\x1b + some value

It always evaluates to true because of missing spaces in conditional expression.

Edit: an update on that statement.

Your if operates on the exit status of the [ command. The [ command is equivalent to test. The fact that it is a command is a very important fact. As a command it require spaces between arguments. The [ command is further special in that it require ] as last argument.


The command exits with the status determined by EXPRESSION. 1 or 0, true or false.

It is not an exotic way to write parenthesis. In other words it is not part of the if syntax as for example in C:

if (x == 39)


if [ "$keycode"=39 ]; then

you issue:

[ "$keycode"=39 ]

which expands to

[ \x1b=39 ]

here \x1b=39 is read as one argument. When test or [ is given one argument it exits with false only if EXPRESSION is null – which is is never going to be. Even if $keycode was empty, it would result in =39 (which is not null / empty).

Another way to look at it is that you say:

if 0 ; then # When _command_ exit with 0.
if 1 ; then # When _command_ exit with 1.

Read these questions and answers for more details – as well as discussion on [ vs [[:

In that regard you could also research back ticks `` vs $( )

Multibyte escape sequence with arrow keys:

As mentioned at the top: You (likely) read first out of two+ bytes. $keycode in your script would be ESC when arrow key is pressed.

Arrow and other special keys result in escape sequences to be sent to the system. The ESC byte signals that "here comes some bytes that should be interpreted differently". As for arrow keys that would be the ASCII [ followed by ASCII A, B, C or D.

In other words you have to parse three bytes when dealing with arrow keys.

You could try something in the direction of this to check:

{   stty_state=$(stty -g)
    stty raw isig -echo
    keycode=$(dd bs=8 conv=sync count=1)
    stty "$stty_state"
} </dev/tty 2>/dev/null
printf %s "$keycode" | xxd


HEX        ASCII
1b 5b 41   .[A # Up arrow
1b 5b 42   .[B # Down arrow
1b 5b 43   .[C # Right arrow
1b 5b 44   .[D # Left arrow
 |  |  |
 |  |  +------ ASCII A, B, C and D
 |  +--------- ASCII [
 +------------ ASCII ESC

Not sure how portable this is, but have earlier played around with code like this for catching arrow keys. Press q to quit:

while read -rsn1 ui; do
    case "$ui" in
    $'\x1b')    # Handle ESC sequence.
        # Flush read. We account for sequences for Fx keys as
        # well. 6 should suffice far more then enough.
        read -rsn1 -t 0.1 tmp
        if [[ "$tmp" == "[" ]]; then
            read -rsn1 -t 0.1 tmp
            case "$tmp" in
            "A") printf "Up\n";;
            "B") printf "Down\n";;
            "C") printf "Right\n";;
            "D") printf "Left\n";;
        # Flush "stdin" with 0.1  sec timeout.
        read -rsn5 -t 0.1
    # Other one byte (char) cases. Here only quit.
    q) break;;

(As a minor note you also (intend to) test against decimal 39 – which looks like a mixup between decimal and hexadecimal. First byte in an escape sequence is ASCII value ESC, which is decimal 27 and hexadecimal 0x1b, while decimal 39 is hexadecimal 0x27.)

  • 2
    The first problem in the question is that there are no spaces surrounding the = sign in the test, so it's parsed simply as a non-empty string and hence is true. The fact that arrow keys are multiple bytes is a separate issue.
    – wurtel
    Jan 15, 2015 at 7:42
  • 2
    Hmm, that sentence doesn't have much context as-is, I read over that as I thought that was part of the explanation of multi-byte sequences which I knew already.
    – wurtel
    Jan 15, 2015 at 8:02
  • 2
    @wurtel: Yes. To vague I guess. Find that once one explain [ is a builtin command people grasp why spaces are important much more quickly. (It is not simply a weird way of bash to use brackets instead of parenthesis.) Have to run out now. Update once back.
    – user367890
    Jan 15, 2015 at 8:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .