279

I need to read a value from the terminal in a bash script. I would like to be able to provide a default value that the user can change.

# Please enter your name: Ricardo^

In this script the prompt is "Please enter your name: " the default value is "Ricardo" and the cursor would be after the default value. Is there a way to do this in a bash script?

Ricardo Marimon
  • 10,339
  • 9
  • 52
  • 59

9 Answers9

439

You can use parameter expansion, e.g.

read -p "Enter your name [Richard]: " name
name=${name:-Richard}
echo $name

Including the default value in the prompt between brackets is a fairly common convention

What does the :-Richard part do? From the bash manual:

${parameter:-word} If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted.

Also worth noting that...

In each of the cases below, word is subject to tilde expansion, parameter expansion, command substitution, and arithmetic expansion.

So if you use webpath=${webpath:-~/httpdocs} you will get a result of /home/user/expanded/path/httpdocs not ~/httpdocs, etc.

squarecandy
  • 4,894
  • 3
  • 34
  • 45
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • 13
    I ended up doing something based on this. Reading into a temp variable `input` and then using `name=${input:-$name}`. – Ricardo Marimon Apr 15 '10 at 05:05
  • 54
    This doesn't actually answer the question. The default value should be displayed in the prompt. – SO Stinks Oct 26 '11 at 00:47
  • 4
    and what will `name=${!input:-$name}` do? – Harry Lee Mar 17 '15 at 10:27
  • 9
    @Dr.PersonPersonII - you could add the default value by doing something like this: read -p "Enter the remote host name [$remote_host_default]: " remote_host remote_host=${remote_host:-$remote_host_default} – Dobler Jun 04 '15 at 00:37
  • 7
    `$1` becomes `${1:-some_default_string}` – ThorSummoner Oct 24 '16 at 22:58
  • @HarryLee: it will always be same as `$name`. **Reason: **This syntax (`parameter expansion`) checks for parameter (in this case `input`) for `null or undefined` before substitution. However, the test will always return false because `input` is always defined (irrespective of whether it is empty or not). So, `!false === true` and we always get `name = "$name"` – Fr0zenFyr Aug 21 '18 at 13:27
  • Also, `${name:=Richard}` gives the same output as `name=${name:-Richard}` but it first assigns `Richard` to `name` and then the value is substituted. `man bash` for details – Fr0zenFyr Aug 23 '18 at 04:43
  • 1
    Link takes you to buy an Amazon book... Is that a joke? --- https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html --- Is a worthwhile link. – Jack_Hu Jan 15 '19 at 12:21
  • @Fr0zenFyr `${name:=Richard}` and `${name:-Richard}` only give the some output when *name* was unset beforehand. They give very different results when there's an existing value. – jpaugh Jan 18 '19 at 22:49
  • @Jack_Hu Good catch! The link [actually worked](https://web.archive.org/web/20100722143541/http://www.network-theory.co.uk/docs/bashref/ShellParameterExpansion.html) at the time ghostdog chose it. – jpaugh Jan 18 '19 at 22:51
  • 1
    I'd like to recommend using `read -rp` instead of `read -p`. This is because not using `-r` will mangle backslashes. See [SC2162](https://github.com/koalaman/shellcheck/wiki/SC2162). – aggregate1166877 Aug 20 '20 at 22:12
221
read -e -p "Enter Your Name:" -i "Ricardo" NAME

echo $NAME

Notes:

  • This option requires bash 4 or higher (bash --version)
  • On macos, install current bash using homebrew - brew.sh
  • -e and -i work together: -e uses readline, -i inserts text using readline
  • bash 4+ manual page for read - linuxcommand.org
Tom Harrison
  • 13,533
  • 3
  • 49
  • 77
Jadav Bheda
  • 5,031
  • 1
  • 30
  • 28
  • 1
    Great answer! I just want to mention that I was having trouble with it, because I didn't see the space between "Ricardo" and NAME, but once I figured that out.... *magic*! Thank you! – Mr Mikkél Jun 17 '12 at 00:05
  • 50
    unfortunately -i is is not a valid option for OSX 10.7 – undefined Jan 22 '13 at 17:37
  • 3
    @BrianMortenson You can upgrade bash using homebrew: http://stackoverflow.com/questions/16416195/how-do-i-upgrade-bash-in-mac-osx-mountain-lion-and-set-it-the-correct-path – antonagestam Apr 01 '14 at 22:13
  • 2
    This answer shows how to make this work on OS X (Bash 3.x): http://stackoverflow.com/questions/22634065/bash-read-command-does-not-accept-i-parameter-on-mac-any-alternatives – Christoph Petschnig Feb 11 '15 at 08:30
  • 7
    Just note that `-e` seems to be mandatory to allow `-i` to actually work – MestreLion Jun 21 '18 at 05:11
  • 1
    @MikeShiyan: `help read` in any terminal will give you a full description of all options – MestreLion Jun 21 '18 at 05:12
  • This answer could use some context: What version of bash is required? What do the parameters mean/do? – squarecandy Aug 18 '20 at 00:39
59

In Bash 4:

name="Ricardo"
read -e -i "$name" -p "Please enter your name: " input
name="${input:-$name}"

This displays the name after the prompt like this:

Please enter your name: Ricardo

with the cursor at the end of the name and allows the user to edit it. The last line is optional and forces the name to be the original default if the user erases the input or default (submitting a null).

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 1
    Can't use bash4 because it is non standard in debian distributions. I need something that will work without much hassle. – Ricardo Marimon Apr 15 '10 at 05:06
  • 1
    no need for the last line of code, just use `name` instead of `input` in the `read` command. – RNA Mar 15 '12 at 23:59
  • 2
    @RNAer: By using the extra variable, the value of `$name` is preserved if the user deletes the proposed value (and thus inputs a null string). It all depends on what your need is. I said as much in my answer. You're right, though, that I could have been more explicit and said that if the optional line were not used then the variable could have been `name`. – Dennis Williamson Mar 16 '12 at 11:43
  • 1
    @DennisWilliamson: You are right. It's a good practice if that's what one wants. – RNA Mar 16 '12 at 15:25
18

Code:

IN_PATH_DEFAULT="/tmp/input.txt"
read -p "Please enter IN_PATH [$IN_PATH_DEFAULT]: " IN_PATH
IN_PATH="${IN_PATH:-$IN_PATH_DEFAULT}"

OUT_PATH_DEFAULT="/tmp/output.txt"
read -p "Please enter OUT_PATH [$OUT_PATH_DEFAULT]: " OUT_PATH
OUT_PATH="${OUT_PATH:-$OUT_PATH_DEFAULT}"

echo "Input: $IN_PATH Output: $OUT_PATH"

Sample run:

Please enter IN_PATH [/tmp/input.txt]: 
Please enter OUT_PATH [/tmp/output.txt]: ~/out.txt
Input: /tmp/input.txt Output: ~/out.txt
Manish Singh
  • 5,848
  • 4
  • 43
  • 31
  • 1
    I like this because it follows the convention that I see in a lot of shell scripts where the default value is in square brackets before the colon. Thanks!!! – morphatic Aug 04 '17 at 18:35
18

I found this question, looking for a way to present something like:

Something interesting happened.  Proceed [Y/n/q]:

Using the above examples I deduced this:-

echo -n "Something interesting happened.  "
DEFAULT="y"
read -e -p "Proceed [Y/n/q]:" PROCEED
# adopt the default, if 'enter' given
PROCEED="${PROCEED:-${DEFAULT}}"
# change to lower case to simplify following if
PROCEED="${PROCEED,,}"
# condition for specific letter
if [ "${PROCEED}" == "q" ] ; then
  echo "Quitting"
  exit
# condition for non specific letter (ie anything other than q/y)
# if you want to have the active 'y' code in the last section
elif [ "${PROCEED}" != "y" ] ; then
  echo "Not Proceeding"
else
  echo "Proceeding"
  # do proceeding code in here
fi

Hope that helps someone to not have to think out the logic, if they encounter the same problem

sibaz
  • 1,242
  • 14
  • 26
  • Very nice. It could be even improved a little to repeat the question if someone just enters "k" or something else than the given choices. – erikbstack Apr 10 '17 at 12:03
  • 1
    I just wrote this comment following a scratched itch. I now have a shell function, which includes the above, and I tend to use that. Shell doesn't seem to loop nicely, so I try to avoid it. I might test the result from my function, and give one more try, but basically, I'm writing admin tools where things might go wrong, so I want the admin to be able to stop the script cleanly at any point. – sibaz Apr 11 '17 at 10:06
12

I've just used this pattern, which I prefer:

read name || name='(nobody)'
bukzor
  • 37,539
  • 11
  • 77
  • 111
8
name=Ricardo
echo "Please enter your name: $name \c"
read newname
[ -n "$newname" ] && name=$newname

Set the default; print it; read a new value; if there is a new value, use it in place of the default. There is (or was) some variations between shells and systems on how to suppress a newline at the end of a prompt. The '\c' notation seems to work on MacOS X 10.6.3 with a 3.x bash, and works on most variants of Unix derived from System V, using Bourne or Korn shells.

Also note that the user would probably not realize what is going on behind the scenes; their new data would be entered after the name already on the screen. It might be better to format it:

echo "Please enter your name ($name): \c"
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • `printf` is more portable than `echo` – ghostdog74 Apr 15 '10 at 03:54
  • 3
    @ghostdog74: maybe so; those of us who learned shell programming over 25 years ago have a hard time working out which of our practices are still relevant. It increasingly looks like bash has rewritten just about everything. I know printf has been around a while as a command - I very seldom use it, though (probably - never?). I get the impression I should shut up on shell until I've (re)learned bash. 'Tis funny; the software I work on has shell scripts that don't work well - but the trouble isn't bash-isms. I just get fed up with fixing '`if (test -z "$xxx"); ...`' and other C shellisms. – Jonathan Leffler Apr 15 '10 at 07:20
  • I'm amazed that bash supports `\c`, since it also supports `echo -n`! However, you do have to add `-e` to get bash's echo to interpret escapes. I guess `\c` is for things left unsaid: `echo -e "Syntax slightly off\c, but I've learned so much from what you've shared. Thanks, @JonathanLeffler!"` – jpaugh Jan 18 '19 at 23:02
  • 1
    @Jonathan Leffler Unfortunately, not really what the OP asked for, at least not on my bash version 4.x `$ bash --version` `GNU bash, version 4.3.33(0)-release` a) "I would like to be able to provide a default value that the user can change." Your `echo "Please enter your name: $name \c"` does not enable me to edit the default value. b) "and the cursor would be after the default value." Not true, either... The answer by @Paused until further notice. does fulfill both requirements. – Bernie Reiter Jul 16 '20 at 16:01
