【问题标题】:running a bat file though python in current process在当前进程中通过 python 运行 bat 文件
【发布时间】:2013-02-04 23:26:32
【问题描述】:

我正在尝试通过 python 脚本构建一个大型系统。我首先需要为 Visual Studio 设置环境。遇到问题,我决定看看是否可以设置并启动 Visual Studio。我先设置了几个环境变量,然后调用C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat x64

一旦完成,我会致电devenv /useenv。如果我从命令提示符处执行这些操作,一切正常,我可以在 VS 中做我需要做的事情。我的python代码是:

import os
vcdir=os.environ['ProgramFiles(x86)']
arch = 'x64'
command  = 'CALL "' +vcdir+'\\Microsoft Visual Studio 11.0\\VC\\vcvarsall.bat" '+arch
os.system(command)
command = "CALL devenv /useenv"
os.system(command)

如果我运行它,bat 文件将运行,当它尝试 devenv 命令时,我发现它无法识别。看起来 bat 文件在与运行脚本的子进程不同的子进程中运行。我真的需要在我当前的进程中运行它。我的最终目标是在 python 脚本中完成整个构建,并且会多次调用devenv 来完成大部分构建。

谢谢。

【问题讨论】:

  • subprocess它会解决你所有的问题
  • @JakobBowyer:不,subprocess 本身并不能解决这个问题,因为它没有解决根本问题。
  • @MichaelB 我和你有类似的问题。我问了 [this] (stackoverflow.com/questions/38790813/how-can-i-run-cl-using-x64/…) 问题,现在我可以通过答案进行编译了。我现在要做的是从os.system() 执行这个命令。你知道我该如何配置它,以便os.system() 在 Visual Studio x64 Win64 命令提示符(2010)中运行 cmd 并在运行 cmd 之前执行 vcvarsall.bat 文件?谢谢

标签: python build visual-studio-2012


【解决方案1】:

我遇到了和你一样的问题。我试图将 vcvarsall.bat 作为用 python 编写的构建脚本的一部分运行,我需要 vcvarsall 创建的环境。我找到了一种方法。首先创建一个名为 setup_environment.bat 的包装脚本:

call "C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat" amd64
set > environment.txt

然后在您调用 vcvarsall.bat 的 Python 脚本中,运行包装脚本,然后将文本文件中的环境变量读取到当前环境中:

    this_dir = os.path.dirname(os.path.realpath(__file__)) # path of the currently executing script
    os.system('call ' + this_dir + '\setup_environment.bat') # run the wrapper script, creates environment.txt
    f = open('environment.txt','r')
    lines = f.read().splitlines()
    f.close()
    os.remove('environment.txt')
    for line in lines:
        pair = line.split('=',1)
        os.environ[pair[0]] = pair[1]

