Equivalente de Bash Backticks en Python [duplicado]


84

¿Cuál es el equivalente de las comillas invertidas que se encuentran en Ruby y Perl en Python? Es decir, en Ruby puedo hacer esto:

foo = `cat /tmp/baz`

¿Cómo se ve la declaración equivalente en Python? Lo intenté, os.system("cat /tmp/baz")pero eso pone el resultado en estándar y me devuelve el código de error de esa operación.


Respuestas:


100
output = os.popen('cat /tmp/baz').read()

4
@mckenzm La pregunta se trata de capturar el resultado de un proceso externo. Capturar la salida de una función de Python sería una cuestión bastante diferente.
John Kugelman

1
Maravillosamente conciso, y de hecho el equivalente de Ruby `...`(capturando stdout, pasando stderr a través) - con una excepción: Ruby permite determinar el código de salida del proceso vía $?después del hecho; en Python, por lo que puedo decir, tendrás que usar las subprocessfunciones del módulo para eso.
mklement0

82

La forma más flexible es utilizar el subprocessmódulo:

import subprocess

out = subprocess.run(["cat", "/tmp/baz"], capture_output=True)
print("program output:", out)

capture_outputse introdujo en Python 3.7, para versiones anteriores check_output()se puede usar la función especial en su lugar:

out = subprocess.check_output(["cat", "/tmp/baz"])

También puede construir manualmente un objeto de subproceso si necesita un control detallado:

proc = subprocess.Popen(["cat", "/tmp/baz"], stdout=subprocess.PIPE)
(out, err) = proc.communicate()

Todas estas funciones admiten parámetros de palabras clave para personalizar cómo se ejecuta exactamente el subproceso. Por ejemplo, puede usar shell=Truepara ejecutar el programa a través del shell, si necesita cosas como expansiones de nombres de archivos *, pero eso tiene limitaciones .


2
sí, esta es la única forma sensata, podría envolverlo en una función para poder llamar a algo como ejecutar ("comando")
Vinko Vrsalovic

En realidad, esto no funciona para mí, ya que en este caso, baz es un directorio y estoy tratando de obtener el contenido de todos los archivos en ese directorio. (hacer cat / tmp / baz / * funciona en ticks pero no a través del método descrito aquí)
Chris Bunch

6
re: "*" no funciona; use subprocess.Popen (["cat", "/ tmp / baz"], stdout = subprocess.PIPE, shell = True) en su lugar. Dado que la expansión glob (estrella) es manejada por shell, el módulo de subprocesamiento debe usar la expansión de shell en este caso (proporcionada por / bin / sh).
Pasi Savolainen

1
De docs.python.org/2/library/subprocess.html#popen-constructor : "(con shell = True) Si args es una secuencia, el primer elemento especifica la cadena de comando y cualquier elemento adicional se tratará como argumento adicional al propio caparazón ". Entonces, si va a usar shell = True, entonces el primer argumento probablemente debería ser la cadena "cat / tmp / baz". Alternativamente, si desea usar una secuencia como primer argumento, debe usar shell = False
ninguno

1
@gerrit: no está en desuso. Los documentos recomiendan subprocess.run() (no sé si se merece) si no necesita admitir versiones anteriores o si no necesita la flexibilidad proporcionada por Popen().
jfs

27

algo tiene razón . También puede usar os.popen (), pero cuando esté disponible (Python 2.4+), generalmente es preferible el subproceso.

Sin embargo, a diferencia de algunos lenguajes que lo fomentan, generalmente se considera de mala forma generar un subproceso en el que se puede hacer el mismo trabajo dentro del lenguaje. Es más lento, menos confiable y depende de la plataforma. Su ejemplo estaría mejor si:

foo= open('/tmp/baz').read()

eta:

baz es un directorio y estoy tratando de obtener el contenido de todos los archivos en ese directorio

? cat en un directorio me da un error.

Si desea una lista de archivos:

import os
foo= os.listdir('/tmp/baz')

Si desea el contenido de todos los archivos en un directorio, algo como:

contents= []
for leaf in os.listdir('/tmp/baz'):
    path= os.path.join('/tmp/baz', leaf)
    if os.path.isfile(path):
        contents.append(open(path, 'rb').read())
