Äquivalent zu Bash Backticks in Python [Duplikat]

83

Was entspricht den Backticks in Ruby und Perl in Python? Das heißt, in Ruby kann ich Folgendes tun:

foo = `cat /tmp/baz`

Wie sieht die entsprechende Anweisung in Python aus? Ich habe es versucht, os.system("cat /tmp/baz")aber das setzt das Ergebnis auf Standard und gibt mir den Fehlercode dieser Operation zurück.

Chris Bunch
quelle

Antworten:

99
output = os.popen('cat /tmp/baz').read()
John Kugelman
quelle
4
@mckenzm Bei der Frage geht es darum, die Ausgabe eines externen Prozesses zu erfassen. Das Erfassen der Ausgabe einer Python-Funktion wäre eine ganz andere Frage.
John Kugelman
1
Wunderbar prägnant und in der Tat das Äquivalent zu Rubys `...`(Erfassen von stdout, Durchlaufen von stderr) - mit einer Ausnahme: Ruby ermöglicht es, den Exit-Code des Prozesses nachträglich zu bestimmen $?. In Python müssen Sie, soweit ich das beurteilen kann, subprocessdie Funktionen des Moduls dafür verwenden.
mklement0
82

Am flexibelsten ist die Verwendung des subprocessModuls:

import subprocess

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

capture_outputwurde in Python 3.7 eingeführt, für ältere Versionen check_output()kann stattdessen die Sonderfunktion verwendet werden:

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

Sie können ein Unterprozessobjekt auch manuell erstellen, wenn Sie eine fein abgestimmte Steuerung benötigen:

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

Alle diese Funktionen unterstützen Schlüsselwortparameter, um anzupassen, wie genau der Unterprozess ausgeführt wird. Sie können shell=Truedas Programm beispielsweise über die Shell ausführen, wenn Sie beispielsweise Dateinamenerweiterungen von benötigen *, dies ist jedoch mit Einschränkungen verbunden .

etw
quelle
2
Ja, dies ist der einzig vernünftige Weg, Sie könnten es in eine Funktion
einbinden,
Dies funktioniert bei mir eigentlich nicht, da in diesem Fall baz ein Verzeichnis ist und ich versuche, den Inhalt aller Dateien in diesem Verzeichnis abzurufen. (Cat / tmp / baz / * funktioniert in Zecken, aber nicht über die hier beschriebene Methode)
Chris Bunch
6
re: "*" funktioniert nicht; Verwenden Sie stattdessen subprocess.Popen (["cat", "/ tmp / baz"], stdout = subprocess.PIPE, shell = True). Da die Glob (Star) -Erweiterung von der Shell ausgeführt wird, muss das Unterverarbeitungsmodul in diesem Fall die Shell-Erweiterung verwenden (bereitgestellt von / bin / sh).
Pasi Savolainen
1
Von docs.python.org/2/library/subprocess.html#popen-constructor : "(mit shell = True) Wenn args eine Sequenz ist, gibt das erste Element die Befehlszeichenfolge an, und alle zusätzlichen Elemente werden als zusätzliche Argumente behandelt zur Schale selbst. " Wenn Sie also shell = True verwenden, sollte das erste Argument wahrscheinlich die Zeichenfolge "cat / tmp / baz" sein. Wenn Sie alternativ eine Sequenz als erstes
Argument verwenden
1
@gerrit: es ist nicht veraltet. In den Dokumenten wird empfohlen subprocess.run() (ich weiß nicht, ob dies verdient ist), wenn Sie frühere Versionen nicht unterstützen müssen oder wenn Sie die von bereitgestellte Flexibilität nicht benötigen Popen().
JFS
27

etw ist richtig . Sie können auch os.popen () verwenden, aber wo verfügbar (Python 2.4+) ist der Unterprozess im Allgemeinen vorzuziehen.

Im Gegensatz zu einigen Sprachen, die dies fördern, wird es im Allgemeinen als schlechte Form angesehen, einen Unterprozess zu erzeugen, in dem Sie denselben Job innerhalb der Sprache ausführen können. Es ist langsamer, weniger zuverlässig und plattformabhängig. Ihr Beispiel wäre besser dran als:

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

eta:

