กระบวนการย่อยเปลี่ยนไดเร็กทอรี


98

ฉันต้องการเรียกใช้สคริปต์ภายในไดเร็กทอรีย่อย / superdirectory (ฉันต้องอยู่ใน sub / super-directory ก่อน) ฉันไม่สามารถ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 และฉันไม่รู้ว่าทำไม ไม่สำคัญว่าฉันจะพยายามเข้าไปใน subdir ที่มีอยู่หรือไปที่ไดเร็กทอรีเดียว (ตามด้านบน) - ฉันมักจะพบข้อผิดพลาดเดียวกัน


1
จะเกิดอะไรขึ้นถ้าใช้os.chdir()แทน
greole

คำตอบ:


152

cd ..สิ่งที่รหัสของคุณพยายามที่จะทำคือการเรียกโปรแกรมชื่อ cdสิ่งที่คุณต้องการคือการเรียกคำสั่งที่มีชื่อว่า

แต่cdเป็นเปลือกภายใน. ดังนั้นคุณสามารถเรียกมันว่า

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

แต่มันไม่มีจุดหมายที่จะทำเช่นนั้น เนื่องจากไม่มีกระบวนการใดสามารถเปลี่ยนไดเร็กทอรีการทำงานของกระบวนการอื่นได้ (อย่างน้อยก็ในระบบปฏิบัติการที่เหมือน UNIX แต่ก็เช่นกันใน Windows) การเรียกนี้จะให้ subshell เปลี่ยน dir และออกทันที

สิ่งที่คุณต้องการสามารถทำได้โดยos.chdir()ใช้subprocessพารามิเตอร์cwdที่มีชื่อซึ่งจะเปลี่ยนไดเร็กทอรีการทำงานทันทีก่อนที่จะเรียกใช้กระบวนการย่อย

ตัวอย่างเช่นในการดำเนินการlsในไดเรกทอรีรากคุณสามารถทำได้

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

หรือเพียงแค่

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

1
cdมักจะมีอยู่ในรูปแบบไบนารีไม่เพียง แต่เชลล์ในตัว ปัญหาที่แท้จริงของ OP คือเขาเรียกไบนารีcd ..ใช่ (และย่อหน้าที่สามของคุณจะเป็นปัญหาต่อไปของเขาคำตอบที่ดี)
Leon Weber

@LeonWeber ควรcdจะทำงานเป็นไบนารีได้อย่างไร? มันไม่สามารถสวดมนต์ทำงานของพ่อแม่ได้
glglgl

2
ฉันกำลังพูดถึงลินุกซ์ จุดที่ดีแม้ว่า ฉันสงสัยตัวเองและนี่คือคำตอบ: /usr/bin/cdประกอบด้วยbuiltin cd "$@"- ดังนั้นมันก็เรียกเชลล์ในตัวcdเช่นกัน
Leon Weber

1
@The_Diver นั่นคือเหตุผลที่cdต้องใช้เป็นคำสั่งเชลล์ภายใน ไม่มีวิธีอื่นที่จะทำได้ คำสั่งเชลล์ภายในถูกดำเนินการภายในกระบวนการเดียวกับเชลล์ สิ่งที่ฉันหมายโดย subshell shell=Trueเป็นเปลือกประหารชีวิต ได้รับคำสั่งให้ดำเนินการดำเนินการและออก
glglgl

1
ฉันคิดว่าตัวอย่างหรือสองวิธีที่คุณแนะนำจะเป็นประโยชน์
sscirrus

57

ในการรันyour_commandเป็นกระบวนการย่อยในไดเร็กทอรีอื่นให้ส่งผ่านcwdพารามิเตอร์ตามที่แนะนำในคำตอบของ @ wim :

import subprocess

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

กระบวนการลูกไม่สามารถเปลี่ยนไดเร็กทอรีการทำงานของพาเรนต์ได้ ( โดยปกติ ) ทำงานcd ..ในกระบวนการที่เปลือกเด็กโดยใช้กระบวนการย่อยจะไม่เปลี่ยนไดเรกทอรีการทำงานผู้ปกครองของคุณหลามสคริปต์เช่นตัวอย่างรหัสในคำตอบ @ glglgl คือผิด cdเป็นเชลล์ในตัว (ไม่ใช่ปฏิบัติการแยกต่างหาก) สามารถเปลี่ยนไดเร็กทอรีได้เฉพาะในกระบวนการเดียวกัน


24

คุณต้องการใช้พา ธ สัมบูรณ์ไปยังไฟล์ปฏิบัติการและใช้cwdkwarg ของPopenเพื่อตั้งค่าไดเร็กทอรีการทำงาน ดูเอกสาร

ถ้า cwd ไม่ใช่ไม่มีไดเร็กทอรีปัจจุบันของเด็กจะเปลี่ยนเป็น cwd ก่อนที่จะดำเนินการ โปรดทราบว่าไดเร็กทอรีนี้ไม่ได้รับการพิจารณาเมื่อค้นหาไฟล์ปฏิบัติการดังนั้นคุณจึงไม่สามารถระบุพา ธ ของโปรแกรมที่สัมพันธ์กับ cwd ได้


ขึ้นอยู่กับว่าควรดำเนินการกระบวนการย่อยอื่นหรือไม่ ถ้าเป็นเช่นนั้นวิธีของคุณคือทางที่ถูกต้อง แต่สำหรับการมีโปรแกรมของตัวเองเท่านั้นที่ทำหน้าที่ในไดเร็กทอรีอื่นนั้นจะไม่ช่วย
glglgl

หมายความว่ายังไงก็ไม่ช่วย? นี่เป็นวิธีหนึ่งที่ชัดเจนที่จะทำ
Wim

1
ไม่เพราะมันเพิ่งเปลี่ยน cwd ของกระบวนการที่ฉันกำลังจะเปิดตัวเช่นsubprocess.call(['ls', '-l'], cwd='/'). สิ่งนี้เปลี่ยน cwd เป็น/และรันlsด้วย-lเป็นอาร์กิวเมนต์ แต่ถ้าฉันต้องการทำos.chdir('/')แล้วopen('etc/fstab', 'r')ฉันไม่สามารถแทนที่os.chdir()ด้วยอะไรก็ได้subprocess.XXX(cwd='/')เพราะมันจะไม่ช่วยอย่างที่กล่าวไว้ นี่คือสองสถานการณ์ที่แตกต่างกันโดยสมบูรณ์
glglgl

นั่นเป็นเหตุผลที่คำตอบของฉันบอกว่าให้ใช้เส้นทางสัมบูรณ์ไปยังไฟล์ปฏิบัติการคุณพลาดส่วนนั้นหรือไม่?
Wim

2
ไม่ฉันไม่ได้ ฉันคิดว่าฉันยอมแพ้ หากฉันต้องการเปลี่ยนไดเร็กทอรีการทำงานปัจจุบันและเปิดไฟล์ฉันไม่มีไฟล์ปฏิบัติการ มันเป็นสถานการณ์ที่แตกต่างไปจากเดิมอย่างสิ้นเชิง BTW: ไม่จำเป็นต้องใช้เส้นทางที่แน่นอนถ้าฉันใช้cwd=ตามที่ตั้งใจไว้ subprocess.call(['bin/ls', '-l'], cwd='/')ฉันเป็นอย่างดีสามารถทำ
glglgl

18

subprocess.callและวิธีการอื่น ๆ ในsubprocessโมดูลมี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'
Tripleee


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ไม่ดี
tripleee
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.