Odpowiednik Bash Backticks w Pythonie [duplikat]

85

Jaki jest odpowiednik znaków grawerowanych w języku Ruby i Perl w Pythonie? Oznacza to, że w Rubim mogę to zrobić:

foo = `cat /tmp/baz`

Jak wygląda równoważna instrukcja w Pythonie? Próbowałem, os.system("cat /tmp/baz")ale to ustawia wynik na standard i zwraca mi kod błędu tej operacji.

Chris Bunch
źródło

Odpowiedzi:

101
output = os.popen('cat /tmp/baz').read()
John Kugelman
źródło
4
@mckenzm Pytanie dotyczy przechwytywania danych wyjściowych procesu zewnętrznego. Przechwytywanie danych wyjściowych funkcji Pythona to zupełnie inna kwestia.
John Kugelman,
1
Cudownie zwięzły i rzeczywiście odpowiednik Rubiego `...`(przechwytywanie wyjścia standardowego, przekazywanie błędu standardowego) - z jednym wyjątkiem: Ruby umożliwia określenie kodu wyjścia procesu przez $?po fakcie; w Pythonie, z tego, co wiem, będziesz musiał użyć do subprocesstego funkcji modułu.
mklement0
82

Najbardziej elastycznym sposobem jest użycie subprocessmodułu:

import subprocess

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

capture_outputzostał wprowadzony w Pythonie 3.7, dla starszych wersji check_output()można zamiast tego użyć funkcji specjalnej :

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

Jeśli potrzebujesz precyzyjnej kontroli, możesz również utworzyć obiekt podprocesu ręcznie:

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

Wszystkie te funkcje obsługują parametry słów kluczowych w celu dostosowania dokładnego sposobu wykonywania podprocesu. Możesz na przykład użyć shell=Truedo wykonania programu przez powłokę, jeśli potrzebujesz takich rzeczy, jak rozszerzenia nazw plików *, ale wiąże się to z ograniczeniami .

sth
źródło
2
tak, to jedyny rozsądny sposób, możesz zawinąć go w funkcję, aby wywołać coś takiego jak execute ("polecenie")
Vinko Vrsalovic
To właściwie nie działa dla mnie, ponieważ w tym przypadku baz jest katalogiem i próbuję uzyskać zawartość wszystkich plików w tym katalogu. (wykonanie cat / tmp / baz / * działa w tickach, ale nie w sposób opisany tutaj)
Chris Bunch,
6
re: „*” nie działa; użyj zamiast tego subprocess.Popen (["cat", "/ tmp / baz"], stdout = subprocess.PIPE, shell = True). Ponieważ ekspansja glob (gwiazda) jest obsługiwana przez powłokę, moduł przetwarzania podrzędnego musi w tym przypadku używać rozszerzenia powłoki (zapewnianego przez / bin / sh).
Pasi Savolainen
1
Z docs.python.org/2/library/subprocess.html#popen-constructor : „(z shell = True) Jeśli argumenty są sekwencją, pierwsza pozycja określa ciąg polecenia, a wszelkie dodatkowe elementy będą traktowane jako dodatkowe argumenty do samej skorupy. " Tak więc, jeśli zamierzasz używać shell = True, to pierwszym argumentem powinien być prawdopodobnie łańcuch „cat / tmp / baz”. Alternatywnie, jeśli chcesz użyć sekwencji jako pierwszego argumentu, powinieneś użyć shell = False
onlynone
1
@gerrit: nie jest przestarzałe. Dokumentacja zaleca subprocess.run() (nie wiem, czy na to zasługuje), jeśli nie potrzebujesz obsługi wcześniejszych wersji lub jeśli nie potrzebujesz elastyczności zapewnianej przez Popen().
jfs
27

coś jest w porządku . Możesz także użyć os.popen (), ale jeśli jest dostępny (Python 2.4+) podproces jest generalnie preferowany.

Jednak w przeciwieństwie do niektórych języków, które do tego zachęcają, generowanie podprocesu, w którym można wykonać tę samą pracę w języku, jest ogólnie uważane za złą formę. Jest wolniejszy, mniej niezawodny i zależny od platformy. Twój przykład byłby lepszy, ponieważ:

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