baz ist ein Verzeichnis und ich versuche, den Inhalt aller Dateien in diesem Verzeichnis abzurufen

? Katze in einem Verzeichnis bekommt mir einen Fehler.

Wenn Sie eine Liste von Dateien wünschen:

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

Wenn Sie den Inhalt aller Dateien in einem Verzeichnis haben möchten, gehen Sie wie folgt vor:

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)

Wenn Sie sicher sein können, dass dort keine Verzeichnisse vorhanden sind, können Sie es in einen Einzeiler einfügen:

path= '/tmp/baz'
foo= ''.join(open(os.path.join(path, child), 'rb').read() for child in os.listdir(path))
Bobince
quelle
1
Obwohl dies keine Antwort auf die Frage war, ist es die beste Antwort, um Benutzer zu schulen.
Noamtm
1
Der Fragentitel lautet "Was entspricht Backticks?". Ich nahm an, dass "Katze" nur ein Beispielbefehl war. Diese Antwort hilft im allgemeinen Fall nicht.
Jason
15
foo = subprocess.check_output(["cat", "/tmp/baz"])
jfs
quelle
3
Dies ist jetzt der einfachste Weg. "subprocess.check_output" wurde in Python 2.7 hinzugefügt, das im Juli 2010 veröffentlicht wurde, nachdem die anderen "popen" -Antworten gegeben wurden.
Robert Fleming
10

Ab Python 3.5 wird die Verwendung empfohlen subprocess.run. Um das gleiche Verhalten wie beschrieben zu erzielen, würden Sie Folgendes verwenden:

output = subprocess.run("ls", shell=True, stdout=subprocess.PIPE).stdout

Dies gibt ein bytesObjekt zurück. Vielleicht möchten Sie anhängen .decode("ascii")oder .decode("utf-8")bis zum Ende, um eine zu erhalten str.

gerrit
quelle
6

Am einfachsten ist es, das Befehlspaket zu verwenden.

import commands

commands.getoutput("whoami")

Ausgabe:

"Bganesan"

Balachander Ganesan
quelle
6
Sehr einfach, aber das Modul ist jetzt veraltet.
Gringo Suave
3
import os
foo = os.popen('cat /tmp/baz', 'r').read()
awatts
quelle
3
Dies entspricht den Backticks von Ruby. Wenn Sie jedoch den Inhalt eines Verzeichnisses auflisten möchten, ist dies nicht der beste Weg, dies zu tun.
Awatts
2

Ich benutze

(6: 0) $ python --version Python 2.7.1

Eines der obigen Beispiele ist:

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

Für mich war dies kein Zugriff auf das Verzeichnis / tmp. Nachdem ich mir die Dokumentzeichenfolge für den Unterprozess angesehen hatte, ersetzte ich sie

["prog", "arg"]

mit

"prog arg"

und bekam das gewünschte Shell-Expansionsverhalten (a la Perls `prog arg`)

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


Ich habe vor einiger Zeit aufgehört, Python zu verwenden, weil ich mich über die Schwierigkeit geärgert habe, das Äquivalent von Perl `cmd ...` zu machen. Ich bin froh, dass Python dies vernünftig gemacht hat.

funkyj
quelle
1

Wenn Sie subprocess.Popen verwenden, denken Sie daran, bufsize anzugeben. Der Standardwert ist 0, was "ungepuffert" bedeutet, nicht "Wählen Sie einen angemessenen Standardwert".

George
quelle
1

Dies funktioniert in Python3 nicht, aber in Python2 können Sie strmit einer benutzerdefinierten __repr__Methode erweitern, die Ihren Shell-Befehl aufruft und wie folgt zurückgibt:

#!/usr/bin/env python

import os

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

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

Was du gerne benutzen kannst

#!/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`
ThorSummoner
quelle
3
Auch sollten Sie dies wahrscheinlich nicht tun *
ThorSummoner
0

repr()

Der backtickOperator (`) wurde entfernt in Python 3. Es ähnelt verwirrend einem einfachen Anführungszeichen und ist auf einigen Tastaturen schwer zu tippen. backtickVerwenden Sie anstelle von die entsprechende integrierte Funktion repr().

CONvid19
quelle