Skip to main content

3. Reading keyboard input

  1. The script test-integer2.sh, that we have seen previously, has the value of INT hardcoded, so that we need to edit the script in order to test another value. We can make it more interactive by using the command read:

    read-integer.sh
    #!/bin/bash

    # read-integer: evaluate the value of an integer.

    echo -n "Please enter an integer -> "
    read int

    if [[ ! "$int" =~ ^-?[0-9]+$ ]]; then
    echo "Input value is not an integer." >&2
    exit 1
    fi

    if ((int == 0)); then
    echo "$int is zero."
    else
    if ((int < 0)); then
    echo "$int is negative."
    else
    echo "$int is positive."
    fi

    if (( ((int % 2)) == 0)); then
    echo "$int is even."
    else
    echo "$int is odd."
    fi
    fi
    vim read-integer.sh
    ./read-integer.sh    # enter 0
    ./read-integer.sh    # enter 7
    ./read-integer.sh    # enter 4
    ./read-integer.sh    # enter -3
    ./read-integer.sh    # enter -8

    The command read assigns the input to the variable int. If no variable name is given, then it assigns the input to the variable REPLY.

  2. The command read can also get multiple variable names, as in this example:

    read-multiple.sh
    #!/bin/bash

    # read-multiple: read multiple values from keyboard

    echo -n "Enter one or more values > "
    read var1 var2 var3 var4 var5

    echo "var1 = '$var1'"
    echo "var2 = '$var2'"
    echo "var3 = '$var3'"
    echo "var4 = '$var4'"
    echo "var5 = '$var5'"
    vim read-multiple.sh

    In this script, we assign and display up to five values.

    ./read-multiple.sh    # enter: a b c d e
    ./read-multiple.sh    # enter: a b
    ./read-multiple.sh    # enter: a b c d e f g
  3. It can also get some options:

    help read | less

    With the -p option we can provide a prompt string:

    read -p "Enter one or more values > "    # enter: a b c 
    echo "REPLY = '$REPLY'"

    The option -s can be used for a silent input, and -t to set a timeout. Let's see them in an example that tries to read a password:

    read-user-pass.sh
    #!/bin/bash

    # -e: use readline to get the input
    # -p: display a prompt
    # -i: provide a default reply
    read -e -p "What is your user name > " -i $USER username
    echo "Welcome '$username'"

    # -t: timeout (in seconds)
    # -s: silent (do not echo characters to the display as they are typed)
    # -p: display a prompt
    if read -t 10 -sp "Enter your secret passphrase > " secret_pass
    then
    echo -e "\nYour secret passphrase is '$secret_pass'"
    else
    echo -e "\nInput timed out" >&2
    exit 1
    fi
    vim read-user-pass.sh
    ./read-user-pass.sh

    If we don't type a password in 10 seconds, the read command will time out with an error exit code.

  4. The input provided to read is split by the shell. There is a shell variable named IFS (Internal Field Separator) which contains a list of separators. By default it contains a space, a tab, and a newline character. Each of them can separate items from each-other.

    If we want to modify the way that the input is separated into fields, we can change the value of IFS.

    read-ifs.sh
    #!/bin/bash
    # read-ifs: read fields from a file

    read -p "Enter a username > " user_name

    file_info="$(grep "^$user_name:" /etc/passwd)"

    if [ -z "$file_info" ]; then
    echo "No such user '$user_name'" >&2
    exit 1
    fi

    IFS=":" read user pw uid gid name home shell <<< "$file_info"

    echo "User = '$user'"
    echo "UID = '$uid'"
    echo "GID = '$gid'"
    echo "Full Name = '$name'"
    echo "Home Dir = '$home'"
    echo "Shell = '$shell'"
    vim read-ifs.sh

    Notice that we set IFS=":" before calling read. The shell allows one or more variable assignments to take place immediately before a command. These assignments alter the environment for the command that follows. The effect of the assignment is temporary changing the environment, for the duration of the command.

    It is the same as doing this, but more concise:

    OLD_IFS="$IFS"
    IFS=":"
    read user pw uid gid name home shell <<< "$file_info"
    IFS="$OLD_IFS"

    The <<< operator indicates a here string. A here string is like a here document, only shorter, consisting of a single string. We need to use it because read does not work well with a pipe (for example: echo "$file_info" | read ...)

    ./read-ifs.sh    # enter: xyz
    ./read-ifs.sh    # enter: user1
Loading asciinema cast...