277

I have a Bash shell script in which I would like to pause execution until the user presses a key. In DOS, this is easily accomplished with the pause command. Is there a Linux equivalent I can use in my script?

Neuron
  • 5,141
  • 5
  • 38
  • 59
jt.
  • 7,625
  • 4
  • 27
  • 24

10 Answers10

371

read does this:

user@host:~$ read -n1 -r -p "Press any key to continue..." key
[...]
user@host:~$ 

The -n1 specifies that it only waits for a single character. The -r puts it into raw mode, which is necessary because otherwise, if you press something like backslash, it doesn't register until you hit the next key. The -p specifies the prompt, which must be quoted if it contains spaces. The key argument is only necessary if you want to know which key they pressed, in which case you can access it through $key.

If you are using Bash, you can also specify a timeout with -t, which causes read to return a failure when a key isn't pressed. So for example:

read -t5 -n1 -r -p 'Press any key in the next five seconds...' key
if [ "$?" -eq "0" ]; then
    echo 'A key was pressed.'
else
    echo 'No key was pressed.'
fi
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jim
  • 72,985
  • 14
  • 101
  • 108
  • 11
    Strictly speaking, that would be `"Enter any non-NUL character to continue"`. Some keys don't send any character (like `Ctrl`...) and some send more than one (like `F1`, `Home`...). `bash` ignores NUL characters. – Stephane Chazelas Jun 04 '14 at 20:33
  • 3
    Usually it's a better idea to ask for a specific key like enter, space or Y. "ANY" can be confusing to some users, there is a TAB-key so why no ANY-key and for sure there are keys that are potentially dangerous like ESCAPE, CTRL, CMD, the power button, etc. This isn't so relevant anymore today, because nowadays the console is usually only used by advanced computer users that will interpret "ANY key" correctly. The Apple 2 Design Manual, tough quite old, has an interesting section devoted to this subject (https://www.apple2scans.net/files/1982-A2F2116-m-a2e-aiiedg.pdf). – Gellweiler Apr 06 '18 at 17:26
  • 7
    If you instead use the message `Press a key to continue...` then even novice users will be able to find the `a` key and press it ;o) – DJDaveMark May 11 '18 at 09:13
  • 1
    This will have issues with `command | myscript.sh` or `myscript.sh | command`. See [this answer](https://stackoverflow.com/a/51075278/5353461) for a solution. – Tom Hale Jun 28 '18 at 05:29
  • 1
    If anyone gets `read: 1: read: Illegal option -n` make sure to wrap your command in `bash -c 'command && command'` etc. as that error is likely from `sh`. I am doing this in a Lando wrapper command. – Elijah Lynn Apr 25 '19 at 17:03
  • Is there a way to do this only when script was run by double-clicking it in the GUI? – typo Oct 13 '20 at 07:35
  • @typo You should open your own question and include more details, such as which GUI you are using. – Jim Oct 13 '20 at 07:46
183

I use these ways a lot that are very short, and they are like @theunamedguy and @Jim solutions, but with timeout and silent mode in addition.

I especially love the last case and use it in a lot of scripts that run in a loop until the user presses Enter.

Commands

  • Enter solution

    read -rsp $'Press enter to continue...\n'
    
  • Escape solution (with -d $'\e')

    read -rsp $'Press escape to continue...\n' -d $'\e'
    
  • Any key solution (with -n 1)

    read -rsp $'Press any key to continue...\n' -n 1 key
    # echo $key
    
  • Question with preselected choice (with -ei $'Y')

    read -rp $'Are you sure (Y/n) : ' -ei $'Y' key;
    # echo $key
    
  • Timeout solution (with -t 5)

    read -rsp $'Press any key or wait 5 seconds to continue...\n' -n 1 -t 5;
    
  • Sleep enhanced alias

    read -rst 0.5; timeout=$?
    # echo $timeout
    

Explanation

-r specifies raw mode, which don't allow combined characters like "\" or "^".

-s specifies silent mode, and because we don't need keyboard output.

-p $'prompt' specifies the prompt, which need to be between $' and ' to let spaces and escaped characters. Be careful, you must put between single quotes with dollars symbol to benefit escaped characters, otherwise you can use simple quotes.

-d $'\e' specifies escappe as delimiter charater, so as a final character for current entry, this is possible to put any character but be careful to put a character that the user can type.

-n 1 specifies that it only needs a single character.

-e specifies readline mode.

-i $'Y' specifies Y as initial text in readline mode.

-t 5 specifies a timeout of 5 seconds

key serve in case you need to know the input, in -n1 case, the key that has been pressed.

$? serve to know the exit code of the last program, for read, 142 in case of timeout, 0 correct input. Put $? in a variable as soon as possible if you need to test it after somes commands, because all commands would rewrite $?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
y.petremann
  • 2,702
  • 1
  • 15
  • 19
  • 2
    +1 for explaining `-s`; `man read` and `read --help` help didn't help on Ubuntu 10.04.1 LTS. Edit: `help read` did; is the rest deprecated? – Cees Timmerman Oct 22 '14 at 08:35
  • 2
    1+ for the great explanation: but i got `read: -i: invalid option` for the ex. `read -rp $'Are you sure (Y/n) : ' -ei $'Y' key;`on #osx `read -rp $'kill-server: Are you sure (Y/n) : ' -d $'Y' key;` works for me instead. ` – Tino Rüb Mar 21 '16 at 18:26
  • I don't know how it works on OSX but I've made some test and `-i` works perfectly on Ubuntu, also I don't know how if `-d` works the same way on OSX. – y.petremann Mar 21 '16 at 22:40
  • one can work around prompt really quick and nice by using an echo before read, eventually echo -n – THESorcerer Mar 13 '18 at 09:07
  • I especially like the timeout solution. – TheGeeko61 Aug 29 '23 at 22:15
18

This worked for me on multiple flavors of Linux, where some of these other solutions did not (including the most popular ones here). I think it's more readable too...

echo Press enter to continue; read dummy;

Note that a variable needs to be supplied as an argument to read.

BuvinJ
  • 10,221
  • 5
  • 83
  • 96
  • this answer solve my problem, if I copy and paste this line and for some reason I got more lines appended, the appended lines are not executed , like was with DOS pause – Sérgio Oct 28 '17 at 00:38
17

read without any parameters will only continue if you press enter. The DOS pause command will continue if you press any key. Use read –n1 if you want this behaviour.

xsl
  • 17,116
  • 18
  • 71
  • 112
15

read -n1 is not portable. A portable way to do the same might be:

(   trap "stty $(stty -g;stty -icanon)" EXIT
    LC_ALL=C dd bs=1 count=1 >/dev/null 2>&1
)   </dev/tty

Besides using read, for just a press ENTER to continue prompt you could do:

sed -n q </dev/tty
mikeserv
  • 694
  • 7
  • 9
  • 3
    `status=none` is not portable either. Redirect stdout and stderr to /dev/null instead. `read -r line < /dev/tty` would be enought for _press ENTER..._. – Stephane Chazelas Jun 04 '14 at 20:28
  • @StephaneChezales thanks - i didnt know that. ill fix it now. Thanks again - fixed. Youre a bottomless well of worthwhile information, by the way. – mikeserv Jun 04 '14 at 20:31
  • 1
    Also note the `settings=$(stty -g); stty raw; dd ...; stty "$settings"` to save and restore the tty settings. – Stephane Chazelas Jun 04 '14 at 20:36
  • @StephaneChezales - im not at a computer - do you think the `tr` edit thing could work too? – mikeserv Jun 04 '14 at 20:46
  • 1
    No, because `tr` would buffer its output as its a pipe, and non-US keyboards have keys that send characters outside the `\1-\177` range. `dd` is the idiomatic way here. – Stephane Chazelas Jun 04 '14 at 20:51
  • @StephaneChezales - thanks again. I'll pull it. I think there's a way to get canonical mode tty's to translate like that, though. I cant remember. I'll check again when i get to a computer. – mikeserv Jun 04 '14 at 20:53
  • If you're redirecting `dd`'s stdin, then you should probably redirect `stty`'s as well (wrap the whole thing inside `{...} < /dev/tty`. Also, like for [@Jim's answer](http://stackoverflow.com/questions/92802/what-is-the-linux-equivalent-to-dos-pause#comment37074994_92813), if you want to read all the characters sent upon a key press, you'd need to do something like: `(settings=$(stty -g); stty raw min 99 time 1; dd count=1 bs=99 > /dev/null 2>&1; stty "$settings") < /dev/tty`. – Stephane Chazelas Jun 04 '14 at 21:10
  • `raw` implies `-isig` which may not be desirable. You might also want to flush the input buffer before issuing the prompt (`stty -icanon min 0 time 0; cat > /dev/null`). – Stephane Chazelas Jun 04 '14 at 21:11
  • @StephaneChezales - thats kinda funny - i was just looking at what i might do with `stty -icanon min N` - but it might have to wait til i get an actual screen/keyboard. just typing this comment on the tablet is a chore... i think i should be using `-icanon` anyway to preserve interrupts. i guess you put in that comment before i could hit submit. we're thinking along the same lines, though, i guess. – mikeserv Jun 04 '14 at 21:14
  • "probable" ?!? ... WHAT IS THAT MEAN ?!? ... **EXACTLY ?!?** read -n1 is working thank you very much, and is easy an simple – THESorcerer Mar 13 '18 at 09:05
  • 1
    I used `read -rsp $'Press any key to continue...\n' -n 1 key` before, but it always failed to reset my terminal's settings, so I'd have to run `reset` afterwards. This command works perfectly for me (on macOS 10.15)! I also added `stty -echo` after the `trap` line because I didn't want to see the character I typed. – telotortium Oct 29 '20 at 17:47
3

If you just need to pause a loop or script, and you're happy to press Enter instead of any key, then read on its own will do the job.

do_stuff
read
do_more_stuff

It's not end-user friendly, but may be enough in cases where you're writing a quick script for yourself, and you need to pause it to do something manually in the background.

mwfearnley
  • 3,303
  • 2
  • 34
  • 35
3

This function works in both bash and zsh, and ensures I/O to the terminal:

# Prompt for a keypress to continue. Customise prompt with $*
function pause {
  >/dev/tty printf '%s' "${*:-Press any key to continue... }"
  [[ $ZSH_VERSION ]] && read -krs  # Use -u0 to read from STDIN
  [[ $BASH_VERSION ]] && </dev/tty read -rsn1
  printf '\n'
}
export_function pause

Put it in your .{ba,z}shrc for Great Justice!

Tom Hale
  • 40,825
  • 36
  • 187
  • 242
2

This fixes it so pressing any key other than ENTER will still go to a new line

read -n1 -r -s -p "Press any key to continue..." ; echo

it's better than windows pause, because you can change the text to make it more useful

read -n1 -r -s -p "Press any key to continue... (cant find the ANY key? press ENTER) " ; echo
SwiftNinjaPro
  • 787
  • 8
  • 17
  • I think your solution is the only one that actually resembles the prompt from Windows, where you are asked to type any key on the same line. Except that before every dot `.` in the prompt, there is an additional space, for some reason – phil294 Nov 03 '21 at 17:23
  • Doesn't work in system() call. E.g. this C++ code `system("read -n1 -r -s -p \"Press any key to continue...\" ; echo");` displays an error: `sh: 1: read: Illegal option -n`. Though `read -n1` command works fine from the same Ubuntu shell. The `pause` command can be used as `system("pause");` In Windows programs. – Sergey Beloglazov Jan 03 '22 at 00:18
-1

Yes to using read - and there are a couple of tweaks that make it most useful in both cron and in the terminal.

Example:

time rsync (options)
read -n 120 -p "Press 'Enter' to continue..." ; echo " "

The -n 120 makes the read statement time out after 2 minutes so it does not block in cron.

In terminal it gives 2 minutes to see how long the rsync command took to execute.

Then the subsequent echo is so the subsequent bash prompt will appear on the next line.

Otherwise it will show on the same line directly after "continue..." when Enter is pressed in terminal.

SDsolar
  • 2,485
  • 3
  • 22
  • 32
-2

Try this:

function pause(){
   read -p "$*"
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Alex Fort
  • 18,459
  • 5
  • 42
  • 51