Linux Bash陷阱未杀死子对象,导致意外的ctrl-c行为

Linux Bash陷阱未杀死子对象,导致意外的ctrl-c行为,linux,bash,inotify,Linux,Bash,Inotify,编辑 给未来的读者。这个问题的根源实际上是在交互式shell中运行函数,而不是将其放在单独的脚本中 另外,在我最初发布的代码中还有很多地方可以改进。关于本可以/应该做得更好的事情,请参见评论 /edit 我有一个bash函数,用于在目录中的文件发生更改时在后台重新运行进程(类似于Grunt,但出于一般目的)。运行时,脚本将按需要运行: 子进程已正确启动(包括所有子进程) 在文件更改时,将终止子项(包括子项)并重新启动 但是,在退出时(ctrl-c),没有进程被终止。此外,第二次按ctrl-c

编辑

给未来的读者。这个问题的根源实际上是在交互式shell中运行函数,而不是将其放在单独的脚本中

另外,在我最初发布的代码中还有很多地方可以改进。关于本可以/应该做得更好的事情,请参见评论

/edit

我有一个bash函数,用于在目录中的文件发生更改时在后台重新运行进程(类似于Grunt,但出于一般目的)。运行时,脚本将按需要运行:

  • 子进程已正确启动(包括所有子进程)
  • 在文件更改时,将终止子项(包括子项)并重新启动
但是,在退出时(ctrl-c),没有进程被终止。此外,第二次按ctrl-c将终止当前终端会话。我假设这是我的陷阱的一个问题,但一直无法确定问题的原因

这是rerun.sh的代码

#!/bin/bash
# rerun.sh

_kill_children() {
    isTop=$1
    curPid=$2
        # Get pids of children
    children=`ps -o pid --no-headers --ppid ${curPid}`
    for child in $children
    do
            # Call this function to get grandchildren as well
            _kill_children 0 $child
    done
    # Parent calls this with 1, all other with 0 so only children are killed
    if [[ $isTop -eq 0 ]]; then
            kill -9 $curPid 2> /dev/null
    fi
}

rerun() {
    trap " _kill_children 1 $$; exit 0" SIGINT SIGTERM
    FORMAT=$(echo -e "\033[1;33m%w%f\033[0m written")
    #Command that should be repeatedly run is passed as args
    args=$@
    $args &

    #When a file changes in the directory, rerun the process
    while inotifywait -qre close_write --format "$FORMAT" .
    do
        #Kill current bg proc and it's children
        _kill_children 1 $$
        $args & #Rerun the proc
    done
}

#This is sourced in my bash profile so I can run it any time
要对此进行测试,请创建一对可执行文件parent.sh和child.sh,如下所示:

#!/bin/bash
#parent.sh
./child.sh

#!/bin/bash
#child.sh
sleep 86400
然后找到rerun.sh文件的源代码并运行
rerun./parent.sh
。在另一个终端窗口中,我
观察“ps-ef | grep pts/4”
以查看重新运行的所有进程(在本例中为pts/4)。触摸目录中的文件会触发parent.sh和children的重新启动。[ctrl-c]退出,但保持pids运行。[ctrl-c]再次杀死pts/4上的bash和所有其他进程

所需行为:在[ctrl-c]上,杀死子对象并正常退出shell。帮忙

-- 代码来源:

Inotify的想法来自:


杀害儿童:

首先,这不是一个好的做法。明确跟踪您的孩子:

children=( )
foo & children+=( "$!" )
…然后,您可以明确地杀死或等待它们,请参考列表中的
“${children[@]}”
。如果您也想获得孙子女,这是
fuser-k
和锁文件的好用户:

lockfile_name="$(mktemp /tmp/lockfile.XXXXXX)" # change appropriately
trap 'rm -f "$lockfile_name"' 0

exec 3>"$lockfile_name" # open lockfile on FD 3
kill_children() {
    # close our own handle on the lockfile
    exec 3>&-

    # kill everything that still has it open (our children and their children)
    fuser -k "$lockfile_name" >/dev/null

    # ...then open it again.
    exec 3>"$lockfile_name"
}

rerun() {
    trap 'kill_children; exit 0' SIGINT SIGTERM
    printf -v format '%b' "\033[1;33m%w%f\033[0m written"

    "$@" &

    #When a file changes in the directory, rerun the process
    while inotifywait -qre close_write --format "$format" .; do
        kill_children
        "$@" &
    done
}

首先,这不是一个好的做法。明确跟踪您的孩子:

children=( )
foo & children+=( "$!" )
…然后,您可以明确地杀死或等待它们,请参考列表中的
“${children[@]}”
。如果您也想获得孙子女,这是
fuser-k
和锁文件的好用户:

lockfile_name="$(mktemp /tmp/lockfile.XXXXXX)" # change appropriately
trap 'rm -f "$lockfile_name"' 0

exec 3>"$lockfile_name" # open lockfile on FD 3
kill_children() {
    # close our own handle on the lockfile
    exec 3>&-

    # kill everything that still has it open (our children and their children)
    fuser -k "$lockfile_name" >/dev/null

    # ...then open it again.
    exec 3>"$lockfile_name"
}

