Subproses mengubah direktori

99

Saya ingin menjalankan skrip di dalam subdirektori / superdirectory (saya harus berada di dalam sub / super-direktori ini terlebih dahulu). Saya tidak bisa subprocessmasuk ke subdirektori saya:

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 melempar OSError dan saya tidak tahu mengapa. Tidak masalah apakah saya mencoba masuk ke subdir yang ada atau naik satu direktori (seperti di atas) - saya selalu berakhir dengan kesalahan yang sama.

ducin
sumber
1
Apa yang terjadi jika digunakan os.chdir()sebagai gantinya.
greole

Jawaban:

153

Apa yang coba dilakukan kode Anda adalah memanggil program bernama cd ... Yang Anda inginkan adalah memanggil perintah bernama cd.

Tetapi cdmerupakan cangkang internal. Jadi Anda hanya bisa menyebutnya sebagai

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

Tetapi tidak ada gunanya melakukan itu. Karena tidak ada proses yang dapat mengubah direktori kerja proses lain (sekali lagi, setidaknya pada OS mirip UNIX, tetapi juga di Windows), panggilan ini akan membuat subkulit mengubah direktori dan segera keluar.

Apa yang Anda inginkan dapat dicapai dengan os.chdir()atau dengan subprocessparameter bernama cwdyang mengubah direktori kerja segera sebelum menjalankan subproses.

Misalnya, untuk mengeksekusi lsdi direktori root, Anda bisa melakukannya

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

atau sederhananya

subprocess.Popen("ls", cwd="/")
glglgl
sumber
1
cdbiasanya juga ada sebagai biner, tidak hanya shell bawaan. Masalah sebenarnya dari OP adalah dia memanggil biner cd .., ya. (Dan paragraf ketiga Anda akan menjadi masalah berikutnya, jawaban yang sangat bagus.)
Leon Weber
@LeonWeber Bagaimana seharusnya cdbisa bekerja sebagai biner? Itu tidak bisa chante direktori kerja orang tuanya.
glglgl
2
Saya berbicara tentang Linux. Poin yang bagus. Saya bertanya-tanya pada diri saya sendiri, dan inilah jawabannya: /usr/bin/cdterdiri dari builtin cd "$@"- jadi itu hanya memanggil shell built-in cdjuga.
Leon Weber
1
@The_Diver Itu sebabnya cdharus diimplementasikan sebagai perintah shell internal. Tidak ada cara lain untuk melakukannya. Perintah shell internal dijalankan dalam proses yang sama dengan shell. Yang saya maksud dengan subkulit adalah shell yang dieksekusi shell=True. Ia mendapat perintah untuk dieksekusi, mengeksekusinya dan keluar.
glglgl
1
Saya pikir satu atau dua contoh pendekatan yang Anda sarankan akan berguna.
sscirrus
58

Untuk menjalankan your_commandsebagai subproses di direktori yang berbeda, berikan cwdparameter, seperti yang disarankan dalam jawaban @ wim :

import subprocess

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

Proses anak tidak dapat mengubah direktori kerja induknya ( biasanya ). Laricd .. dalam proses cangkang anak menggunakan subproses tidak akan mengubah direktori kerja skrip Python induk Anda, misalnya, contoh kode dalam jawaban @ glglgl salah . cdadalah shell builtin (bukan executable terpisah), itu dapat mengubah direktori hanya dalam proses yang sama .

jfs
sumber
24

Anda ingin menggunakan jalur absolut ke file yang dapat dieksekusi, dan menggunakan cwdkwarg dari Popenuntuk menyetel direktori kerja. Lihat dokumennya .

Jika cwd bukan None, direktori anak saat ini akan diubah menjadi cwd sebelum dijalankan. Perhatikan bahwa direktori ini tidak dipertimbangkan saat mencari file yang dapat dieksekusi, jadi Anda tidak dapat menentukan jalur program yang berhubungan dengan cwd.

wim
sumber
Itu tergantung pada apakah subproses lain seharusnya dijalankan. Jika demikian, cara Anda adalah yang benar. Tetapi hanya memiliki program sendiri yang bertindak di dalam direktori yang berbeda, itu tidak akan membantu.
glglgl
Apa maksudmu itu tidak akan membantu? Ini adalah cara yang jelas untuk melakukannya.
wim
1
Tidak, karena ini hanya mengubah cwd dari proses yang akan saya luncurkan, misalnya subprocess.call(['ls', '-l'], cwd='/'). Ini mengubah cwd menjadi /dan kemudian dijalankan lsdengan -lsebagai argumen. Tetapi jika saya ingin melakukan os.chdir('/')dan kemudian open('etc/fstab', 'r'), saya tidak dapat mengganti os.chdir()dengan apa pun subprocess.XXX(cwd='/')karena tidak akan membantu, seperti yang dikatakan. Ini adalah dua skenario berbeda yang lengkap.
glglgl
Itulah mengapa jawaban saya mengatakan untuk menggunakan jalur absolut ke yang dapat dieksekusi, apakah Anda melewatkan bagian itu?
wim
2
Tidak, saya tidak melakukannya. Saya pikir saya menyerah. Jika saya ingin mengubah direktori kerja saat ini dan membuka file, saya tidak memiliki file yang dapat dieksekusi. Ini adalah situasi yang sangat berbeda. BTW: Tidak perlu menggunakan jalur absolut jika saya gunakan cwd=sesuai keinginan. Saya juga bisa melakukannya subprocess.call(['bin/ls', '-l'], cwd='/').
glglgl
18

subprocess.calldan metode lain dalam subprocessmodul memiliki acwd parameter.

Parameter ini menentukan direktori kerja tempat Anda ingin menjalankan proses Anda.

Jadi Anda bisa melakukan sesuatu seperti ini:

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

Lihat docs subprocess.popen-constructor

l__flex__l
sumber
7

Opsi lain berdasarkan jawaban ini: https://stackoverflow.com/a/29269316/451710

Ini memungkinkan Anda untuk menjalankan beberapa perintah (misalnya cd) dalam proses yang sama.

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'))
Eyal Levin
sumber
1
Ini hanyalah cara memutar dan tidak efisien untuk melakukannyashell=True, executable='/bin/bash'
tripleee
3

Saya kira hari ini Anda akan melakukan:

import subprocess

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

Jika Anda ingin memiliki fungsionalitas cd (dengan asumsi shell = True) dan masih ingin mengubah direktori dengan skrip Python, kode ini akan memungkinkan perintah 'cd' untuk bekerja.

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
Tux taktis
sumber
-1

Jika Anda perlu mengubah direktori, jalankan perintah dan dapatkan juga output 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<[email protected]>\nDate:   2020-04-23 09:58:49 +0200\n\n
jturi
sumber
Anda menciptakan kembali check_call, dengan buruk.
tripleee