Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash, CTRL+C in eval not interrupting the main script

In my bash script, I'm running an external command that's stored in $cmd variable. (It could be anything, even some simple bash oneliner.)

If ctrl+C is pressed while running the script, I want it to kill the currently running $cmd but it should still continue running the main script. However, I would like to preserve the option to kill the main script with ctrl+C when the main script is running.

#!/bin/bash
cmd='read -p "Ooook?" something; echo $something; sleep 4 ' 
while true; do
    echo "running cmd.."
    eval "$cmd"     # ctrl-C now should terminate the eval and print "done cmd"
    echo "done cmd"
    sleep 5         # ctrl-C now should terminate the main script
done

Any idea how to do it some nice bash way?

Changes applied based on answers:

#! /bin/bash

cmd='read -p "Ooook1?" something; read -p "Oook2?" ; echo $something; sleep 4 ' 
while true; do
    echo "running cmd.."
    trap "echo Interrupted" INT
    eval "($cmd)"     # ctrl-C now should terminate the eval and print "done cmd"
    trap - INT
    echo "done cmd"
    sleep 5         # ctrl-C now should terminate the main script
done

Now, pressing ctrl+C while "Ooook1?" read will break the eval only after that read is done. (it will interrupt just before "Oook2") However it will interrupt "sleep 4" instantly.

In both cases it will do the right thing - it will just interrupt the eval subshell, so we're almost there - just that weird read behaviour..

like image 508
Miroslav Avatar asked Oct 09 '22 18:10

Miroslav


1 Answers

If you can afford having the eval part run in a subshell, "all" you need to do is trap SIGINT.

#! /bin/bash

cmd='read -p "Ooook1?" something; read -p "Oook2?" ; echo $something; sleep 4 ' 
while true; do
    echo "running cmd.."
    trap "echo Interrupted" INT
    eval "($cmd)"     # ctrl-C now should terminate the eval and print "done cmd"
    trap - INT
    echo "done cmd"
    sleep 5         # ctrl-C now should terminate the main script
done

Don't know if that will fit your specific need though.

$ ./t.sh 
running cmd..
Ooook1?^CInterrupted
done cmd
^C
$ ./t.sh 
running cmd..
Ooook1?qsdqs^CInterrupted
done cmd
^C
$ ./t.sh 
running cmd..
Ooook1?qsd
Oook2?^CInterrupted
done cmd
^C
$ 
GNU bash, version 4.1.9(2)-release (x86_64-pc-linux-gnu)
like image 68
Mat Avatar answered Oct 13 '22 11:10

Mat