bashcov - 如果子程序被 python 调用则不起作用

bashcov - does not work if subprogram is called by python

我安装了 bashcov.. 以测量一堆 bash 脚本中的代码覆盖率:

$ 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 [[ "" == "" ]]
then
  echo no args
else
  echo args
fi

./sub.sh
$ cat sub.sh 
#! /usr/bin/env bash

set -e

if [[ "" == "" ]]
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 报告:

所以它完全可以调用另一个 bash 程序并测量它的覆盖率。


然而

当我尝试在中间放置一个 python 程序时,事情并没有像我希望的那样工作:

$ cat main.sh 
#! /usr/bin/env bash

set -e

if [[ "" == "" ]]
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.

我们没有子程序的覆盖信息:


所以...

如果从 python 调用,它放在环境中的文件描述符“8”似乎在子程序中不再有效,但在 bash 程序调用时不知何故有效另一个直接。我也试过用 shell=True 调用子进程,结果没有错误,也没有子程序的覆盖信息。

有人知道它是如何组合在一起的吗?为什么文件描述符在直接从 main 调用的 bash 程序中有效,但在从 python 调用的程序中无效?


我知道它在 python 调用的子程序中使用了 bashcov,因为我破解了 bashcov 以使用 stderr 而不是自定义文件描述符...它确实如此bash 版本早于 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 的问题。也许我应该提出一个新问题:

我创建了一组新的示例程序,如下所示:

==> 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 不会。有人知道是否有办法改变它吗?

感谢评论和回答:

这个有效:

替换:

subprocess.run(["./sub.sh"])

subprocess.Popen(["./sub.sh"], close_fds=False).communicate()

可以为 Popen 关闭该功能,但不能为 runclose_fds