Python和Pytest调用包含tail的shell命令失败
环境: 平台qnx——Python 2.7.12、pytest-4.6.9、py-1.8.1、Plugy-0.13.1 插件:json-report-1.2.1、shell-0.2.3 注: 我知道Python2.7很旧,不受支持,但目前QNX没有其他版本 问题: 我正在运行一个测试,当某个关键字出现在某个服务的日志中时,该测试应该会终止该服务。为此,我需要在后台运行它。 为此,我使用以下shell命令:Python和Pytest调用包含tail的shell命令失败,python,pytest,qnx,Python,Pytest,Qnx,环境: 平台qnx——Python 2.7.12、pytest-4.6.9、py-1.8.1、Plugy-0.13.1 插件:json-report-1.2.1、shell-0.2.3 注: 我知道Python2.7很旧,不受支持,但目前QNX没有其他版本 问题: 我正在运行一个测试,当某个关键字出现在某个服务的日志中时,该测试应该会终止该服务。为此,我需要在后台运行它。 为此,我使用以下shell命令: def test_kill_process(): expected_output=
def test_kill_process():
expected_output="XXXXXXXXX"
expected_rc=0
check_kill_process(expected_output, expected_rc)
import os
def check_kill_process(expected_output, expected_rc):
test_log = File(r"/path/to/log")
erase_log_entry = "Action"
service=MyService()
service.start()
sleep(2)
kill_command = "tail -f " + test_log.file_path + " | grep --line-buffered " + erase_log_entry + \
" | while read ; do kill " + service.pid + " ; done &"
os.popen(kill_command)
service.action()
f = open(test_log.file_path, "r")
output = f.read()
assert re.search(expected_output, output)
========================================================================
没有Pytest甚至Python,它的工作就像一个符咒
如果我尝试使用subprocess模块运行该命令,测试将无限期冻结。
如果我尝试使用os.popen或os.system,命令将以错误结尾:
tail: read failed in '/path/to/logfile' (Invalid argument)
此外,如果我试着做同样的事情,只要一只“猫”,我就会得到这样的结果:
--stdout--: Broken pipe
如果有人有任何想法,请提前感谢 与其使用
子流程
(这通常是unidiomatic python的症状),不如只打开(日志文件,'r')
,并在循环中使用file.readline()
来读取文件?这将产生与使用tail
相同的效果,而不需要子流程的开销(生成的代码也更干净、更容易理解)
这对于您要做的事情应该非常有效:
def wait_for_line(filename):
f = open(filename, 'r')
while 1:
if line := f.readline():
if re.match(some_regex, line):
return True
else:
sleep(0.5)
下面是一个完整的工作示例:
import threading
from time import sleep
import re
FILENAME = 'some_file'
REGEX = r'^foo\s...!\s\d+$'
REGEX_MATCH = "foo bar! 123"
def perform_other_action(filename):
f = open(filename, 'w')
sleep(5) # just for effect
f.write(REGEX_MATCH)
def wait_for_line(filename):
f = open(filename, 'r')
while 1:
if line := f.readline():
if re.match(REGEX, line):
return True
else:
sleep(0.5) # throttle the read frequency if no new lines are found
with open(FILENAME, 'w') as f: # make sure file exists
pass
t = threading.Thread(target=wait_for_line, args=(FILENAME,))
t.start() # returns immediately, wait_for_line runs in background
perform_other_action(FILENAME)
t.join()
根据经验,subprocess和popen喜欢命令序列而不是字符串。我认为技术原因在于不同的系统如何解释字符串路径和参数。有关部门内的更多说明,请查看 但是,例如,如果我想执行命令
git commit-m“修复一个bug”。
我必须将该字符串命令拆分为一个由空格分隔的列表
#命令作为字符串
cmd='git commit-m“修复了一个bug。”
#命令作为参数序列。考虑使用SLEX.SPLIT()代替
cmd=cmd.split(“”)
#现在使用新的格式化参数调用popen
os.popen(cmd)
使脚本更加健壮。对于更复杂的情况,可以使用内置函数shlex.split()
将字符串拆分为类似shell的语法
另外,看起来您还在命令中使用
|
管道。调用popen使管道工作时,必须传递shell=True
。如果尝试使用子流程-subprocess
在空环境中运行,则不会定义日志文件
或关键字
。使用env
arg传递环境。对不起,我在这里写得不太好。我直接传递参数,而不是作为shell变量。我重写了这个问题。感谢您的及时回答,但这不是问题…请共享完整的源代码问题是我需要在后台运行此代码,以便我还可以向服务发出最终将与shell行中的条件匹配的操作。@RazvanBadea在启动此循环之前,您能运行该操作吗?如果不可能,那么创建第二个线程来管理操作如何?我的第一个选择是线程。我遇到了同样的问题-挂起。我甚至制作了一个小的testscript,它单独使用python运行得很好,但使用PyTest挂起。不过我会尝试颠倒顺序。您是否尝试过用本机os.kill()
方法替换子流程?将MyService()
设置放在测试夹具中也是一个好主意。谢谢您的输入。始终使用子流程的列表,但不知道shlex。但这仍然不能解决我的问题。对于subprocess.Popen(shlex.split(kill_命令)),它只是挂起。看起来您还在命令中使用|
管道。调用popen使管道工作时,必须传递shell=True
。根据这篇文章尝试过,但结果相同。测试无限期地挂起。