【问题标题】:Subprocess changing directory子进程更改目录
【发布时间】:2014-02-19 19:52:12
【问题描述】:

我想在子目录/超级目录中执行一个脚本(我需要首先在这个子目录/超级目录中)。我无法让subprocess 进入我的子目录:

tducin@localhost:~/Projekty/tests/ve$ python
Python 2.7.4 (default, Sep 26 2013, 03:20:26) 
[GCC 4.7.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> import os
>>> os.getcwd()
'/home/tducin/Projekty/tests/ve'
>>> subprocess.call(['cd ..'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 524, in call
    return Popen(*popenargs, **kwargs).wait()
  File "/usr/lib/python2.7/subprocess.py", line 711, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1308, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

Python 抛出 OSError,我不知道为什么。无论我是尝试进入现有的子目录还是向上一个目录(如上所述)都没有关系 - 我总是会遇到同样的错误。

【问题讨论】:

  • 如果改用os.chdir()会发生什么。

标签: python subprocess


【解决方案1】:

只需使用os.chdir
示例:

>>> import os
>>> import subprocess
>>> # Lets Just Say WE want To List The User Folders
>>> os.chdir("/home/")
>>> subprocess.run("ls")
user1 user2 user3 user4

【讨论】:

    【解决方案2】:

    如果您需要更改目录,请运行命令并获取 std 输出:

    import os
    import logging as log
    from subprocess import check_output, CalledProcessError, STDOUT
    log.basicConfig(level=log.DEBUG)
    
    def cmd_std_output(cd_dir_path, cmd):
        cmd_to_list = cmd.split(" ")
        try:
            if cd_dir_path:
                os.chdir(os.path.abspath(cd_dir_path))
            output = check_output(cmd_to_list, stderr=STDOUT).decode()
            return output
        except CalledProcessError as e:
            log.error('e: {}'.format(e))
    
    def get_last_commit_cc_cluster():
        cd_dir_path = "/repos/cc_manager/cc_cluster"
        cmd = "git log --name-status HEAD^..HEAD --date=iso"
        result = cmd_std_output(cd_dir_path, cmd)
        return result
    
    log.debug("Output: {}".format(get_last_commit_cc_cluster()))
    
    
    Output: "commit 3b3daaaaaaaa2bb0fc4f1953af149fa3921e\nAuthor: user1<user1@email.com>\nDate:   2020-04-23 09:58:49 +0200\n\n
    

    【讨论】:

    • 你在重塑check_call,很糟糕。
    【解决方案3】:

    如果您想拥有 cd 功能(假设 shell=True)并且仍想根据 Python 脚本更改目录,则此代码将允许“cd”命令工作。

    import subprocess
    import os
    
    def cd(cmd):
        #cmd is expected to be something like "cd [place]"
        cmd = cmd + " && pwd" # add the pwd command to run after, this will get our directory after running cd
        p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) # run our new command
        out = p.stdout.read()
        err = p.stderr.read()
        # read our output
        if out != "":
            print(out)
            os.chdir(out[0:len(out) - 1]) # if we did get a directory, go to there while ignoring the newline 
        if err != "":
            print(err) # if that directory doesn't exist, bash/sh/whatever env will complain for us, so we can just use that
        return
    

    【讨论】:

      【解决方案4】:

      我想这些天你会这样做:

      import subprocess
      
      subprocess.run(["pwd"], cwd="sub-dir")
      

      【讨论】:

        【解决方案5】:

        您的代码尝试调用一个名为cd .. 的程序。你想要的是调用一个名为cd的命令。

        但是cd 是一个shell 内部。所以你只能称它为

        subprocess.call('cd ..', shell=True) # pointless code! See text below.
        

        但这样做是没有意义的。 由于没有进程可以更改另一个进程的工作目录(同样,至少在类 UNIX 操作系统上,但在 Windows 上也是如此),此调用将子shell更改其目录并立即退出。

        您可以使用os.chdir()subprocess 命名参数cwd 来实现您想要的,它会在执行子进程之前立即更改工作目录。

        例如,要在根目录下执行ls,你可以这样做

        wd = os.getcwd()
        os.chdir("/")
        subprocess.Popen("ls")
        os.chdir(wd)
        

        或者干脆

        subprocess.Popen("ls", cwd="/")
        

        【讨论】:

        • cd 通常也以二进制形式存在,而不仅仅是内置的 shell。 OP 的真正问题是他正在调用二进制文件cd ..,是的。 (你的第三段将是他的下一个问题,很好的答案。)
        • @LeonWeber cd 应该如何作为二进制文件工作?它不能唱它父母的工作目录。
        • 我说的是 Linux。不过好点。我在想自己,答案是:/usr/bin/cdbuiltin cd "$@" 组成——所以它也只是调用了内置的 shell cd
        • @The_Diver 这就是为什么必须将cd 实现为内部shell 命令。没有其他方法可以做到这一点。内部 shell 命令在与 shell 相同的进程中执行。我所说的子shell 是为shell=True 执行的shell。它获取要执行的命令,执行并退出。
        • 我认为您建议的方法中的一两个示例会很有用。
        【解决方案6】:

        subprocess.callsubprocess 模块中的其他方法都有一个cwd 参数。

        此参数确定您要执行进程的工作目录。

        所以你可以这样做:

        subprocess.call('ls', shell=True, cwd='path/to/wanted/dir/')
        

        查看文档subprocess.popen-constructor

        【讨论】:

          【解决方案7】:

          基于此答案的另一个选项:https://stackoverflow.com/a/29269316/451710

          这允许您在同一进程中执行多个命令(例如cd)。

          import subprocess
          
          commands = '''
          pwd
          cd some-directory
          pwd
          cd another-directory
          pwd
          '''
          
          process = subprocess.Popen('/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE)
          out, err = process.communicate(commands.encode('utf-8'))
          print(out.decode('utf-8'))
          

          【讨论】:

          • 这只是一种迂回且低效的做法shell=True, executable='/bin/bash'
          【解决方案8】:

          要将your_command 作为不同目录中的子进程运行,请将cwd 参数作为suggested in @wim's answer 传递:

          import subprocess
          
          subprocess.check_call(['your_command', 'arg 1', 'arg 2'], cwd=working_dir)
          

          子进程无法更改其父进程的工作目录 (normally)。使用子进程在子 shell 进程中运行 cd .. 不会更改父 Python 脚本的工作目录,即 the code example in @glglgl's answer is wrongcd 是一个shell builtin(不是一个单独的可执行文件),它只能在same 进程中改变目录。

          【讨论】:

            【解决方案9】:

            您想使用可执行文件的绝对路径,并使用Popencwd kwarg 来设置工作目录。请参阅docs

            如果 cwd 不是 None,则子目录的当前目录将更改为 cwd 在执行之前。注意这个目录不考虑 搜索可执行文件时,不能指定程序的路径 相对于 cwd。

            【讨论】:

            • 这取决于是否应该执行另一个子进程。如果是这样,那么您的方式是正确的。但是对于只让自己的程序在不同的目录中运行,这无济于事。
            • 你是什么意思它不会帮助?这是一种显而易见的方法。
            • 不,因为它只是更改了我将要启动的进程的 cwd,例如 subprocess.call(['ls', '-l'], cwd='/')。这会将 cwd 更改为 /,然后以 -l 作为参数运行 ls。但是,如果我想做os.chdir('/') 然后open('etc/fstab', 'r'),我不能用任何关于subprocess.XXX(cwd='/') 的东西替换os.chdir(),因为它不会有帮助,如前所述。这是两个完全不同的场景。
            • 这就是为什么我的回答说使用可执行文件的绝对路径,你错过了那部分吗?
            • 不,我没有。我想我放弃了。如果我想更改当前工作目录并打开一个文件,我没有可执行文件。这是完全不同的情况。顺便说一句:如果我按预期使用cwd=,则无需使用绝对路径。我也可以subprocess.call(['bin/ls', '-l'], cwd='/')
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-11-06
            • 2014-09-30
            • 2020-06-05
            • 2020-11-09
            • 1970-01-01
            • 1970-01-01
            • 2011-04-04
            相关资源
            最近更新 更多