父Shell关闭时子Shell中sudo ufw失效的解决方法
解决终端关闭后脚本无法自动关闭UFW端口的问题
我写了一个后台运行的Shell脚本,用来控制Syncthing并关联管理UFW防火墙,简化版代码如下:
#!/bin/bash sync () { # 开放Syncthing通信所用端口。 sudo ufw allow syncthing &> /dev/null # 启动Syncthing,脚本在此阻塞直至Syncthing关闭。 syncthing &> /dev/null # Syncthing关闭后,关闭其通信所用端口。 sudo ufw delete allow syncthing &> /dev/null } # 在后台运行sync函数前获取sudo权限。 sudo -v # 后台运行sync函数。 sync &
当启动脚本的终端保持开启时,脚本工作正常,但如果Syncthing运行期间关闭终端,Syncthing结束后防火墙端口不会自动关闭。有没有办法让脚本在终端提前关闭的情况下仍正常执行——也就是Syncthing关闭后自动关闭防火墙端口?
我还准备了无需安装Syncthing的测试脚本,输出会写入syslog,用来复现这个问题:
#!/bin/bash test_function () { echo '-- Opening port' | logger -t TEST sudo ufw allow 80 | logger -t TEST echo '-- Close the terminal you started this script from in the next 10 seconds' | logger -t TEST sleep 10 echo '-- Closing port' | logger -t TEST sudo ufw delete allow 80 | logger -t TEST } sudo -v test_function &
解决方法
方法1:用nohup让进程脱离终端控制
终端关闭时会给后台进程发送SIGHUP信号,导致进程终止。nohup可以让进程忽略该信号,同时将输出重定向到默认文件nohup.out(也可自定义路径)。
修改原脚本的启动逻辑:
sudo -v nohup sync &
对应测试脚本修改:
sudo -v nohup test_function &
这样即使关闭终端,脚本里的后续逻辑(比如关闭端口)依然会正常执行。
方法2:用disown移除进程的终端关联
如果已经通过&把进程放到后台,可执行disown让进程脱离当前终端的控制,终端关闭时就不会影响它:
sudo -v sync & disown
测试脚本同理:
sudo -v test_function & disown
方法3:创建systemd服务(长期稳定方案)
如果需要脚本长期可靠运行,推荐用systemd服务管理,它能彻底脱离终端,还支持开机自启、状态监控等功能。
- 创建服务文件
/etc/systemd/system/syncthing-ufw.service:
[Unit] Description=Syncthing with UFW port management After=network.target ufw.service [Service] Type=simple User=你的用户名 ExecStartPre=/usr/bin/sudo /usr/sbin/ufw allow syncthing ExecStart=/usr/bin/syncthing ExecStopPost=/usr/bin/sudo /usr/sbin/ufw delete allow syncthing Restart=no [Install] WantedBy=multi-user.target
- 重新加载systemd配置:
sudo systemctl daemon-reload
- 启动服务:
sudo systemctl start syncthing-ufw.service
- (可选)设置开机自启:
sudo systemctl enable syncthing-ufw.service
这种方式下,Syncthing启动时自动开端口,停止后自动关端口,完全不受终端状态影响。
内容的提问来源于stack exchange,提问作者countermeasure
相关产品推荐
相关产品推荐

