如何在shell脚本中处理ctrl+c?

5
我正在尝试在shell脚本中处理ctrl+c。我有一段代码在一个while循环中运行,但是我从一个shell脚本中调用二进制文件并在后台运行,所以当我想停止这个二进制文件时,它应该停止。下面是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);
    }
}

以下是我使用的bash脚本。
#/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

在启动脚本后,hello二进制文件将持续运行。我通过其他终端使用"kill -9 pid"命令杀死了这个二进制文件。

我尝试过trap_ctrlc函数,但它没有起作用。如何在shell脚本中处理ctrl+c信号?

in.txt中,我添加了输入,这样我就可以直接将该文件传递给二进制文件。

1
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   
Enter the first number    
Enter the second number    
Entered number are n1 = 1 , n2 =2  

而且它持续不断地进行。

@TedLyngmo 谢谢,这个可以用。我有一个疑问,如果我在脚本中运行2或3个二进制文件,那么如何在按下ctrl +c后杀死所有二进制文件。关于第4行脚本的解决方案,我遇到了错误“语法错误:”(“意外”。在print_forever之前编写的'function'引起了此问题。因此我将其删除了。 - Sagar Talole
"kill -9" 是 SIGKILL,你无法捕获 SIGKILL 信号。请参见:https://man7.org/linux/man-pages/man7/signal.7.html。你可以尝试使用 "kill -HUP" 或者普通的 "kill" 命令。 - Kingsley
1个回答

5

修改你的程序,以便检查读取数据是否成功:

#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);
    }
}

现在,当来自in.txt的输入耗尽时,它将终止。

要创建一个从in.txt多次读取的程序,您可以在脚本中创建一个循环,不断地向./hello提供输入(或者直到它被终止)。

示例:

#!/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

可能的输出:
$ ./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

另一种选择是在 wait 被中断后杀死该子进程:

#!/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)"`
``

hello二进制文件已被终止,但hello.sh脚本仍在运行。按下Ctrl+c后无法从脚本中退出。输入第一个数字 输入第二个数字 输入的数字为n1 = 1,n2 = 2 输入第一个数字 ^C-e kill=0(0 =成功)^Cnew.sh:20:kill:没有这个进程-e kill=1(0 =成功)^Cnew.sh:20:kill:没有这个进程 仍在进行中,因此我在不同的终端中grep脚本并将其杀死。 - Sagar Talole
@SagarTalole 当你运行答案中的确切 [tag:c] 程序和 [tag:bash] 脚本时,是否得到了相同的结果,或者存在差异? - Ted Lyngmo
没有区别。我复制了你分享的代码并进行了检查。 - Sagar Talole
@SagarTalole 这也让我很烦恼,所以我想找出我的答案哪里不正确。在Posix环境中,我相信它应该可以工作。 - Ted Lyngmo
在你的最新版本中,trap trap_ctrlc INT 是做什么用的?谢谢。 - d-b
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接