foo= ''.join(contents)

o, si puede estar seguro de que no hay directorios allí, puede colocarlo en una sola línea:

path= '/tmp/baz'
foo= ''.join(open(os.path.join(path, child), 'rb').read() for child in os.listdir(path))

1
Aunque esta no fue una respuesta a la pregunta, es la mejor respuesta para educar a los usuarios.
noamtm

1
El título de la pregunta es "¿cuál es el equivalente a comillas invertidas?". Supuse que "gato" era solo un comando de ejemplo. Esta respuesta no ayuda con el caso general.
Jason

15
foo = subprocess.check_output(["cat", "/tmp/baz"])

3
Esta es la forma más sencilla ahora. "subprocess.check_output" se agregó en Python 2.7, que fue lanzado en julio de 2010, después de que se dieran las otras respuestas "popen".
Robert Fleming

10

Desde Python 3.5 en adelante, la forma recomendada es usar subprocess.run. Desde Python 3.7, para obtener el mismo comportamiento que describe, usaría:

cpe = subprocess.run("ls", shell=True, capture_output=True)

Esto devolverá un subprocess.CompletedProcessobjeto. La salida a stdout estará adentro cpe.stdout, la salida a stderr estará adentro cpe.stderr, que serán ambos bytesobjetos. Puede decodificar la salida para obtener un strobjeto utilizando cpe.stdout.decode()u obtener un pasando text=Truea subprocess.run:

cpe = subprocess.run("ls", shell=True, capture_output=True, text=True)

En el último caso, cpe.stdouty cpe.stderrson ambos strobjetos.


Desde python 3.7 en adelante, puede usar el text=Trueparámetro para recuperar una cadena en lugar de bytes.
bwv549

6

La forma más sencilla es utilizar el paquete de comandos.

import commands

commands.getoutput("whoami")

Salida:

'bganesan'


6
Muy fácil, pero el módulo ahora está obsoleto.
Gringo Suave

3
import os
foo = os.popen('cat /tmp/baz', 'r').read()

3
Este es el equivalente a las comillas invertidas de Ruby, pero si su problema es enumerar el contenido de un directorio, esta no es la mejor manera de hacerlo.
awatts el

2

Estoy usando

(6: 0) $ python --versión Python 2.7.1

Uno de los ejemplos anteriores es:

import subprocess
proc = subprocess.Popen(["cat", "/tmp/baz"], stdout=subprocess.PIPE, shell=True)
(out, err) = proc.communicate()
print "program output:", out

Para mí, esto no pudo acceder al directorio / tmp. Después de mirar la cadena doc para el subproceso, reemplacé

["prog", "arg"]

con

"prog arg"

y obtuvo el comportamiento de expansión de shell que se deseaba (a la Perl's `prog arg`)

print subprocess.Popen ("ls -ld / tmp / v *", stdout = subprocess.PIPE, shell = True) .communicate () [0]


Dejé de usar Python hace un tiempo porque me molestaba la dificultad de hacer el equivalente de perl `cmd ...`. Me alegra descubrir que Python ha hecho esto razonable.


1

Si usa subprocess.Popen, recuerde especificar bufsize. El valor predeterminado es 0, que significa "sin búfer", no "elija un valor predeterminado razonable".


1

Esto no funcionará en python3, pero en python2 puede extender strcon un __repr__método personalizado que llama a su comando de shell y lo devuelve así:

#!/usr/bin/env python

import os

class Command(str):
    """Call system commands"""

    def __repr__(cmd):
        return os.popen(cmd).read()

Que puedes usar como

#!/usr/bin/env python
from command import Command

who_i_am = `Command('whoami')`

# Or predeclare your shell command strings
whoami = Command('whoami')
who_i_am = `whoami`

4
Además, probablemente no deberías hacer esto *
ThorSummoner

-1

repr()

El backtickoperador (`) se eliminó en Python 3. Es confusamente similar a una comilla simple y difícil de escribir en algunos teclados. En lugar de backtick, utilice la función incorporada equivalente repr().


Eso no es el equivalente a las comillas invertidas de bash.
gerrit
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.