Каталог, що змінює підпроцес


98

Я хочу виконати сценарій всередині підкаталогу / супердиректорії (мені спочатку потрібно бути всередині цього підкаталогу / суперкаталогу). Я не можу 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, і я не знаю чому. Не має значення, намагаюся я перейти до існуючого піддиректорії або перейти на один каталог вгору (як вище) - у мене завжди трапляється одна і та ж помилка.


1
Що станеться, якщо використовувати os.chdir()замість цього.
greole

Відповіді:


151

Що ваш код намагається зробити, це викликати програму з іменем cd ... Що ви хочете, це викликати команду з ім'ям cd.

Але cdце оболонка внутрішня. Тож ви можете назвати це лише як

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

Але робити це безглуздо. Оскільки жоден процес не може змінити робочий каталог іншого процесу (знову ж таки, принаймні в UNIX-подібній ОС, але також і в Windows), під час цього виклику підшелушка змінить свій каталог і негайно вийде.

Те, що ви хочете, може бути досягнуто за os.chdir()допомогою subprocessназваного параметра, cwdякий змінює робочий каталог безпосередньо перед виконанням підпроцесу.

Наприклад, для виконання lsв кореневому каталозі ви можете це зробити

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

або просто

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

1
cdзазвичай також існує як двійковий файл, а не лише вбудована оболонка. Справжня проблема OP полягала в тому, що він називав двійковий файл cd .., так. (І ваш третій абзац став би його наступною проблемою, тому хороша відповідь.)
Леон Вебер,

@LeonWeber Як слід cdвміти працювати як двійковий файл? Він не може повторювати робочий каталог своїх батьків.
glglgl

2
Я говорив про Linux. Хороший момент, хоча. Я дивувався собі, і ось відповідь: /usr/bin/cdскладається з builtin cd "$@"- отже, він просто називає вбудовану оболонку cd.
Леон Вебер

1
@The_Diver Ось чому cdповинен бути реалізований як внутрішня команда оболонки. Немає іншого способу це зробити. Внутрішні команди оболонки виконуються в тому ж процесі, що і оболонка. Я мав на увазі під оболонкою оболонку, для якої виконується 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 ..у дочірньому процесі оболонки з використанням підпроцесу не змінить робочого каталогу вашого батьківського сценарію Python, тобто приклад коду у відповіді @ glglgl є неправильним . cdє вбудованою оболонкою (не окремим виконуваним файлом), вона може змінювати каталог лише в тому ж процесі.


24

Ви хочете використовувати абсолютний шлях до виконуваного файлу та використовувати cwdkwarg Popenдля встановлення робочого каталогу. Перегляньте документи .

Якщо cwd не None, поточний каталог дитини буде змінено на cwd перед тим, як його виконати. Зверніть увагу, що цей каталог не враховується при пошуку виконуваного файлу, тому ви не можете вказати шлях програми щодо cwd.


Це залежить від того, чи передбачається виконати інший підпроцес. Якщо так, то ваш шлях правильний. Але якщо у вас є лише власна програма, яка діє в іншому каталозі, це не допоможе.
glglgl

Що ви маєте на увазі, це не допоможе? Це єдиний очевидний спосіб зробити це.
wim

1
Ні, оскільки це просто змінює cwd процесу, який я збираюся запустити, наприклад subprocess.call(['ls', '-l'], cwd='/'). Це змінює вухо до , /а потім запускає lsз в -lякості аргументу. Але якщо я хочу зробити це os.chdir('/')і потім open('etc/fstab', 'r'), я не можу замінити os.chdir()нічим приблизно, subprocess.XXX(cwd='/')оскільки це не допоможе, як сказано. Це два цілком різні сценарії.
glglgl

Ось чому моя відповідь говорить, щоб використовувати абсолютний шлях до виконуваного файлу, ви пропустили цю частину?
wim

2
Ні, я не зробив. Здається, я здаюся. Якщо я хочу змінити поточний робочий каталог і відкрити файл, у мене немає виконуваного файлу. Зовсім інша ситуація. До речі: немає необхідності використовувати абсолютний шлях, якщо я використовую cwd=за призначенням. Я теж можу це зробити subprocess.call(['bin/ls', '-l'], cwd='/').
glglgl

17

subprocess.callта інші методи в subprocessмодулі мають acwd параметр.

Цей параметр визначає робочий каталог, в якому ви хочете виконати ваш процес.

Тож ви можете зробити щось подібне:

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'
триплі


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, погано.
триплеє
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.