Katalog zmiany podprocesu

106

Chcę wykonać skrypt w podkatalogu / superkatalogu (najpierw muszę być w tym podkatalogu / superkatalogu). Nie mogę wejść subprocessdo mojego podkatalogu:

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 wyrzuca OSError i nie wiem dlaczego. Nie ma znaczenia, czy próbuję wejść do istniejącego podkatalogu, czy przejść o jeden katalog wyżej (jak wyżej) - zawsze kończy się ten sam błąd.

ducin
źródło
1
Co się stanie, jeśli os.chdir()zamiast tego użyjesz .
greole

Odpowiedzi:

160

To, co próbuje zrobić twój kod, to wywołać program o nazwie cd ... To, czego chcesz, to wywołanie polecenia o nazwie cd.

Ale cdjest skorupa wewnętrzna. Możesz więc nazwać to tylko jako

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

Ale to nie ma sensu. Ponieważ żaden proces nie może zmienić katalogu roboczego innego procesu (ponownie, przynajmniej w systemie podobnym do systemu UNIX, ale także w systemie Windows), to wywołanie spowoduje, że podpowłoka zmieni swój katalog i natychmiast zakończy pracę.

To, co chcesz, można osiągnąć za os.chdir()pomocą subprocessnazwanego parametru, cwdktóry zmienia katalog roboczy bezpośrednio przed wykonaniem podprocesu.

Na przykład, aby wykonać lsw katalogu głównym, możesz to zrobić

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

lub po prostu

subprocess.Popen("ls", cwd="/")
glglgl
źródło
1
cdzwykle istnieje również jako plik binarny, a nie tylko jako wbudowana powłoka. Prawdziwym problemem OP było to, że dzwonił do binarnego cd .., tak. (A twój trzeci akapit byłby jego następnym problemem, więc dobra odpowiedź.)
Leon Weber
@LeonWeber W jaki sposób powinien cddziałać jako plik binarny? Nie może śpiewać pracy rodzica w reż.
glglgl
2
Mówiłem o Linuksie. Słuszna uwaga. Zastanawiałem się sam, a oto odpowiedź: /usr/bin/cdskłada się z builtin cd "$@"- więc po prostu wywołuje również wbudowaną powłokę cd.
Leon Weber
1
@The_Diver Dlatego cdmusi być zaimplementowane jako wewnętrzne polecenie powłoki. Nie ma innego sposobu, aby to zrobić. Polecenia powłoki wewnętrznej są wykonywane w ramach tego samego procesu co powłoka. Przez podpowłokę rozumiałem powłokę, dla której wykonuje się shell=True. Pobiera polecenie do wykonania, wykonuje je i wychodzi.
glglgl
1
Myślę, że jeden lub dwa przykłady twojego sugerowanego podejścia byłyby przydatne.
sscirrus
59

Aby uruchomić your_commandjako podproces w innym katalogu, przekaż cwdparametr, zgodnie z sugestią w odpowiedzi @ wim :

import subprocess

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

Proces potomny nie może ( normalnie ) zmienić katalogu roboczego swojego rodzica . Uruchomienie cd ..w procesie powłoki potomnej przy użyciu podprocesu nie zmieni katalogu roboczego twojego nadrzędnego skryptu Python, tj. Przykładowy kod w odpowiedzi @ glglgl jest nieprawidłowy . cdjest wbudowaną powłoką (nie jest oddzielnym plikiem wykonywalnym), może zmienić katalog tylko w tym samym procesie.

jfs
źródło
24

Chcesz użyć bezwzględnej ścieżki do pliku wykonywalnego i użyć cwdkwarg of, Popenaby ustawić katalog roboczy. Zobacz dokumentację .

Jeśli cwd nie ma wartości None, bieżący katalog dziecka zostanie zmieniony na cwd przed wykonaniem. Zauważ, że ten katalog nie jest brany pod uwagę podczas wyszukiwania pliku wykonywalnego, więc nie możesz określić ścieżki programu względem cwd.

wim
źródło
Zależy to od tego, czy ma zostać wykonany inny podproces. Jeśli tak, to Twoja droga jest właściwa. Jednak posiadanie własnego programu działającego w innym katalogu nie pomoże.
glglgl
Co masz na myśli, mówiąc, że to nie pomoże? To jedyny oczywisty sposób, aby to zrobić.
wim
1
Nie, ponieważ zmienia tylko cwd procesu, który mam zamiar uruchomić, na przykład subprocess.call(['ls', '-l'], cwd='/'). To zmienia cwd na, /a następnie uruchamia się lsz -largumentem. Ale jeśli chcę zrobić os.chdir('/'), a następnie open('etc/fstab', 'r'), nie mogę wymienić os.chdir()z niczego na temat subprocess.XXX(cwd='/'), jak to nie pomoże, jak powiedział. To są dwa zupełnie różne scenariusze.
glglgl
Dlatego moja odpowiedź mówi, aby użyć bezwzględnej ścieżki do pliku wykonywalnego, czy przegapiłeś tę część?
wim
2
Nie, nie wiedziałem. Myślę, że się poddaję. Jeśli chcę zmienić bieżący katalog roboczy i otworzyć plik, nie mam pliku wykonywalnego. To zupełnie inna sytuacja. BTW: Nie ma potrzeby używania ścieżki bezwzględnej, jeśli używam cwd=zgodnie z przeznaczeniem. Równie dobrze mogę subprocess.call(['bin/ls', '-l'], cwd='/').
glglgl
21

subprocess.calla inne metody w subprocessmodule mają cwdparametr.

Ten parametr określa katalog roboczy, w którym chcesz wykonać proces.

Możesz więc zrobić coś takiego:

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

Sprawdź konstruktor subprocess.popen dokumentów

l__flex__l
źródło
7

Inna opcja oparta na tej odpowiedzi: https://stackoverflow.com/a/29269316/451710

Pozwala to na wykonywanie wielu poleceń (np. cd) W tym samym procesie.

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
źródło
1
To tylko okrężny i nieefektywny sposóbshell=True, executable='/bin/bash'
tripleee
6

Myślę, że w tych dniach zrobiłbyś:

import subprocess

subprocess.run(["pwd"], cwd="sub-dir")
Francois
źródło
0

Jeśli chcesz mieć funkcjonalność cd (zakładając, że shell = True) i nadal chcesz zmienić katalog pod względem skryptu Python, ten kod pozwoli na działanie poleceń „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
Tactical Tux
źródło
0

po prostu użyj os.chdir
przykładu:

>>> import os
>>> import subprocess
>>> # Lets Just Say WE want To List The User Folders
>>> os.chdir("/home/")
>>> subprocess.run("ls")
user1 user2 user3 user4
Harsha Addanki
źródło
-1

Jeśli chcesz zmienić katalog, uruchom polecenie i uzyskaj również standardowe wyjście:

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
źródło
check_callSłabo wymyślasz na nowo .
tripleee