1
#Script for calculating various values in MB
echo "Please enter some input: "
read input_variable
echo $input_variable | awk '{ foo = $1 / 1024 / 1024 ; print foo "MB" }'
Sufiyan Ghori
  • 18,164
  • 14
  • 82
  • 110
kokane
  • 11
  • 2
0

The -e and -t parameter does not work together. i tried some expressions and the result was the following code snippet :

QMESSAGE="SHOULD I DO YES OR NO"
YMESSAGE="I DO"
NMESSAGE="I DO NOT"
FMESSAGE="PLEASE ENTER Y or N"
COUNTDOWN=2
DEFAULTVALUE=n
#----------------------------------------------------------------#
function REQUEST ()
{
read -n1 -t$COUNTDOWN -p "$QMESSAGE ? Y/N " INPUT
    INPUT=${INPUT:-$DEFAULTVALUE}
    if  [ "$INPUT" = "y" -o "$INPUT" = "Y" ] ;then
        echo -e "\n$YMESSAGE\n"
        #COMMANDEXECUTION
    elif    [ "$INPUT" = "n" -o "$INPUT" = "N" ] ;then
        echo -e "\n$NMESSAGE\n"
        #COMMANDEXECUTION
    else
        echo -e "\n$FMESSAGE\n"
    REQUEST
    fi
}
REQUEST
speefak
  • 11