代码之家  ›  专栏  ›  技术社区  ›  Alex028502

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

  •  0
  • Alex028502  · 技术社区  · 3 年前

    我已经安装了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 [[ "$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报告:

    enter image description here

    enter image description here

    因此,它完全可以调用另一个bash程序并测量其覆盖率。


    然而

    当我试图将python程序放在中间时,事情并不像我希望的那样工作:

    $ 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.
    

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

    enter image description here


    所以。。。

    如果从python调用,它放入环境中的文件描述符“8”在子程序中似乎不再有效,但当一个bash程序直接调用另一个时,它在某种程度上是有效的。我还尝试使用以下方式调用子流程 shell=True 结果是子程序没有错误和覆盖信息。

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


    我知道它在python调用的子程序中使用了bashcov,因为我破解了bashcvo,使用了stderr而不是自定义文件描述符。。。这适用于早于4.1的bash版本

          # 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
    
    

    得到了这个: enter image description here


    我几乎可以在不使用的情况下显示正在发生的事情 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不会。有人知道是否有办法改变这一点吗?

    0 回复  |  直到 3 年前
        1
  •  1
  •   Alex028502    3 年前

    感谢您的评论和回复: https://stackoverflow.com/a/67108740/147356

    这起作用:

    替换:

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

    具有

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

    该功能可以关闭 Popen 但不是 run 具有 close_fds