【讨论】:

    【解决方案2】:

    这是一个简单的例子,应该可以直接使用:

    import os
    import platform
    
    
    def init_vsvars():
        vswhere_path = r"%ProgramFiles(x86)%/Microsoft Visual Studio/Installer/vswhere.exe"
        vswhere_path = os.path.expandvars(vswhere_path)
        if not os.path.exists(vswhere_path):
            raise EnvironmentError("vswhere.exe not found at: %s", vswhere_path)
    
        vs_path = os.popen('"{}" -latest -property installationPath'.format(vswhere_path)).read().rstrip()
        vsvars_path = os.path.join(vs_path, "VC\\Auxiliary\\Build\\vcvars64.bat")
    
        output = os.popen('"{}" && set'.format(vsvars_path)).read()
    
        for line in output.splitlines():
            pair = line.split("=", 1)
            if(len(pair) >= 2):
                os.environ[pair[0]] = pair[1]
    
    if "windows" in platform.system().lower():
        init_vsvars()
        os.system("where cl.exe")
    

    请注意,python脚本退出后环境变量不起作用。

    【讨论】:

      【解决方案3】:

      您尝试执行的操作将行不通。 vcvarsall.bat 设置环境变量,这些变量只在特定进程中起作用。并且 Python 进程无法在同一进程空间内运行 CMD.exe 进程。

      我有几个解决方案给你。

      一种是运行vcvarsall.bat,然后找出它设置的所有环境变量并让Python为你设置它们。您可以使用命令set > file.txt 从 CMD shell 中保存所有环境变量,然后编写 Python 代码来解析该文件并设置环境变量。可能工作量太大了。

      另一种方法是创建一个运行vcvarsall.bat 的批处理文件,然后从该批处理文件中启动一个新的 Python 解释器。新的Python解释器会在一个新的进程中,但是会是CMD.exe进程下的子进程,会继承所有的环境变量。

      或者,嗯。也许最好的办法就是编写一个运行vcvarsall.bat 的批处理文件,然后运行devenv 命令。是的,这可能是最简单的,简单就是好的。

      需要注意的重要一点是,在 Windows 批处理文件语言(以及之前的 DOS 批处理文件)中,当您执行另一个批处理文件时,另一个批处理文件设置的变量设置在同一环境中。在 *NIX 中,您需要使用特殊命令 source 在同一环境中运行 shell 脚本,但它是批量运行的。但是你永远不会让变量在进程终止后持续存在。

      【讨论】:

      • 可以使用setx 命令在Windows 上对批处理文件中的环境变量进行持久更改,如this 答案中所述。不过,您可能需要修改 vcvarsall.bat 脚本才能使用它。
      • 嗯,我不知道setx,但我只是查了一下。它似乎是一个用于在启动新的CMD.exe shell 时设置适当的注册表项以设置环境变量的命令。这本身不会导致在 Python 首次运行时传递给 Python 的环境副本中设置变量值。而且我很确定您不想永久更改vcvarsall.bat;我相信这应该为 Visual Studio 的单个会话进行设置。
      • 顺便说一下,我刚刚查了一下,_winreg 模块中有一个与 setx 等效的 Python 本机:SetValue()SetValueEx()
      • SetValue()SetValueEx() in _winregsetsetx 批处理命令无关。一件大事是它们都会影响当前运行的 shell 进程。可以使用前者之一来设置新的cmd.exe 实例使用的注册表值(setx 也可能这样做)。影响cmd.exe 启动的注册表项是HKLM\SOFTWARE\Microsoft\Command Processor\AutoRunHKCU\Software\Microsoft\Command Processor\AutoRun。两者都在this MS 文章中进行了描述。
      • @martineau,如果我正确理解了setx 的文档,它会更改注册表项以使环境变量保持不变;这就是我提到 Python 注册表项功能的原因。我确定setx 也会改变环境。但是,当该进程终止时,CMD.exe 进程的环境副本将丢失。当一个新进程被创建时,它会得到一个父进程环境的副本,就像父进程创建子进程时一样;当子进程终止时,父进程的环境变量不会改变。
      【解决方案4】:

      在@derekswanson08 的回答的基础上,我想出了这个更成熟的方法。使用 VS 2017 自带的 wvwhere.exe。

      def init_vsvars():
          cprint("")
          cprint_header("Initializing vs vars")
      
          if "cygwin" in platform.system().lower():
              vswhere_path = "${ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe"
          else:
              vswhere_path = r"%ProgramFiles(x86)%/Microsoft Visual Studio/Installer/vswhere.exe"
          vswhere_path = path.expandvars(vswhere_path)
          if not path.exists(vswhere_path):
              raise EnvironmentError("vswhere.exe not found at: %s", vswhere_path)
      
          vs_path = common.run_process(".", vswhere_path,
                                       ["-latest", "-property", "installationPath"])
          vs_path = vs_path.rstrip()
      
          vsvars_path = os.path.join(vs_path, "VC/Auxiliary/Build/vcvars64.bat")
      
          env_bat_file_path = "setup_build_environment_temp.bat"
          env_txt_file_path = "build_environment_temp.txt"
          with open(env_bat_file_path, "w") as env_bat_file:
              env_bat_file.write('call "%s"\n' % vsvars_path)
              env_bat_file.write("set > %s\n" % env_txt_file_path)
      
          os.system(env_bat_file_path)
          with open(env_txt_file_path, "r") as env_txt_file:
              lines = env_txt_file.read().splitlines()
      
          os.remove(env_bat_file_path)
          os.remove(env_txt_file_path)
          for line in lines:
              pair = line.split("=", 1)
              os.environ[pair[0]] = pair[1]
      

      【讨论】:

        【解决方案5】:

        您应该使用的是标准库中名为 subprocess 的模块

        我已将您链接到标准库中的一个示例here.

        这里是你的情况的一个例子。

        import shlex, subprocess
        args = shlex.split(your_command)
        p = subprocess.Popen(args)
        

        如果您需要知道调用发生了什么,那应该执行它也返回stderr。您的命令甚至可能是包含两个 bat 文件的第三个 bat 文件,因此您可以获得所有环境变量。

        【讨论】:

        • 这恐怕不能解决第一个命令(vcvarsall.bat)设置的环境变量在下一个命令(devenv)运行时仍然存在的问题。
        • @martineau 所以在 python 脚本中设置它们。
        • 因为这样做基本上相当于将 Visual Studio 提供的 vcvarsall.bat 批处理文件转换为 Python 脚本。
        • 这个答案不起作用。 @martineau 告诉过你,现在我告诉你。 subprocess 将启动一个新进程,vcvarsall.bat 脚本将在该进程内设置环境变量,然后该进程将终止并且环境变量设置将丢失。然后当 Python 脚本运行命令启动 Visual Studio 时,不会设置任何环境变量。可能最好的解决方案是制作一个批处理文件,首先运行vcvarsall.bat,然后运行devenv 命令;如果它们在同一个批处理文件中,则devenv 将在同一个进程中运行。
        • @steveha:我倾向于同意创建一个批处理文件来运行这两个命令,然后从 Python 执行它听起来是最简单的解决方案。新的批处理文件可以很容易地由 Python 创建,然后在运行后删除。
        【解决方案6】:

        另一种解决方案是在与构建命令相同的 os.system 中调用 vcvarsall.bat:

        os.system("cd " + vcFolder + " & vcvarsall.bat amd64 & cd " + buildFolder + " & make")
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-08-03
          • 2013-10-02
          • 1970-01-01
          • 2016-03-16
          • 1970-01-01
          • 2011-01-25
          • 2015-09-23
          • 1970-01-01
          相关资源
          最近更新 更多