Thư mục thay đổi quy trình con


98

Tôi muốn thực thi một tập lệnh bên trong thư mục con / siêu thư mục (trước tiên tôi cần phải ở bên trong thư mục con / siêu thư mục này). Tôi không thể subprocessvào thư mục con của mình:

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 ném OSError và tôi không biết tại sao. Không quan trọng cho dù tôi cố gắng truy cập vào một thư mục con hiện có hay truy cập một thư mục (như trên) - tôi luôn gặp phải cùng một lỗi.


1
Điều gì xảy ra nếu sử dụng os.chdir()thay thế.
greole

Câu trả lời:


151

Những gì mã của bạn cố gắng làm là gọi một chương trình có tên cd ... Những gì bạn muốn là gọi một lệnh có tên cd.

Nhưng cdlà một lớp vỏ bên trong. Vì vậy, bạn chỉ có thể gọi nó là

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

Nhưng làm như vậy là vô nghĩa. Vì không có quy trình nào có thể thay đổi thư mục làm việc của quy trình khác (một lần nữa, ít nhất là trên hệ điều hành giống UNIX, nhưng cũng như trên Windows), lệnh gọi này sẽ khiến vỏ con thay đổi dir và thoát ngay lập tức.

Những gì bạn muốn có thể đạt được với os.chdir()hoặc với subprocesstham số được đặt tên cwdthay đổi thư mục làm việc ngay lập tức trước khi thực hiện một quy trình con.

Ví dụ: để thực thi lstrong thư mục gốc, bạn có thể làm

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

hoặc đơn giản

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

1
cdthường cũng tồn tại dưới dạng nhị phân, không chỉ là một trình bao được tích hợp sẵn. Vấn đề thực sự của OP là anh ta đang gọi một mã nhị phân cd .., vâng. (Và đoạn thứ ba của bạn có thể đã được vấn đề tiếp theo của mình, vì vậy câu trả lời tốt.)
Leon Weber

@LeonWeber Làm thế nào để cdcó thể hoạt động như một tệp nhị phân? Nó không thể tụng kinh dir làm việc của cha mẹ nó.
glglgl

2
Tôi đã nói về Linux. Điểm tốt mặc dù. Tôi đã tự hỏi bản thân mình, và đây là câu trả lời: /usr/bin/cdbao gồm builtin cd "$@"- vì vậy nó chỉ gọi trình bao được tích hợp sẵn cd.
Leon Weber

1
@The_Diver Đó là lý do tại sao cdphải được triển khai dưới dạng lệnh shell bên trong. Không có cách nào khác để làm điều đó. Các lệnh bên trong shell được thực hiện trong cùng một quá trình với shell. Ý tôi muốn nói về subshell là shell được thực thi shell=True. Nó nhận lệnh được thực thi, thực hiện lệnh đó và thoát.
glglgl,

1
Tôi nghĩ một hoặc hai ví dụ về cách tiếp cận được đề xuất của bạn sẽ hữu ích.
sscirrus

57

Để chạy your_commanddưới dạng một quy trình con trong một thư mục khác, hãy chuyển cwdtham số, như được đề xuất trong câu trả lời của @ wim :

import subprocess

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

Process con không thể thay đổi thư mục làm việc của cha của nó ( bình thường ). Chạy cd ..trong một quy trình shell con sử dụng quy trình con sẽ không thay đổi thư mục làm việc của tập lệnh Python cha của bạn, tức là ví dụ mã trong câu trả lời của @ glglgl là sai . cdlà một nội trang của trình bao (không phải là tệp thực thi riêng biệt), nó chỉ có thể thay đổi thư mục trong cùng một quy trình.


24

Bạn muốn sử dụng một đường dẫn tuyệt đối đến tệp thực thi và sử dụng cwdkwarg of Popenđể đặt thư mục làm việc. Xem tài liệu .

Nếu cwd không phải là Không, thư mục hiện tại của đứa trẻ sẽ được thay đổi thành cwd trước khi nó được thực thi. Lưu ý rằng thư mục này không được xem xét khi tìm kiếm tệp thực thi, vì vậy bạn không thể chỉ định đường dẫn của chương trình liên quan đến cwd.


Nó phụ thuộc vào việc liệu một quy trình con khác có được thực thi hay không. Nếu vậy, cách của bạn là đúng. Nhưng đối với việc chỉ có chương trình riêng hoạt động bên trong một thư mục khác, điều đó sẽ không hữu ích.
glglgl

Ý bạn là nó sẽ không giúp được gì? Đây là một cách rõ ràng để làm điều đó.
wim

1
Không, vì nó chỉ thay đổi cwd của quá trình tôi sẽ khởi chạy, chẳng hạn như subprocess.call(['ls', '-l'], cwd='/'). Điều này thay đổi cwd thành /và sau đó chạy lsvới -lđối số là. Nhưng nếu tôi muốn làm os.chdir('/')và sau đó open('etc/fstab', 'r'), tôi không thể thay thế os.chdir()bằng bất cứ điều gì subprocess.XXX(cwd='/')vì nó sẽ không giúp ích, như đã nói. Đây là hai kịch bản hoàn toàn khác nhau.
glglgl

Đó là lý do tại sao câu trả lời của tôi nói rằng hãy sử dụng một đường dẫn tuyệt đối đến tệp thực thi, bạn có bỏ lỡ phần đó không?
wim

2
Không, tôi không có. Tôi nghĩ rằng tôi từ bỏ. Nếu tôi muốn thay đổi thư mục làm việc hiện tại và mở một tệp, tôi không có tệp thực thi. Đó là một tình huống hoàn toàn khác. BTW: Không cần phải sử dụng một đường dẫn tuyệt đối nếu tôi sử dụng cwd=như dự định. Tôi cũng có thể làm subprocess.call(['bin/ls', '-l'], cwd='/').
glglgl

17

subprocess.callvà các phương thức khác trong subprocessmô-đun có một cwdtham số.

Tham số này xác định thư mục làm việc nơi bạn muốn thực hiện quy trình của mình.

Vì vậy, bạn có thể làm điều gì đó như sau:

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

Kiểm tra docs subprocess.popen-constructor


7

Một tùy chọn khác dựa trên câu trả lời này: https://stackoverflow.com/a/29269316/451710

Điều này cho phép bạn thực hiện nhiều lệnh (ví dụ cd) trong cùng một quá trình.

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
Đây chỉ là một cách làm vòng vo và không hiệu quảshell=True, executable='/bin/bash'
tripleee

3

Tôi đoán những ngày này bạn sẽ làm:

import subprocess

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

0

Nếu bạn muốn có chức năng cd (giả sử shell = True) và vẫn muốn thay đổi thư mục theo tập lệnh Python, mã này sẽ cho phép các lệnh 'cd' hoạt động.

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

Nếu bạn cần thay đổi thư mục, hãy chạy một lệnh và nhận đầu ra 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

Bạn đang sáng tạo lại check_call, thật tội nghiệp.
tripleee
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.