问题描述
我已经安装了 bashcov.. 以测量一堆 bash 脚本中的代码覆盖率:
I have installed bashcov.. in order to measure code coverage in a bunch of bash scripts:
$ bash --version GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)
...
$ bundle exec bashcov --version
bashcov 1.8.2
这完全按预期工作:
$ cat main.sh
#! /usr/bin/env bash
set -e
if [[ "$1" == "" ]]
then
echo no args
else
echo args
fi
./sub.sh
$ cat sub.sh
#! /usr/bin/env bash
set -e
if [[ "$1" == "" ]]
then
echo no args in subprogram
else
echo args in subprogram
fi
$ bundle exec bashcov ./main.sh
no args
no args in subprogram
Run completed using bashcov 1.8.2 with Bash 5.0, Ruby 2.7.0, and SimpleCov 0.15.1.
Coverage report generated for /bin/bash ./main.sh to ~/bashcov/coverage. 7 / 9 LOC (77.78%) covered.
我得到这些 html 报告:
I get these html reports:
所以它完全可以调用另一个 bash 程序并测量它的覆盖范围.
So it can totally call another bash program and measure the coverage for it too.
然而
当我尝试将 python 程序放在中间时,事情并不像我希望的那样工作:
When I try to put a python program in the middle, things don't work quite as I would like:
$ cat main.sh
#! /usr/bin/env bash
set -e
if [[ "$1" == "" ]]
then
echo no args
else
echo args
fi
python3 -u sub.py
$ cat sub.py
import subprocess
print("running subprograms")
subprocess.run(["./sub.sh"])
$ # and leave sub.sh the same as above
$ bundle exec bashcov ./main.sh
no args
running subprograms
bash: BASH_XTRACEFD: 8: invalid value for trace file descriptor
+BASHCOV>f4697250-c3da-4086-.....set -e
+BASHCOV>f4697250-c3da-4086-.....[[ '' == '' ]]
+BASHCOV>f4697250-c3da-4086-.....echo no args in subprogram
no args in subprogram
Run completed using bashcov 1.8.2 with Bash 5.0, Ruby 2.7.0, and SimpleCov 0.15.1.
Coverage report generated for /bin/bash ./main.sh to ~/bashcov/coverage. 4 / 9 LOC (44.44%) covered.
并且我们没有子程序的覆盖信息:
and we have no coverage info for the subprogram:
所以...
如果从 python 调用,它放在环境中的文件描述符 '8' 看起来在子程序中不再有效,但是当一个 bash 程序直接调用另一个程序时,它以某种方式有效.我还尝试使用 shell=True
调用子进程,结果没有错误,也没有子程序的覆盖信息.
It looks like that the file descriptor '8' that it puts in the environment is no longer valid in the subprogram if called from python, but somehow is valid when one bash program calls another directly. I have also tried calling the subprocess with shell=True
and the result is no error and no coverage info for the subprogram.
有人知道它是如何组合在一起的吗?为什么文件描述符在直接从 main 调用的 bash 程序中有效,而在从 python 调用的程序中无效?
Does anybody know how it fits together? Why is the file descriptor valid in a bash program that is called directly from main, but not in one that is called from python?
我知道它在 python 调用的子程序中使用了 bashcov,因为我破解了 bashcov 使用 stderr 而不是自定义文件描述符......它适用于 4.1 之前的 bash 版本
I know it is using bashcov in the subprogram called by python because I hacked bashcov to uses stderr instead of the custom file descriptor... which it does for bash versions older than 4.1
# Older versions of Bash (< 4.1) don't have the BASH_XTRACEFD variable
+ if Bashcov.bash_xtracefd? and false
- if Bashcov.bash_xtracefd?
options[fd] = fd # bind FDs to the child process
env["BASH_XTRACEFD"] = fd.to_s
else
# Send subprocess standard error to @xtrace.file_descriptor
options[:err] = fd
# Don't bother issuing warning if we're silencing output anyway
unless Bashcov.mute
write_warning <<-WARNING
you are using a version of Bash that does not support
BASH_XTRACEFD. All xtrace output will print to standard error, and
your script's output on standard error will not be printed to the
console.
WARNING
end
得到这个:
我几乎可以在不使用 bashcov
的情况下展示正在发生的事情.这实际上只是一个 python 和 linux 问题.也许我应该创建一个新问题:
I can pretty much show what is going on without using bashcov
at all.This is really just a python and linux question. Maybe I should have create a new question:
我创建了一组新的示例程序,如下所示:
I have created a new set of sample programs that look like this:
==> m.sh <==
#! /usr/bin/env bash
set -e
exec 3<>messages
ls -l /proc/$$/fd/
echo bash program called directly
./s.sh
==> r.py <==
import subprocess
import sys
import os
print("python program:")
subprocess.run(["ls", "-l", "/proc/%s/fd/" % os.getpid()])
print("bash program called by python:")
subprocess.run(sys.argv[1:], check=True)
==> s.sh <==
#! /usr/bin/env bash
set -e
ls -l /proc/$$/fd/
这就是结果
$ ./m.sh
total 0
lrwx------ 1 me me 64 Apr 15 22:43 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 15 22:43 1 -> pipe:[188295]
lrwx------ 1 me me 64 Apr 15 22:43 2 -> /dev/pts/2
lr-x------ 1 me me 64 Apr 15 22:43 255 -> /home/me/bashcov/m.sh
lrwx------ 1 me me 64 Apr 15 22:43 3 -> /home/me/bashcov/messages
bash program called directly
total 0
lrwx------ 1 me me 64 Apr 15 22:43 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 15 22:43 1 -> pipe:[188295]
lrwx------ 1 me me 64 Apr 15 22:43 2 -> /dev/pts/2
lr-x------ 1 me me 64 Apr 15 22:43 255 -> /home/me/bashcov/s.sh
lrwx------ 1 me me 64 Apr 15 22:43 3 -> /home/me/bashcov/messages
python program:
total 0
lrwx------ 1 me me 64 Apr 15 22:43 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 15 22:43 1 -> pipe:[188295]
lrwx------ 1 me me 64 Apr 15 22:43 2 -> /dev/pts/2
lrwx------ 1 me me 64 Apr 15 22:43 3 -> /home/me/bashcov/messages
bash program called by python:
total 0
lrwx------ 1 me me 64 Apr 15 22:43 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 15 22:43 1 -> pipe:[188295]
lrwx------ 1 me me 64 Apr 15 22:43 2 -> /dev/pts/2
lr-x------ 1 me me 64 Apr 15 22:43 255 -> /home/me/bashcov/s.sh
bash 将所有文件描述符传递给子进程,但 python 不会.有没有人知道是否有办法改变它?
bash passes all file descriptors down to subprocesses, but python doesn't. Does anybody know if there is a way to change that?
推荐答案
感谢评论,以及这个答案:https://stackoverflow.com/a/67108740/147356
Thanks to a comment, and this answer: https://stackoverflow.com/a/67108740/147356
这有效:
替换:
subprocess.run(["./sub.sh"])
与
subprocess.Popen(["./sub.sh"], close_fds=False).communicate()
可以为 Popen
关闭该功能,但不能为 close_fds
that feature can be turned off for Popen
but not run
with close_fds
这篇关于bashcov - 如果子程序被 python 调用,则不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!