Dlaczego Popen.communicate () zwraca b'hi \ n 'zamiast' hi '?

92

Czy ktoś może wyjaśnić, dlaczego oczekiwany przeze mnie wynik, „cześć”, jest poprzedzony literą „b” i zakończony nową linią?

Używam Pythona 3.3

>>> import subprocess
>>> print(subprocess.Popen("echo hi", shell=True,
                           stdout=subprocess.PIPE).communicate()[0])
b'hi\n'

To dodatkowe `` b '' nie pojawia się, jeśli uruchomię go z Pythonem 2.7

imagineerThat
źródło
1
Jakiej wersji Pythona używasz?
Necrolyte2
2
Nie jestem pewien co do „b”, ale znak nowej linii jest taki, że jest echo hidrukowany hi\r\n. Aby tego uniknąć, możesz dodać .strip () na końcu lub podobną poprawkę.
azhrei
7
możesz użyć check_output()zamiast .communicate()tutaj:print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="")
jfs

Odpowiedzi:

22

Polecenie echo domyślnie zwraca znak nowego wiersza

Porównaj z tym:

print(subprocess.Popen("echo -n hi", \
    shell=True, stdout=subprocess.PIPE).communicate()[0])

Jeśli chodzi o b poprzedzające ciąg, oznacza to, że jest to sekwencja bajtów, która jest równoważna zwykłemu ciągowi w Pythonie 2.6+

http://docs.python.org/3/reference/lexical_analysis.html#literals

Necrolyte2
źródło
5
nie potrzebujesz znaku '\' w nawiasach.
jfs
94

Symbol bwskazuje, że to, co masz bytes, jest binarną sekwencją bajtów, a nie ciągiem znaków Unicode. Podprocesy wysyłają bajty, a nie znaki, więc to właśnie communicate()zwraca.

bytesTyp nie jest bezpośrednio print()w stanie, więc jesteś jest pokazany reprz bytesmasz. Jeśli znasz kodowanie bajtów, które otrzymałeś z podprocesu, możesz użyć ich decode()do przekształcenia ich w plik drukowalny str:

>>> print(b'hi\n'.decode('ascii'))
hi

Oczywiście ten konkretny przykład działa tylko wtedy, gdy faktycznie otrzymujesz ASCII z podprocesu. Jeśli to nie jest ASCII, pojawi się wyjątek:

>>> print(b'\xff'.decode('ascii'))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0…

Nowa linia jest częścią echo hiwyniku. echoZadanie polega na wyświetleniu przekazanych parametrów, po których następuje znak nowej linii. Jeśli nie jesteś zainteresowany białymi znakami otaczającymi wyjście procesu, możesz użyć w ten strip()sposób:

>>> b'hi\n'.strip()
b'hi'
zigg
źródło
1
Jak sprawić, by funkcja print () wyświetlała ciąg bajtów bez poprzedzającego go „b”? A może musisz najpierw przekonwertować go na ciąg znaków Unicode?
imagineerThat
Jestem ciekawy, kiedy os.popenzwraca ciągi tekstowe, czy istnieje sposób, aby subprocess.Popenrównież je zwrócić, zamiast ciągów bajtów.
Pavel Šimerda
11
Odpowiem sobie, istnieje opcja z tajemniczą nazwą, universal_newlinesktóra powoduje, że Popenobiekt przyjmuje i zwraca ciągi tekstowe.
Pavel Šimerda
3
@ PavelŠimerda Podczas gdy os.popen zwraca ciągi tekstowe, najwyraźniej są one nieprawidłowo dekodowane dla znaków innych niż ASCII, przynajmniej w systemie Windows. Np. Uruchomienie check_output("dir"), wyodrębnienie nazwy pliku z wyjścia, a następnie próba uzyskania do niego dostępu nie openpowiedzie się, jeśli nazwa pliku zawiera niemieckie umlauty. To może być błąd.
kdb
57

Jak wspomniano wcześniej, echo hifaktycznie wracahi\n , co jest oczekiwanym zachowaniem.

Ale prawdopodobnie chcesz po prostu uzyskać dane w „odpowiednim” formacie i nie zajmować się kodowaniem. Wystarczy, że przekażesz universal_newlines=Trueopcję subprocess.Popen()polubienia:

>>> import subprocess
>>> print(subprocess.Popen("echo hi",
                           shell=True,
                           stdout=subprocess.PIPE,
                           universal_newlines=True).communicate()[0])
hi

W ten sposób sam Popen()zastąpi te niechciane symbole.

Danil
źródło
11
universal_newlines=Truedziałał jak urok. To powinna być akceptowana odpowiedź, moim skromnym zdaniem ...
Ethan Strider,
3
Tworzy dodatkowe puste linie.
LoMaPh
1
Jeśli chcesz odciąć kończący znak nowej linii, możesz potrzebować zarówno universal_newlines=True in Popen(aby pozbyć się b''), jak i a strip()na otrzymanym ciągu.
arielf
FYI, dokumentacja mówi, że universal_newlinesjest teraz tylko zgodnym wstecz aliasem textparametru, który jest bardziej przejrzysty, ale tylko w Pythonie 3.7 i nowszych.
Harry Cutts
Tworzy dodatkowe puste wiersze, ponieważ nie działa. universal_newlines nie usuwa \ n
kol23
8

b jest reprezentacją bajtową, a \ n jest wynikiem wyjścia echa.

Następujące spowoduje wydrukowanie tylko danych wyników

import subprocess
print(subprocess.Popen("echo hi", shell=True,stdout=subprocess.PIPE).communicate()[0].decode('utf-8').strip())
Jenish
źródło