子流程更改目录


98

我想在子目录/超级目录中执行脚本(我需要先在此子目录/超级目录中)。我无法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,我不知道为什么。无论是尝试进入现有的子目录还是进入一个目录(如上所述)都没有关系-我总是会遇到相同的错误。


1
如果使用会发生什么os.chdir()吧。
greole

Answers:


151

您的代码尝试执行的操作是调用名为的程序cd ..。您想要的是称为的命令cd

但是cd是外壳内部的。所以你只能称它为

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

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

您可以os.chdir()使用subprocess命名参数或通过使用命名参数来实现所需功能,该参数cwd在执行子流程之前立即更改工作目录。

例如,要ls在根目录中执行,您可以

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

或简单地

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

1
cd通常也以二进制形式存在,而不仅仅是内置的shell。OP的真正问题在于,他正在调用二进制文件cd ..,是的。(而且您的第三段将是他的下一个问题,所以很好的答案。)
Leon Weber

@LeonWeber应该如何cd以二进制形式工作?它不能诵读其父母的工作目录。
glglgl 2014年

2
我说的是Linux。好点。我在想自己,这就是答案:/usr/bin/cdbuiltin cd "$@"–组成,因此它也调用内置的shell cd
Leon Weber

1
@The_Diver这就是为什么cd必须作为内部shell命令来实现的原因。没有其他方法可以做到。内部Shell命令在与Shell相同的过程中执行。我所说的subshel​​l是为执行的shell shell=True。它获取要执行的命令,执行并退出。
glglgl 2015年

1
我认为您建议的方法中的一两个示例会很有用。
sscirrus

57

your_command在另一个目录中作为子进程运行cwd,请按照@wim的答案中的建议传递参数:

import subprocess

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

子进程无法更改其父级的工作目录(通常是)。cd ..使用子进程在子shell进程中运行不会更改父Python脚本的工作目录,即@glglgl的答案中的代码示例错误cd是内置的Shell(不是单独的可执行文件),它只能在同一过程中更改目录。


24

您想使用可执行文件的绝对路径,并使用cwdkwarg ofPopen设置工作目录。参见文档

如果cwd不为None,则子级的当前目录将在执行前更改为cwd。请注意,搜索可执行文件时不会考​​虑此目录,因此您无法指定程序相对于cwd的路径。


这取决于是否应该执行另一个子流程。如果是这样,您的方法就是正确的方法。但是对于只有自己的程序在不同目录中运行的情况,这无济于事。
glglgl 2014年

您什么意思将无济于事?这是一种显而易见的方法。
2014年

1
不,因为它只是改变了我将要启动的进程的cwd,例如subprocess.call(['ls', '-l'], cwd='/')。这会将cwd更改为/,然后ls-las参数运行。但是,如果我想再做一次os.chdir('/'),那么open('etc/fstab', 'r')我就不能用os.chdir()任何有关的内容代替,subprocess.XXX(cwd='/')因为它无济于事。这是两个完全不同的方案。
glglgl 2014年

这就是为什么我的回答说要使用可执行文件的绝对路径,您错过了那部分吗?
2014年

2
不,我没有。我想我放弃了。如果要更改当前工作目录并打开文件,则没有可执行文件。这是完全不同的情况。顺便说一句:如果我cwd=按预期使用,则无需使用绝对路径。我也可以subprocess.call(['bin/ls', '-l'], cwd='/')
glglgl 2014年

17

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

此参数确定要在其中执行过程的工作目录。

因此,您可以执行以下操作:

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

查看docs 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'))

1
这只是回旋处且效率低下的方式shell=True, executable='/bin/bash'
三人房

3

我想这几天你会做:

import subprocess

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

0

如果您希望具有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

-1

如果需要更改目录,请运行命令并获取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,效果很差。
三点吃
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.