eta:

baz to katalog i próbuję pobrać zawartość wszystkich plików w tym katalogu

? cat w katalogu wyświetla mi błąd.

Jeśli chcesz listę plików:

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

Jeśli chcesz zawartość wszystkich plików w katalogu, na przykład:

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)

lub, jeśli możesz być pewien, że nie ma tam katalogów, możesz umieścić go w jednym wierszu:

path= '/tmp/baz'
foo= ''.join(open(os.path.join(path, child), 'rb').read() for child in os.listdir(path))
bobince
źródło
1
Chociaż nie była to odpowiedź na pytanie, jest to najlepsza odpowiedź do edukacji użytkowników.
noamtm
1
Tytuł pytania brzmi „co jest odpowiednikiem graweru”. Założyłem, że „kot” to tylko przykładowe polecenie. Ta odpowiedź nie pomaga w ogólnym przypadku.
Jason
15
foo = subprocess.check_output(["cat", "/tmp/baz"])
jfs
źródło
3
To jest teraz najprostszy sposób. „subprocess.check_output” został dodany w Pythonie 2.7, który został wydany w lipcu 2010, po udzieleniu innych odpowiedzi „popen”.
Robert Fleming
10

Począwszy od Pythona 3.5, zalecanym sposobem jest użycie subprocess.run. Od Pythona 3.7, aby uzyskać takie samo zachowanie, jak opisujesz, użyłbyś:

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

To zwróci subprocess.CompletedProcessobiekt. Wyjście na stdout będzie w cpe.stdout, wyjście na stderr będzie w cpe.stderr, które będą bytesobiektami. Możesz zdekodować dane wyjściowe, aby uzyskać strobiekt, używając cpe.stdout.decode()lub otrzymując a, przekazując text=Truedo subprocess.run:

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

W tym drugim przypadku cpe.stdouti cpe.stderroba są strobiektami.

gerrit
źródło
Począwszy od Pythona 3.7 możesz użyć text=Trueparametru, aby odzyskać str zamiast bajtów.
bwv549
6

Najłatwiej jest użyć pakietu poleceń.

import commands

commands.getoutput("whoami")

Wynik:

„bganesan”

Balachander Ganesan
źródło
6
Bardzo proste, ale moduł jest teraz przestarzały.
Gringo Suave
3
import os
foo = os.popen('cat /tmp/baz', 'r').read()
awatts
źródło
3
Jest to odpowiednik odwrotnych apostrofów Rubiego, ale jeśli Twoim problemem jest wyświetlenie zawartości katalogu, nie jest to najlepszy sposób zrobienia tego.
awatts
2

używam

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

Jednym z powyższych przykładów jest:

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

W moim przypadku nie udało się uzyskać dostępu do katalogu / tmp. Po przejrzeniu dokumentacji podprocesu podmieniłem

["prog", "arg"]

z

„prog arg”

i uzyskał pożądane zachowanie rozszerzania powłoki (a la Perl's „prog arg”)

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


Zrezygnowałem z używania Pythona jakiś czas temu, ponieważ zirytowała mnie trudność zrobienia odpowiednika perl `cmd ...`. Cieszę się, że Python uczynił to rozsądnym.

funkyj
źródło
1

Jeśli używasz subprocess.Popen, pamiętaj o określeniu bufsize. Wartością domyślną jest 0, co oznacza „niebuforowany”, a nie „wybierz rozsądną wartość domyślną”.

Jerzy
źródło
1

To nie zadziała w python3, ale w python2 możesz rozszerzyć strza pomocą niestandardowej __repr__metody, która wywołuje polecenie powłoki i zwraca ją w następujący sposób:

#!/usr/bin/env python

import os

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

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

Którego możesz użyć jak

#!/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
źródło
4
Również prawdopodobnie nie powinieneś tego robić *
ThorSummoner
-1

repr()

Operator backtick(`) został usunięty w Python 3. Jest łudząco podobny do pojedynczego cytatu i trudny do wpisania na niektórych klawiaturach. Zamiast tego backtickużyj równoważnej funkcji wbudowanej repr().

Pedro Lobito
źródło
To nie jest odpowiednik backticks bash.
gerrit