我想实现这样的事情问答但对于子外壳来说。这是我正在尝试的一个最小示例:
(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
运行sleep
exec
- 您可以使用 SIGPIPE 而不是 SIGTERM 来避免该消息
- 在列表上下文中不加引号的变量在
bash
.
说了这么多,你可以这样做:
(
subshell=$BASHPID
kill -s PIPE "$subshell" &
sleep 600
)
echo subshell done
(如果您想要杀死而不仅仅是子shell,请替换sleep 60
为,在这种情况下,当您杀死它时,子shell甚至可能没有时间运行)。exec sleep 60
kill
sleep
sleep
无论如何,我不确定你想用它来实现什么。
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 岁时的 sudo
- 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 实现不会等待sudo
aftergrep
返回(让它在后台运行,直到收到 SIGPIPED),并且使用bash
,您也可以使用grep ... <(sudo ...)
语法来做到这一点,其中bash
does not wait for sudo
one after grep
has 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
命令输出可以实现此目的。