How to handle ctrl+c in a shell script?
Asked Answered
C

1

5

I am trying to handle ctrl+c in a shell script. I have code running in a while loop but I am calling the binary from a shell script and running it in the background so when I want to stop the binary it should stop. Code is below of hello.c

#include <stdio.h>
    
int main()
{
    while(1)
    {
        int n1,n2;
        printf("Enter the first number\n");
        scanf("%d",&n1);
        printf("Enter the second number\n");
        scanf("%d",&n2);
        printf("Entered number are n1 = %d , n2 =%d\n",n1,n2);
    }
}

Below is the bash script which I use.

#/i/bin/sh
echo run the hello binary
./hello < in.txt &

trap_ctrlc()
{
    ps -eaf | grep hello | grep -v grep | awk  '{print $2}' | xargs kill -9
    echo trap_ctrlc
    exit
}

trap trap_ctrlc SIGHUP SIGINT SIGTERM

After starting the script the hello binary is running continuously. I have killed this binary from other terminal using kill -9 pid command.

I have tried the trap_ctrlc function but it does not work. How to handle ctrl+c in a shell script?

In in.txt I have added the input so I can pass this file directly to the binary.

1
2

Output:

Enter the first number  
Enter the second number  
Entered number are n1 = 1 , n2 =2  
Enter the first number    
Enter the second number  
Entered number are n1 = 1 , n2 =2   
Enter the first number    
Enter the second number    
Entered number are n1 = 1 , n2 =2  

And it going continuously.

Conventionalism answered 1/10, 2020 at 16:16 Comment(2)
@TedLyngmo Thank you so much this works. I have one doubt if i ran say 2 or 3 binary in script then how to kill all binaries after hitting ctrl +c. And about the solution on script on line number 4 i am getting the error "Syntax error: "(" unexpected" The 'function' written before print_forever caused this issue. So i removed it.Conventionalism
kill -9 is SIGKILL, you can't trap SIGKILL. See: man7.org/linux/man-pages/man7/signal.7.html You could try kill -HUP and just plain kill.Ichthyology
B
6

Change your program so it checks if reading data actually succeeded:

#include <stdio.h>

int main()
{
    int n1,n2;
    while(1) {
        printf("Enter the first number\n");
        if(scanf("%d",&n1) != 1) return 0;   /* check here */
        printf("Enter the second number\n");
        if(scanf("%d",&n2) != 1) return 0;   /* check here */
        printf("Entered number are n1 = %d , n2 =%d\n",n1,n2);
    }
}

It will now terminate when the input from in.txt is depleted.

To make something that reads from in.txt many times, you could create a loop in your script that feeds ./hello forever (or until it's killed).

Example:

#!/bin/bash

# a function to repeatedly print the content in "in.txt"
function print_forever() {
    while [ 1 ];
    do
        cat "$1"
        sleep 1
    done
}

echo run the hello binary
print_forever in.txt | ./hello &
pid=$!
echo "background process $pid started"

trap_ctrlc() {
    kill $pid
    echo -e "\nkill=$? (0 = success)\n"
    wait $pid
    echo "wait=$? (the exit status from the background process)"
    echo -e "\n\ntrap_ctrlc\n\n"
}

trap trap_ctrlc INT

# wait for all background processes to terminate
wait

Possible output:

$ ./hello.sh
run the hello binary
background process 262717 started
Enter the first number
Enter the second number
Entered number are n1 = 1 , n2 =2
Enter the first number
Enter the second number
Entered number are n1 = 1 , n2 =2
Enter the first number
^C
kill=0 (0 = success)

wait=143 (the exit status from the background process)


trap_ctrlc

Another option can be to kill the child after the wait is interrupted:

#!/bin/bash

function print_forever() {
    while [ 1 ];
    do
        cat "$1"
        sleep 1
    done
}
 
echo run the hello binary
print_forever in.txt | ./hello &
pid=$!
echo "background process $pid started"
 
trap_ctrlc() {
    echo -e "\n\ntrap_ctrlc\n\n"
}
 
trap trap_ctrlc INT
 
# wait for all background processes to terminate
wait
echo first wait=$?
kill $pid
echo -e "\nkill=$? (0 = success)\n"
wait $pid
echo "wait=$? (the exit status from the background process)"`
``
Binns answered 1/10, 2020 at 16:24 Comment(10)
the hello binary is killed but hello.sh script is still running. After hitting the Ctrl+c not able to come out from the script. Enter the first number Enter the second number Entered number are n1 = 1 , n2 =2 Enter the first number ^C-e kill=0 (0 = success) ^Cnew.sh: 20: kill: No such process -e kill=1 (0 = success) ^Cnew.sh: 20: kill: No such process And still it going on so i grep the script and killed it from different terminalConventionalism
@SagarTalole Is that the result when you run the exact c program and bash script in the answer or is there any difference?Binns
No difference. I copied the code your shared and checked.Conventionalism
@SagarTalole That's odd. What if you try a different approach, like this? Can you create a paste of the output if you run that script and press ctrl-c?Binns
I am not able to open the link you provided due to security reason.Conventionalism
@SagarTalole Ok, I added it in the answer as an alternative. If this doesn't exit the script there's something wierd going on (like restarting the wait call that should be interrupted by the signal).Binns
Can you provide the output you get from running the alternative script? Use whatever pastebin-like service you can use.Binns
@SagarTalole This annoys me as well, so I'd like to find out where my answer doesn't hold. In a Posix environment, I'm pretty sure it should work.Binns
What does trap trap_ctrlc INT do in your last version? Thank you.Haiku
@Haiku Here's a quick intro: tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.htmlBinns

© 2022 - 2024 — McMap. All rights reserved.