rerun() {
    trap 'kill_children; exit 0' SIGINT SIGTERM
    printf -v format '%b' "\033[1;33m%w%f\033[0m written"

    "$@" &

    #When a file changes in the directory, rerun the process
    while inotifywait -qre close_write --format "$format" .; do
        kill_children
        "$@" &
    done
}

首先,这不是一个好的做法。明确跟踪您的孩子:

children=( )
foo & children+=( "$!" )
…然后,您可以明确地杀死或等待它们,请参考列表中的
“${children[@]}”
。如果您也想获得孙子女,这是
fuser-k
和锁文件的好用户:

lockfile_name="$(mktemp /tmp/lockfile.XXXXXX)" # change appropriately
trap 'rm -f "$lockfile_name"' 0

exec 3>"$lockfile_name" # open lockfile on FD 3
kill_children() {
    # close our own handle on the lockfile
    exec 3>&-

    # kill everything that still has it open (our children and their children)
    fuser -k "$lockfile_name" >/dev/null

    # ...then open it again.
    exec 3>"$lockfile_name"
}

rerun() {
    trap 'kill_children; exit 0' SIGINT SIGTERM
    printf -v format '%b' "\033[1;33m%w%f\033[0m written"

    "$@" &

    #When a file changes in the directory, rerun the process
    while inotifywait -qre close_write --format "$format" .; do
        kill_children
        "$@" &
    done
}

首先,这不是一个好的做法。明确跟踪您的孩子:

children=( )
foo & children+=( "$!" )
…然后,您可以明确地杀死或等待它们,请参考列表中的
“${children[@]}”
。如果您也想获得孙子女,这是
fuser-k
和锁文件的好用户:

lockfile_name="$(mktemp /tmp/lockfile.XXXXXX)" # change appropriately
trap 'rm -f "$lockfile_name"' 0

exec 3>"$lockfile_name" # open lockfile on FD 3
kill_children() {
    # close our own handle on the lockfile
    exec 3>&-

    # kill everything that still has it open (our children and their children)
    fuser -k "$lockfile_name" >/dev/null

    # ...then open it again.
    exec 3>"$lockfile_name"
}

rerun() {
    trap 'kill_children; exit 0' SIGINT SIGTERM
    printf -v format '%b' "\033[1;33m%w%f\033[0m written"

    "$@" &

    #When a file changes in the directory, rerun the process
    while inotifywait -qre close_write --format "$format" .; do
        kill_children
        "$@" &
    done
}

这里有很多令人讨厌的东西<例如,code>args=$@表面上是错误的--
$@
是一个数组,
args
是一个标量。您无法可靠地将数组放入标量中,当您稍后运行
$args
时,您遇到了中的所有陷阱。此外,引用。所以,这是非常错误的。(也不要使用SIGKEY,除非你已经给出了一个SigTalk,并且等待足够长的时间让它尝试优雅地清理)。如果你想对孙子和C进行隐式跟踪,考虑使用一个锁文件和<代码> Fux-K<代码>,而不是试图解析<代码> PS < /代码>。bash通过包含它违反了POSIX规范(不是扩展,而是违反——当在其命令行上传递
-e
时,符合POSIX的echo将在其输出上打印
-e
);看
printf
是最佳实践的替代方案。这里有很多令人讨厌的地方<例如,code>args=$@表面上是错误的--
$@
是一个数组,
args
是一个标量。您无法可靠地将数组放入标量中,当您稍后运行
$args
时,您遇到了中的所有陷阱。此外,引用。所以,这是非常错误的。(也不要使用SIGKEY,除非你已经给出了一个SigTalk,并且等待足够长的时间让它尝试优雅地清理)。如果你想对孙子和C进行隐式跟踪,考虑使用一个锁文件和<代码> Fux-K<代码>,而不是试图解析<代码> PS < /代码>。bash通过包含它违反了POSIX规范(不是扩展,而是违反——当在其命令行上传递
-e
时,符合POSIX的echo将在其输出上打印
-e
);看
printf
是最佳实践的替代方案。这里有很多令人讨厌的地方<例如,code>args=$@表面上是错误的--
$@
是一个数组,
args
是一个标量。您无法可靠地将数组放入标量中,当您稍后运行
$args
时,您遇到了中的所有陷阱。此外,引用。所以,这是非常错误的。(也不要使用SIGKEY,除非你已经给出了一个SigTalk,并且等待足够长的时间让它尝试优雅地清理)。如果你想对孙子和C进行隐式跟踪,考虑使用一个锁文件和<代码> Fux-K<代码>,而不是试图解析<代码> PS < /代码>。bash通过包含它违反了POSIX规范(不是扩展,而是违反——当在其命令行上传递
-e
时,符合POSIX的echo将在其输出上打印
-e
);看
printf
是最佳实践的替代方案。这里有很多令人讨厌的地方<例如,code>args=$@表面上是错误的--
$@
是一个数组,
args
是一个标量。您无法可靠地将数组放入标量中,当您稍后运行
$args
时,您遇到了中的所有陷阱。此外,引用。所以,非常错误。(还有,唐