2

I need to code Ctrl+D in a shell script in order to exit and display the result of a code. The goal it's instead of "bye" in my code I tried to find a solution on google and in other website to replace "bye" by a Ctrl+D code.

Can you help me please?

This is my code :

 touch file.txt
 while :
do
    shopt -s nocasematch
    
  read INPUT_STRING 
  case $INPUT_STRING in
     bob)
        echo "boy">>file.txt
        ;;
    alicia)
        echo "girl">>file.txt
        ;;
    cookie)
           echo "dog">>file.txt
           ;;
    bye)
        cat file.txt
        break
        echo "      "
        ;;
    *)
        echo "unknown">>file.txt
        ;;   
  esac
  
done
Jackdaw
  • 7,626
  • 5
  • 15
  • 33
  • 1
    So, if the user hits Ctrl+D for the `read INPUT_STRING` command, you expect the variable to contain a Ctrl+D character? The shell doesn't work that way: the variable will hold an empty string. – glenn jackman Oct 04 '18 at 15:01
  • note that there's no need to call `shopt` inside a loop. asuming there is another command `while` can be followed by a sequence whose exit code is the exit code of the last command e.g. `while cmd1..; read ..; do ...; done` – Nahuel Fouilleul Oct 04 '18 at 15:08

3 Answers3

3

CTRL+D is not a sequence which is sent to input but closes the input so after CTRL+D read will exit with a non null exit code and the variable INPUT_STRING will be empty. in your script read exit code is not checked. Depending on what you need, you can either check if INPUT_STRING is empty or check read exit code.

while read INPUT_STRING; do
...
done 
# put the code after
cat file.txt ...

or

while :; do
   ...
   read INPUT_STRING || break
   ...
done
# put the code after
cat file.txt ...

or

case $INPUT_STRING in
...
'') # to match empty INPUT_STRING (could be because of CTRL+D)
Nahuel Fouilleul
  • 18,726
  • 2
  • 31
  • 36
  • So if I really understand I can use at the same time CTRL+D in order to exit form my program and to display the result? –  Oct 04 '18 at 15:06
  • Think you for you help and you explanation I understand the problem, and I was able to resolve it. –  Oct 04 '18 at 15:21
1

How about checking the return status of read before going into your case?

E.g.:

if [ $? -eq 1 ] ;
  ...
fi

I don't think you can change the behaviour of read to trap the ctrl-d in the result variable.

Venantius
  • 2,471
  • 2
  • 28
  • 36
imodude
  • 46
  • 6
  • +1 for checking return status. I'd probably want to check that return status not equals 0, instead of checking for the specific value of 1, which might change across platforms – Gus Oct 04 '18 at 15:12
  • after `read` and before `case`, add an `if` statement to check for non-zero return status from `read`. And you should probably put the `case` into an else block to stop that happening if you got the `ctrl-d` – imodude Oct 04 '18 at 15:20
  • The CTRL+D is not seen by the bash but by the tty: see `stty -a`, after `stty eof ^A` for example CTRL+A will close the tty. Note also bash behaviour can be changed `set -o ignoreeof` (default is unset `set +o ignoreeof`) – Nahuel Fouilleul Oct 04 '18 at 15:35
  • Checking exit status is great, but using `$?` is not a good way to do it. Just make it `while read input_string; do` and the `while` itself will check whether `read` succeeded. That's much less buggy -- `read input string; if [ $? -eq 1 ]; then ...` will exit the script immediately should `read` fail if `set -e` is enabled before the `if` is ever run. See [Why is checking $? to see if a command succeeded or not an anti-pattern?](https://stackoverflow.com/questions/36313216/why-is-testing-to-see-if-a-command-succeeded-or-not-an-anti-pattern) – Charles Duffy Nov 14 '21 at 21:33
0

Caveat: This answer literally does exactly what is requested. However, keyboard typed terminal input must quote the ctrl-D and send it ( ctrl+v   ctrl+d   Enter ) so the script can "see" it since bash keyboard processing will "trap & grab" and process the ctrl-D without propagation otherwise.

Consequently bash preprocesses, with some predefined function, every keyboard control code:

( ^@ ^A ... ^Z ^[ ^\ ^] ^^ ^_ ^? where ^ means hold ctrl then type the character)

so that the ^V predefined function "quotes" and thus escapes other ctrl'dkey functionality, so the key character code can be propagated.

aside curio:

ctrl@ aka ctrlshift2 over the decades usefully creates a null character code
ctrl[ aka Esc also useful when Esc is missing or broken

Cut to the chase:

echo  ^v^d>ctrl-D.txt
gedit  your-script.sh  ctrl-D.txt

Highlight the ctrl-D character code (it is the only) character in ctrl-D.txt and copy and paste it to replace the bye in your-script.sh.

Note bene: the embedded code is exactly 0x04 preceded by white space and postceded by a ).

The script will now terminate on receiving a ctrl-D, but here's the rub. If bash is the shell it will not receive it because bash will have already processed it. AFAIK there does not exist a unix / linux shell which does not behave like this so it is necessary to quote the ctrl-D so the shell does not grab it. (Ctrl-D was one of the early primitives "used to end text input or to exit a Unix shell".)

To quote the ctrl-D and pass it on through the shell to the script type ctrl+v   ctrl+d   Enter in bash. YMMV (your mileage may vary) and some other quoting method may be available in other shells.



The long answer:

To embed a control code in a shell script quote it using ctrl+v.

It is understood that the ^letter sequences in the coding examples are not to be typed literally with an ^, but as a ctrl and letter combination.

Type ctrl+v ctrl+d contiguously. (for expediency hold the ctrl and type v then d)
Thus typing echo ctrl+vd

linuxuser@ubuntu:~$ echo ^v^d

get this:

____
|00|
|04|

but as a single "code revealed" character, not the 12 _'s |'s #'s used here to represent the result.

To embed a control code like ^D in a file such as bye use

echo ctrl+vd >bye

Thus:

linuxuser@ubuntu:~$ echo now you can see -^v^d- via cat -v >bye
linuxuser@ubuntu:~$ cat -v bye
now you can see -^D- via cat -v

reveals the ctrld character by the two characters, an ^ and a D.

For the curious:

Note that quoting the quote to quote a code has been useful.
Thus (hold) ctrl (type) vvvz (and then release ctrl) will create a ^V^Z sequence in bash.

A poignant point worth noting is that files may contain arbitrary bytes. Ergo control codes per se do not "mean anything". It is on exposure to interpretation, like bash scripts, that various sequences might have meaning as control codes. It is very useful in bash that ctrlv can "disable" bash control code interpretation by its quoting mechanism. Most particularly, quoting is useful within files themselves that are to be interpreted by bash as scripts.

Coda:
linuxuser@ubuntu:~$ echo -e "echo 'ctrl-D^v^j -^v^d-' >bye \ncat -v bye" >doit.sh
linuxuser@ubuntu:~$ ./doit.sh
prints
ctrl-D
-^D-
tested with
linuxuser@ubuntu:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.2 LTS
Release: 18.04
Codename: bionic

ekim
  • 1
  • 1