Subproceso de cambio de directorio


98

Quiero ejecutar un script dentro de un subdirectorio / superdirectorio (primero necesito estar dentro de este sub / superdirectorio). No puedo subprocessingresar a mi subdirectorio:

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 arroja OSError y no sé por qué. No importa si trato de ir a un subdirectorio existente o subir un directorio (como arriba); siempre termino con el mismo error.


1
¿Qué sucede si se usa os.chdir()en su lugar.
greole

Respuestas:


152

Lo que su código intenta hacer es llamar a un programa llamado cd ... Lo que quieres es llamar a un comando llamado cd.

Pero cdes un caparazón interno. Entonces solo puedes llamarlo como

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

Pero no tiene sentido hacerlo. Como ningún proceso puede cambiar el directorio de trabajo de otro proceso (de nuevo, al menos en un sistema operativo similar a UNIX, pero también en Windows), esta llamada hará que la subshell cambie su directorio y salga inmediatamente.

Lo que desea se puede lograr con os.chdir()o con el subprocessparámetro nombrado cwdque cambia el directorio de trabajo inmediatamente antes de ejecutar un subproceso.

Por ejemplo, para ejecutar lsen el directorio raíz, puede hacer

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

o simplemente

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

1
cdgeneralmente también existe como un binario, no solo como un shell integrado. El verdadero problema del OP era que estaba llamando a un binario cd .., sí. (Y su tercer párrafo habría sido su próximo problema, así que buena respuesta.)
Leon Weber

@LeonWeber ¿Cómo debería cdpoder funcionar como binario? No puede cantar el directorio de trabajo de su padre.
glglgl

2
Estaba hablando de Linux. Aunque es un buen punto. Me preguntaba a mí mismo, y aquí está la respuesta: /usr/bin/cdconsiste en builtin cd "$@", por lo que también llama al shell incorporado cd.
Leon Weber

1
@The_Diver Es por eso que cddebe implementarse como comando de shell interno. No hay otra forma de hacerlo. Los comandos de shell internos se ejecutan dentro del mismo proceso que el shell. Lo que quise decir con subshell es el shell ejecutado shell=True. Obtiene el comando que se ejecutará, lo ejecuta y sale.
glglgl

1
Creo que sería útil un ejemplo o dos de su enfoque sugerido.
sscirrus

57

Para ejecutar your_commandcomo un subproceso en un directorio diferente, pase el cwdparámetro, como se sugiere en la respuesta de @ wim :

import subprocess

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

Un proceso hijo no puede cambiar el directorio de trabajo de su padre ( normalmente ). La ejecución cd ..en un proceso de shell secundario usando un subproceso no cambiará el directorio de trabajo de la secuencia de comandos Python principal, es decir, el ejemplo de código en la respuesta de @ glglgl es incorrecto . cdes un shell incorporado (no un ejecutable separado), puede cambiar el directorio solo en el mismo proceso.


24

Desea utilizar una ruta absoluta al ejecutable y utilizar el cwdkwarg de Popenpara establecer el directorio de trabajo. Consulte los documentos .

Si cwd no es None, el directorio actual del niño se cambiará a cwd antes de que se ejecute. Tenga en cuenta que este directorio no se considera al buscar el ejecutable, por lo que no puede especificar la ruta del programa relativa a cwd.


Depende de si se supone que se debe ejecutar otro subproceso. Si es así, tu camino es el correcto. Pero solo por tener el propio programa actuando dentro de un directorio diferente, eso no ayudará.
glglgl

¿Qué quieres decir con que no ayudará? Esta es la única forma obvia de hacerlo.
wim

1
No, ya que solo cambia la cwd del proceso que voy a iniciar, como subprocess.call(['ls', '-l'], cwd='/'). Esto cambia el cwd ay /luego se ejecuta lscon -lcomo argumento. Pero si quiero hacer os.chdir('/')y luego open('etc/fstab', 'r'), no puedo reemplazar os.chdir()con nada, subprocess.XXX(cwd='/')ya que no ayudará, como se dijo. Estos son dos escenarios completamente diferentes.
glglgl

Es por eso que mi respuesta dice que use una ruta absoluta al ejecutable, ¿se perdió esa parte?
wim

2
No, no lo hice. Creo que me rindo. Si quiero cambiar el directorio de trabajo actual y abrir un archivo, no tengo ejecutable. Es una situación completamente diferente. Por cierto: no hay necesidad de usar una ruta absoluta si la uso cwd=según lo previsto. Yo también puedo hacerlo subprocess.call(['bin/ls', '-l'], cwd='/').
glglgl

18

subprocess.cally otros métodos del subprocessmódulo tienen un cwdparámetro.

Este parámetro determina el directorio de trabajo donde desea ejecutar su proceso.

Entonces puedes hacer algo como esto:

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

Consulte los documentos subprocess.popen-constructor


7

Otra opción basada en esta respuesta: https://stackoverflow.com/a/29269316/451710

Esto le permite ejecutar varios comandos (por ejemplo cd) en el mismo proceso.

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
Esta es solo una manera indirecta e ineficiente de hacerloshell=True, executable='/bin/bash'
tripleee

3

Supongo que en estos días harías:

import subprocess

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

0

Si desea tener la funcionalidad de cd (asumiendo shell = True) y aún desea cambiar el directorio en términos del script de Python, este código permitirá que funcionen los comandos '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

Si necesita cambiar de directorio, ejecute un comando y obtenga también la salida estándar:

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

Te estás reinventando check_call, mal.
tripleee
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.