杀死在后台运行的Shell脚本


12

我已经编写了一个shell脚本来使用inotifyt-tools的inotifywait实用程序监视目录。我希望该脚本在后台连续运行,但是我也希望能够在需要时将其停止。

为了使它连续运行,我习惯了while true;像这样:

while true;
do #a set of commands that use the inotifywait utility
end

我已经将其保存在文件中/bin并使其可执行。为了使其在后台运行,我使用nohup <script-name> &并关闭了终端。

我不知道如何停止此脚本。我看过的答案在这里和非常密切相关的问题在这里

更新1: 根据下面的@InfectedRoot的答案,我已经能够使用以下策略解决我的问题。初次使用

ps -aux | grep script_name

sudo kill -9 <pid>用来杀死进程。然后,我不得不再次pgrep inotifywait使用sudo kill -9 <pid>ID作为返回值。

这行得通,但我认为这是一种凌乱的方法,我正在寻找更好的答案。

更新2: 答案包括杀死2个进程。这很重要,因为在命令行上运行脚本会启动2个进程,1个是脚本本身,2个是inotify进程



2
-9从中删除选项kill并使用just kill可以消除混乱。
2014年

1
太棒了 为了使一些“ $$”再次进入双关语现金箱,我想强调一下,该-9选项将在kill此处全部存在。:P
语法错误

为了完整起见:有一种干净的方法可以立即杀死两个进程:您可以杀死脚本的进程组以立即杀死它们,而不是分别杀死两个进程。该PGID是一样的shell脚本主进程的PID和kill它,你前面加上一个减号PGID: kill -- -<pgid>kill -9 -<pgid>等等
cg909

Answers:


6

要改进,使用killall和结合以下命令:

ps -aux | grep script_name
killall script_name inotifywait

或一行完成所有操作:

killall `ps -aux | grep script_name | grep -v grep | awk '{ print $1 }'` && killall inotifywait

您上面的解决方案有效,但单行命令无效。请您检查一下。我收到一个错误:没有过程
light94 2014年

@ light94 Error: no process应该只意味着您已经杀死了它。您可以通过打开其他两个程序(例如VLCGeany)并对其进行尝试来对其进行测试。
cremefraiche

嗨,@ cremefraiche,正如耳环所说的那样,您的代码有效..但是我一直在寻找替代解决方案,我看到的最常见的解决方案是使用kill <pid>。既然我的脚本没有以这种方式结束,我会担心我的代码是否不合适?你能指导我吗?
light94 2014年

您可以grep -v grepgrep script_name变成,以节省grep -e "[s]cript_name"
nmichaels

3

列出使用的后台作业

# jobs

然后选择先前的作业数量并运行

# fg 1 

在此处输入图片说明

将带到前台。

然后使用CTRL + C或更简单的方法杀死它,使用来找到脚本的PID

ps -aux | grep script_name

在此处输入图片说明

然后使用pid杀死

sudo kill -9 pid_number_here

2
第一种选择不适用于我的情况,因为我在说明脚本后关闭了外壳,并且我认为Jobs命令仅适用于同一外壳。另外,我尝试了第二个选项,它确实杀死了进程,但是当我执行pgrep inotifywait时,我仍然可以将其视为一个进程。
light94 2014年

即使您关闭外壳程序,jobs也会给出作业列表。仍然完成该过程,它将在那里。
巴宾·隆斯顿

2
我尝试过,但是没有。
light94 2014年

@ light94你是对的。这里经常发生的是,该jobs命令实际上仅外壳程序中显示正在运行的作业。一旦我关闭了某个终端窗口,访问它们的唯一方法就是通过ps(请参见上文)。因此,可以肯定的是您没有对此进行弥补。
语法错误

2

您可以使用ps+ greppgrep获取进程名称 / pid ; 以后使用killall/ pkill杀死进程名称或使用killpid杀死。以下所有内容均应起作用。

killall $(ps aux | grep script_name | grep -v grep | awk '{ print $1 }') && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $1 }' | xargs killall) && killall inotifywait
(ps -ef | grep script_name | grep -v grep | awk '{ print $2 }' | xargs kill) && killall inotifywait
(pgrep -x script_name | xargs kill) && pkill -x inotifywait
pkill -x script_name && pkill -x inotifywait

最重要的是,您应确保仅杀死希望杀死的确切过程

pkill/ pgrep匹配模式而不是确切的名称,因此更危险;在此处-x添加以匹配确切名称。

另外,在使用pgrep/时,pkill您可能需要

  • -f匹配完整的命令行(像ps aux这样)
  • -a 还可以打印进程名称。

执行上述操作后,我在每个应用程序中都收到使用错误。我正在复制粘贴并替换脚本名称。我使用kill作为输出
light94

script_name正在运行吗?它对我有用(debian jessie)
Chen Hongxu Chen

