49

I want similar option like getche() in C. How can I read just a single character input from command line?

Using read command can we do it?

codeforester
  • 39,467
  • 16
  • 112
  • 140
footy
  • 5,803
  • 13
  • 48
  • 96

5 Answers5

72

In bash, read can do it:

read -n1 ans
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
SiegeX
  • 135,741
  • 24
  • 144
  • 154
  • 12
    read -p "Enter any char: " -n1 ans; echo -e "\nYou entered: $ans" – dimir Jan 04 '12 at 13:22
  • Not all shells implement builtins the same way. For Z-Shell `read -k1 ans"?Enter any char: " ; echo -e "\nYou entered: $ans"` should do the same as dimir's comment does in bash. – Friartek Aug 29 '23 at 15:37
38

read -n1 works for bash

The stty raw mode prevents ctrl-c from working and can get you stuck in an input loop with no way out. Also the man page says stty -raw is not guaranteed to return your terminal to the same state.

So, building on dtmilano's answer using stty -icanon -echo avoids those issues.

#/bin/ksh
## /bin/{ksh,sh,zsh,...}

# read_char var
read_char() {
  stty -icanon -echo
  eval "$1=\$(dd bs=1 count=1 2>/dev/null)"
  stty icanon echo
}

read_char char
echo "got $char"
Community
  • 1
  • 1
fess .
  • 1,489
  • 1
  • 15
  • 13
  • 2
    This also works for `ash` (tested with "BusyBox v1.10.2"), and for "#!/bin/sh" being `dash` on debian. – Sam Dec 26 '18 at 06:28
  • Hi "fess .". I think it is my duty to inform you about [this question](https://stackoverflow.com/q/53893581/10822366), and [this meta question](https://meta.stackoverflow.com/q/378229/10822366). And I fully indent to keep my word (maybe not right after i gained 75, that would drop me down to 25, and than I might loose "comment everywhere", but as soon as I reach 100, I'll use the bouny system) – Sam Dec 26 '18 at 10:14
  • 1
    @Sam I'd advice you to keep your rep for now, as you'll need the reputation for various things: https://stackoverflow.com/help/privileges. I'm offering the bounty, so don't worry. – Cœur Dec 26 '18 at 15:47
  • Why do you make the line containing `dd` a string and then `eval` it, rather than directly running it? – ban_javascript May 09 '23 at 20:03
  • So the purpose of eval is to modify the first positional arg. Great learning for me. However if you don’t need that, you can drop the eval I think. – KFL Jul 17 '23 at 03:33
16

In ksh you can basically do:

stty raw
REPLY=$(dd bs=1 count=1 2> /dev/null)
stty -raw
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
9
read -n1

reads exactly one character from input

echo "$REPLY"

prints the result on the screen

doc: https://www.computerhope.com/unix/bash/read.htm

Sergey Gurin
  • 1,537
  • 15
  • 14
1

Some people mean with "input from command line" an argument given to the command instead reading from STDIN... so please don't shoot me. But i have a (maybe not most sophisticated) solution for STDIN, too!

When using bash and having the data in a variable you can use parameter expansion

${parameter:offset:length}

and of course you can perform that on given args ($1, $2, $3, etc.)

Script

#!/usr/bin/env bash

testdata1="1234"
testdata2="abcd"

echo ${testdata1:0:1}
echo ${testdata2:0:1}
echo ${1:0:1} # argument #1 from command line

Execution

$ ./test.sh foo
1
a
f

reading from STDIN

Script

#!/usr/bin/env bash

echo please type in your message:
read message
echo 1st char: ${message:0:1}

Execution

$ ./test.sh 
please type in your message:
Foo
1st char: F
Mirko Steiner
  • 354
  • 1
  • 5
  • 1
    Reading single characters is more about conditionally reading, waiting, or doing work as characters arrive, than it is isolating a single character assuming you already read ahead successfully. This blocks indefinitely, with or without char 1, alternately consuming everything it can and waiting for more, until it consumes some delimiter. It discards the delimiter, which could be a newline, EOF, or the first character you're trying to read (in which case length is 0.) Valid in some cases, but... caveat emptor. – John P Dec 11 '20 at 08:14