默默杀死子shell?

默默杀死子shell?

我想实现这样的事情问答但对于子外壳来说。这是我正在尝试的一个最小示例:

(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600)

echo subshell done

我怎样才能让它只subshell done返回而不是:

./test.sh: line 4:  5439 Terminated              ( subshell=$BASHPID; ( kill $subshell && wait $subshell 2> /dev/null ) & sleep 600 )
subshell done

编辑:我在这里的术语可能是错误的,我所说的子外壳是指第一组括号内的过程。

更新:

我想发布实际程序中的片段以了解上下文,上面是一个简化:

# If subshell below if killed or returns error connected variable won't be set
(if [ -n "$2" ];then

      # code to setup wpa configurations here

      # If wifi key is wrong kill subshell
      subshell=$BASHPID
      (sudo stdbuf -o0 wpa_supplicant -Dwext -i$wifi -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 \
        | grep -m 1 "pre-shared key may be incorrect" \
        && kill -s PIPE "$subshell") &

      # More code which does the setup necessary for wifi

) && connected=true

# later json will be returned based on if connected is set

答案1

笔记:

  • wait $subshell不会工作,因为$subshell它不是您正在运行的进程的子进程wait。无论如何,您不会等待进程执行此操作,wait因此这并不重要。
  • kill $subshell将杀死子 shell,但如果子 shell 在运行sleep时已成功启动它,则不会杀死该子 shell。但是,您可以在同一进程中kill运行sleepexec
  • 您可以使用 SIGPIPE 而不是 SIGTERM 来避免该消息
  • 在列表上下文中不加引号的变量在bash.

说了这么多,你可以这样做:

(
  subshell=$BASHPID
  kill -s PIPE "$subshell" &
  sleep 600
)
echo subshell done

(如果您想要杀死而不仅仅是子shell,请替换sleep 60为,在这种情况下,当您杀死它时,子shell甚至可能没有时间运行)。exec sleep 60killsleepsleep

无论如何,我不确定你想用它来实现什么。

sleep 600 &

sleep如果这就是您想要做的(或者(sleep 600 &)如果您想sleep从主 shell 中隐藏该进程),那么在后台启动将是一种更可靠的方法

现在与您的实际情况

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf

命令,请注意,它sudo会生成一个子进程来运行该命令(如果只是因为它可能需要记录其状态或随后执行一些 PAM 会话任务)。stdbuf然而,将在同一个进程中执行wpa_supplicant,因此最终您将在wpa_supplicant的祖先中拥有三个进程(除了脚本的其余部分之外):

  1. 子外壳
  2. 1 岁时的 sudo
  3. wpa_supplicant(之前运行 stdbuf)作为 2 的孩子

如果你杀死 1,那不会自动杀死 2。但是,如果你杀死 2,除非它带有像 SIGKILL 这样无法被拦截的信号,否则会杀死 3,因为sudo它会将收到的信号转发到它运行的命令。

无论如何,这不是您想要在这里杀死的子 shell,它是 3 个或至少 2 个。

现在,如果它正在运行,root而脚本的其余部分没有运行,那么您将无法如此轻松地杀死它。

您需要将其kill完成root,因此您需要:

sudo WIFI="$wifi" bash -c '
  (echo "$BASHPID" &&
   exec stdbuf -o0 wpa_supplicant -Dwext -i"$WIFI" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1
  ) | {
    read pid &&
      grep -m1 "pre-shared key may be incorrect" &&
      kill -s PIPE "$pid"
  }'

这样,wpa_supplicant将在与子 shell 相同的进程中运行,$BASHPID就像我们使用exec.

我们通过管道获取 pid 并kill以 root 身份运行。

请注意,如果您准备再等一会儿,

sudo stdbuf -o0 wpa_supplicant -Dwext -i"$wifi" -c/etc/wpa_supplicant/wpa_supplicant.conf 2>&1 |
  grep -m1 "pre-shared key may be incorrect"

wpa_supplicant用 SIGPIPE 自动杀死(由系统,所以没有权限问题)下次grep它在消失后向该管道写入一些内容。

一些 shell 实现不会等待sudoaftergrep返回(让它在后台运行,直到收到 SIGPIPED),并且使用bash,您也可以使用grep ... <(sudo ...)语法来做到这一点,其中bashdoes not wait for sudoone after grephas returned。

更多内容请参见Grep 找到匹配后退出很慢?

答案2

子 shell 是指属于某个 shell 子级的 shell 命令,例如为bash -i您提供提示的交互式登录 shell的子级$。你不在子 shell 中运行命令 - 您可以选择将其作为独立进程运行。听起来这可能是合适的,因为您不希望它的 stdout / stderr 弄乱进度条的外观,并且因为您不希望父 shell 报告甚至注意到其子 shell 的死亡。

有一些标准工具可以实现这一点,例如守护进程和 nohup。 (也可以看看男人 页面.) 你可能最好的是诺哈普。这是一个使用它来运行一个简单程序的示例,它确实不是创建 nohup.out:

$ nohup true 2>&1 > /dev/null &

让您的程序或程序的包装脚本将其 PID 记录在 /tmp/my.pid 中——bash 使其可作为变量使用$$。然后用进度条监控进程即可

$ kill `cat /tmp/my.pid`

当它不再需要该程序进行任何更多处理时。或者,您可能更愿意将程序名称指定为killall.

答案3

您可能正在寻找这个

#!/bin/bash
(subshell=$BASHPID
  (kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) &
wait $! 2>/dev/null

echo subshell done

这里子 shell 被置于后台,然后父 shell 等待,但等待输出发送到 /dev/null。这捕获了Terminated消息。

请注意,如果您更改等待捕获输出到文件,例如,wait $! 2>wait_output您将看到

 ./foo.sh: line 5:  1939 Terminated              ( subshell=$BASHPID; ( kill $subshell & wait $subshell 2> /dev/null ) & sleep 600 )

显示Terminated来自父 shell。

进行一个小检查,看看在终止之前是否有一些活动,它是否有效

#!/bin/bash
(subshell=$BASHPID
 (sleep 1; kill $subshell & wait $subshell 2>/dev/null) &
sleep 600) & wait 2>wait_output

echo subshell done

此示例将在打印前暂停一秒钟subshell done。此示例还展示了如何在同一行上进行后台处理和等待,例如& wait 2>wait_output。我不确定这与wait $!.

这里要注意的关键是消息Terminated来自顶级父 shell 作业控制。这就是子 shell 终止并生成消息的原因。这就是您想要捕获输出的地方。重定向wait命令输出可以实现此目的。

相关内容