是的,它正在运行。并且您的解决方案与上述@cremefraiche解决方案几乎相同,因此我希望它能以相同的方式工作:/
light94 2014年

是的,我在某种程度上总结了这样做的可能方法,尤其是添加pgrep/ pkill。如果仍然出错,则可以pgrep不使用-x。基本上,我认为在不检查其他进程是否匹配的情况下,在一个命令行中杀死该进程不是一个好习惯。
陈洪旭2014年

1

您可能会说,有很多方法可以做到这一点。

关于您的“ UPDATE#2”-一般来说,终止父子层次结构中的任何进程通常都会终止所有关联的进程。但是有很多例外。理想情况下,您要终止流程树中的最后一个“子级”,然后,如果该子级的父级没有其他要运行的任务,则应退出。但是,如果您杀死父母,则当父母去世时,信号应传递给孩子,孩子也应退出-但在某些情况下,孩子进程可能会忽略该信号(通过陷阱或类似机制)并可能继续运行一个将被'init'流程(或类似流程)继承。但是这个流程行为的主题可能会变得很复杂,我将其保留在那里...

如果我不想使用控制脚本(如下所述),我喜欢的一种方法是使用“屏幕”实用程序来启动和管理该过程。“屏幕”命令具有很多功能,需要花费一些时间来掌握。我建议您阅读“屏幕”手册页以获取完整说明。一个在后台启动进程的快速示例是以下命令:

屏幕-d -m / path / to / program

这将在“屏幕”会话中启动“ / path / to / program”。

您可以使用以下命令查看正在运行的会话:

屏幕-ls

而且您可以随时使用以下命令重新连接到正在运行的程序:

屏幕-r

然后只需用^ C或其他命令将其终止。

除了可以随意重新连接和断开与进程的连接之外,“屏幕”还可以捕获程序可能产生的任何stdout()。


但是我个人在这些方面的偏爱是要有一个控制程序来管理过程的开始和停止。这可能会变得有些复杂,并且需要一些可能复杂的脚本。像任何脚本一样,有很多不错的方法可以做到这一点。我提供了一个bash示例,该示例通常用于启动和停止应用程序。如果您的任务很简单,则可以将其直接插入控制脚本中,也可以让该控制脚本调用另一个外部程序。请注意,此示例在管理过程方面绝不是全面的。我已经排除了以下情况的可能性:使用“开始”选项时,确保脚本尚未运行,验证正在运行的PID实际上是您启动的进程(例如,脚本尚未启动)。死亡,并且使用相同的PID启动了另一个进程),并验证脚本实际上是在第一个“ kill”请求中响应(退出)的。进行所有这些检查可能会变得很复杂,我不想使示例过于冗长和复杂。您可能想修改该示例以实践您的Shell脚本。

将以下代码保存到一个名为“ programctl”的文件中,使其可通过以下命令执行:

chmod 755程序ctl

然后编辑文件,并在以“ myscript”开头的案例部分中添加您的代码/脚本。

一切就绪后,假设“ programctl”位于当前目录中,则可以使用以下命令启动程序:

./programctl开始

并使用以下命令停止它:

./programctl停止

干杯。

#!/bin/bash
# Description:  A wrapper script used to stop/start another script.

#--------------------------------------
# Define Global Environment Settings:
#--------------------------------------

# Name and location of a persistent PID file

PIDFILE="/tmp/tmpfile-$LOGNAME.txt"

#--------------------------------------
# Check command line option and run...
# Note that "myscript" should not
# provided by the user.
#--------------------------------------

case $1
in
    myscript)
        # This is where your script would go.
        # If this is a routine 'bash' shell script, you can enter
        # the script below as illustrated in the example.  
        # Or you could simply provide the path and parameters
        # to another script such as /dir/name/command -options

        # Example of an embedded script:

        while true
        do
            # do something over and over...
            sleep 1
        done

        # Example of an external script:

        /usr/local/bin/longrun -x
    ;;

    start)
        # Start your script in the background.
        # (Note that this is a recursive call to the wrapper
        #  itself that effectively runs your script located above.)
        $0 myscript &

        # Save the backgound job process number into a file.
        jobs -p > $PIDFILE

        # Disconnect the job from this shell.
        # (Note that 'disown' command is only in the 'bash' shell.)
        disown %1

        # Print a message indicating the script has been started
        echo "Script has been started..."
    ;;

    stop)
        # Read the process number into the variable called PID
        read PID < $PIDFILE

        # Remove the PIDFILE
        rm -f $PIDFILE

        # Send a 'terminate' signal to process
        kill $PID

        # Print a message indicating the script has been stopped
        echo "Script has been stopped..."
    ;;

    *)
        # Print a "usage" message in case no arguments are supplied
        echo "Usage: $0 start | stop"
    ;;
esac

我尝试了您建议的“屏幕”方法,并且效果很好。我还没有尝试第二种方法。谢谢你的回答。
light94 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.