我无法阻止python脚本使用os.system()
运行,但我能够使用os.system()
运行它。当我在终端上运行相同的命令来终止进程时,它可以工作。
我有web_plants.py
脚本中的代码片段,使用Flask在html中部署它:
@app.route("/auto/water/<toggle>")
def auto_water(toggle):
running = False
if toggle == "ON":
templateData = template(text = "Auto Watering On")
for process in psutil.process_iter():
try:
if process.cmdline()[1] == 'auto_water.py':
templateData = template(text = "Already running")
running = True
except:
pass
if not running:
os.system("python auto_water.py")
else:
templateData = template(text = "Auto Watering Off")
os.system("pkill -f water.py")
return render_template('main.html', **templateData)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80, debug=True)
我有一个water.py
脚本,其中包含html页面上按钮的auto_water
功能。但功能很好,所以我认为这不是问题。
def auto_water(pump_pin = 38):
init_output(pump_pin)
print("Reading Soil Humidity values from ADS1115")
try:
while True:
m = adc.read_adc(0, gain=GAIN)
print('Moisture Level:{0:>6}'.format(m))
time.sleep(1)
if m>23000:
#GPIO.output(pump_pin, GPIO.LOW)
print "relay on"
else:
#GPIO.output(pump_pin, GPIO.HIGH)
print "relay off"
except KeyboardInterrupt:
GPIO.cleanup()
except Exception as e:
print (e)
我有这个auto_water.py
文件导入water
并调用auto_water()
函数来运行它。使用此文件的目的是当我点击&#34;停止&#34;时,能够在文件运行时终止该文件。按钮。
import water
#From the water.py script, call the auto_water function and run it as a process
if __name__ == "__main__":
water.auto_water()
脚本一直在运行,我必须使用终端中的sudo pkill -f water.py
将其终止,以使其停止。但是同样的命令对web_plants.py
文件不起作用,当我按下&#34;停止&#34;按钮。然而,当我杀了它时,网页会注册它并显示文本&#34; Auto Watering OFF&#34;如web_plants.py
文件中的以下行:
templateData = template(text="Auto Watering Off")
os.system("pkill -f water.py")
答案 0 :(得分:2)
您可以使用os.kill()
吗?
import os
import signal
os.kill(pid, signal.SIGTERM)
如果您没有pid
随时可用,请使用subprocess.check_output
获取它。 (如果您正在使用Python 3.5 +,请使用subprocess.run
。)
import os
import signal
import subprocess
def get_water_pid():
return int(subprocess.check_output(['pgrep', '-f', 'water.py']).strip())
def kill_water_process():
pid = get_water_pid()
os.kill(pid, signal.SIGTERM)
答案 1 :(得分:2)
假设脚本只是由您的服务器启动,您可以在启动时存储脚本的PID,然后使用它来终止它。
看起来您的现有代码没有在后台启动脚本;它会让Flask线程永远等待os.system
返回。这有点奇怪,但我现在也会做同样的事情。
另外,我只会编写裸机函数,您可以根据需要进行Flask,并将PID存储在全局中,您可以将其替换为您正在使用的任何持久存储。
autowater_pid = None
def start():
global pid
p = subprocess.Popen(["python", "auto_water.py"])
pid = p.pid
p.wait()
def stop():
global pid
if pid is not None:
os.kill(pid)
pid = None
您可以通过多种方式对此进行扩展。例如,如果我们已经有pid
,我们可以跳过开始新副本。或者我们甚至可以检查它是否仍在运行,并且如果它以某种方式死亡则开始一个新的:
def start():
global pid
if pid is not None:
try:
os.kill(pid, 0) # check that it's still running
return
except ProcessLookupError:
pass
p = subprocess.Popen(["python", "auto_water.py"])
pid = p.pid
p.wait()
但回到那个问题,等待过程完成。你不仅要永久地绑定一个线程,你还要做到这一点,如果Flask进程因任何原因(升级,负载均衡器,未处理的异常,等等)发生故障,脚本将被操作系统杀死。您应该考虑以某种方式守护脚本。这是一个太大的主题,无法包含在答案的旁注中,但您应该能够从搜索开始找到足够的信息(或者,至少,提出一个很好的单独问题来询问SO)。
另一种替代方法 - 守护进程更常用的方法 - 是让脚本本身(或守护程序包装器)在启动时将自己的pid写入已知位置的文件。像这样:
with open('/var/run/auto_water.pid', 'w') as f:
f.write(f'{os.getpid()}\n')
退出时可以os.remove('/var/run/auto_water.pid')
。
然后您的服务器可以从文件中读取该数字以了解要杀死的PID。同样,你可以输入代码来处理进程死亡的情况,而不会删除它的pidfile等。
但是,为了正确地做到这一点,你将要阅读锁定pidfiles,处理意外现有文件的正确行为等。
或者,更好的是,只需从PyPI获取一个pid文件处理库。
或者,甚至可能更好,获取一个守护程序处理库来执行守护程序和pidfile以及错误处理。
答案 2 :(得分:1)
系统调用阻止执行。在执行完成之前,该函数不会将任何内容返回给浏览器。
试试这个:
os.system("python auto_water.py&")
&安培;产生过程并立即返回。