0

In Windows Batch/CMD, to pause the execution of a script, you would use

echo 'This is before pause.' pause echo 'The key was pressed, continuing...' 

How would this be realised using a standard terminal like say bash (but zsh or others should also work at best)? I could of course write an easy C application, but I can't believe, that there is no canonical way - sleep is not, what I'm looking for.

I found these straight-forward approaches using read:

  • read -p 'Press [Enter] key to start backup...'
  • read -p 'Press any key to resume ...'
  • read -s -n 1 -p 'Press any key to continue . . .' inside a function

but they give me XXX.sh: 4: read: arg count for the first two or 3: read: Illegal option -n respectively. I'm using bash 4.4.19(1)-release.

1
  • You can also consider this SO-Q/A Commented Oct 15, 2021 at 9:29

5 Answers 5

4

The main reason your read commands don't work is that they are for bash but you're using sh.

The cited article says,

On Unix-like operating systems, read is a builtin command of the bash shell

It is also a builtin command in many other shells, including the sh that you're using, but the syntax for read in sh is much less rich.

#!/bin/bash ... read -p 'Press [Enter] key to continue: ' ... 

Remember to make the file executable and then run it as a program

# Assume the script is called "demo" chmod a+x demo ./demo 
2
  • 2
    Strictly speaking, the -p flag doesn’t appear in POSIX, either; the POSIX standard only defined the -r flag. The -p flag just happens to be implemented by many shells, including Dash and BusyBox. Commented Dec 6, 2021 at 6:01
  • @FranklinYu, -p in ksh (from the early 80s and a subset of which bash is mostly a clone of) or zsh is to read from the co-process so it will likely never make it into POSIX. To issue a prompt, it's read 'var?prompt' there. Commented Apr 10, 2023 at 17:07
4

In plain sh, there's no easy way to wait for a single key press. It's a lot easier to ask the user to press Enter, because there's an easy way to wait for the user to enter a line.

The read built-in command reads a line. You need to give it the name of a variable to store the content of the line, even if you aren't going to use it.

echo 'Press Enter to continue.' read -r line echo 'Continuing.' 

If your script starts with #!/usr/bin/env bash, #!/usr/bin/env zsh or equivalent, read has additional options that let it wait for a single key press. I don't think it's worth bothering with it, and teaching your users which keys do or do not count as “any key”.

Note that it is very common to run scripts unattended and with no terminal on Unix-like systems. Typical servers have nobody logged in on the console most of the time. So asking the user to confirm something is very uncommon.

2
read_a_key() ( exec </dev/tty t=$(stty -g) trap 'stty "$t"; return' EXIT INT QUIT TERM stty raw -echo isig dd count=1 2>/dev/null ) printf '%s ' 'Press ANY KEY to continue ... ' key=$(read_a_key) echo OK, continuing 

Use it as read_a_key >/dev/null if you don't care what key the user had pressed.

This should also work fine with keys which turn into multiple characters (as e.g. F1), unlike bash's read -n1, which will leave over spurious OP or [[A characters for the next command ;-)

3
  • Note that in shells like bash, if you intercept SIGINT and exit normally in a child process, then the script won't exit upon ^C (same for SIGQUIT vs ^\ ). To work around that, you'd need to replace that return with a kill -s INT <mypid> (where <mypid> is the pid of the subshell (but beware not all shells run subshells in a child process)). Commented Jan 30, 2021 at 7:35
  • As you're reading the key from the controlling tty (/dev/tty), you may want to issue the prompt there as well instead of stdout which may be redirected (or maybe stderr). Commented Jan 30, 2021 at 7:43
  • @StéphaneChazelas it's even more broken than that. a) it doesn't handle TSTP/CONT b) it calls stty "$t" twice c) it doesn't work in yash. I've tried to do something that work in as many shells as possible (thence the awkward return), but the results are still less than stellar ;-) A version which do WCE correctly but only work in bash would not catch the INT, QUIT and TERM at all, but rely on bash running the EXIT trap even when terminating because of a signal. Commented Jan 31, 2021 at 22:19
0

I found a comment by user epileg on a site suggesting this code snippet (slightly edited), claiming that "there is a way to cleanly do that on Linux:"

#!/bin/bash ... echo -n 'Press <ENTER> to continue...' stty -echo dd count=1 1>/dev/null 2>&1 stty echo echo #Produces an empty line in the end, can be removed. ... 

While obvious to some users, it should also be mentioned, that the shebang #!/bin/bash doesn't decide what is used if you do. Let's say the above script was called demo and you typed sh demo or zsh demo, sh or zsh are called respectively, not bash.

0

You are on the right track with:

read -p 'press Enter ...' 

but if you get that arg_count error, you are not using bash but another shell. Fix: just add an argument, e.g.:

read -p 'press Enter ...' x 

Now it should work in your shell. The extra argument can be a dummy name, or exploited to hold the user answer, e.g.:

echo You answered: $x 
1
  • -p is for bash. Most other shells use read 'var?prompt' to specify a prompt when they do have a way to specify a prompt. The only standard option to read is -r. Commented Apr 10, 2023 at 17:26

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.