子流程更改目录

问题:子流程更改目录

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

I want to execute a script inside a subdirectory/superdirectory (I need to be inside this sub/super-directory first). I can’t get subprocess to enter my subdirectory:

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 throws OSError and I don’t know why. It doesn’t matter whether I try to go into an existing subdir or go one directory up (as above) – I always end up with the same error.


回答 0

您的代码尝试执行的操作是调用名为的程序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="/")

What your code tries to do is call a program named cd ... What you want is call a command named cd.

But cd is a shell internal. So you can only call it as

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

But it is pointless to do so. As no process can change another process’s working directory (again, at least on a UNIX-like OS, but as well on Windows), this call will have the subshell change its dir and exit immediately.

What you want can be achieved with os.chdir() or with the subprocess named parameter cwd which changes the working directory immediately before executing a subprocess.

For example, to execute ls in the root directory, you either can do

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

or simply

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

回答 1

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

import subprocess

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

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

To run your_command as a subprocess in a different directory, pass cwd parameter, as suggested in @wim’s answer:

import subprocess

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

A child process can’t change its parent’s working directory (normally). Running cd .. in a child shell process using subprocess won’t change your parent Python script’s working directory i.e., the code example in @glglgl’s answer is wrong. cd is a shell builtin (not a separate executable), it can change the directory only in the same process.


回答 2

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

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

You want to use an absolute path to the executable, and use the cwd kwarg of Popen to set the working directory. See the docs.

If cwd is not None, the child’s current directory will be changed to cwd before it is executed. Note that this directory is not considered when searching the executable, so you can’t specify the program’s path relative to cwd.


回答 3

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

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

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

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

查看docs subprocess.popen-constructor

subprocess.call and other methods in the subprocess module have a cwd parameter.

This parameter determines the working directory where you want to execute your process.

So you can do something like this:

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

Check out docs subprocess.popen-constructor


回答 4

基于此答案的另一种选择: 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'))

Another option based on this answer: https://stackoverflow.com/a/29269316/451710

This allows you to execute multiple commands (e.g cd) in the same process.

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'))

回答 5

我想这几天你会做:

import subprocess

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

I guess these days you would do:

import subprocess

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

回答 6

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

If you want to have cd functionality (assuming shell=True) and still want to change the directory in terms of the Python script, this code will allow ‘cd’ commands to work.

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

回答 7

如果需要更改目录,请运行命令并获取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

If you need to change directory, run a command and get the std output as well:

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