Mengapa Popen.communicate () mengembalikan b'hi \ n 'bukan' hi '?


92

Adakah yang bisa menjelaskan mengapa hasil yang saya inginkan, "hai", diawali dengan huruf 'b' dan diikuti dengan baris baru?

Saya menggunakan Python 3.3

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

'B' ekstra ini tidak muncul jika saya menjalankannya dengan python 2.7


1
Versi Python apa yang Anda gunakan?
Necrolyte2

2
Tidak yakin tentang 'b', tetapi baris baru karena echo hicetakan hi\r\n. Untuk menghindarinya, Anda bisa menambahkan .strip () di akhir, atau perbaikan serupa.
azhrei

7
Anda dapat menggunakan check_output()alih-alih di .communicate()sini:print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="")
jfs

Jawaban:


22

Perintah echo secara default mengembalikan karakter baris baru

Bandingkan dengan ini:

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

Adapun b sebelum string itu menunjukkan bahwa itu adalah urutan byte yang setara dengan string normal di Python 2.6+

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


5
Anda tidak perlu '\' di dalam tanda kurung.
jfs

94

Ini bmenunjukkan bahwa apa yang Anda miliki adalah bytes, yang merupakan urutan byte biner, bukan string karakter Unicode. Subproses mengeluarkan byte, bukan karakter, jadi itulah yang ditampilkan communicate().

The bytestipe tidak langsung print()mampu, jadi Anda sedang menunjukkan reprdari bytesyang Anda miliki. Jika Anda mengetahui pengkodean byte yang Anda terima dari subproses, Anda dapat menggunakan decode()untuk mengubahnya menjadi dapat dicetak str:

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

Tentu saja, contoh khusus ini hanya berfungsi jika Anda benar-benar menerima ASCII dari subproses. Jika bukan ASCII, Anda akan mendapatkan pengecualian:

>>> 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…

Baris baru adalah bagian dari apa yang echo himemiliki keluaran. echoTugasnya adalah menampilkan parameter yang Anda lewati, diikuti dengan baris baru. Jika Anda tidak tertarik dengan spasi yang mengelilingi keluaran proses, Anda dapat menggunakan strip()seperti ini:

>>> b'hi\n'.strip()
b'hi'

1
Bagaimana Anda mendapatkan fungsi print () untuk mencetak string byte tanpa 'b' sebelumnya? Atau apakah Anda perlu mengubahnya menjadi string unicode terlebih dahulu?
Imagineer Itu

Saya penasaran, ketika os.popenmengembalikan string teks, apakah ada cara untuk membuatnya subprocess.Popenjuga mengembalikannya, bukan string byte.
Pavel Šimerda

11
Saya akan menjawab sendiri, ada opsi dengan nama samar yang disebut universal_newlinesyang menyebabkan Popenobjek menerima dan mengembalikan string teks.
Pavel Šimerda

3
@ PavelŠimerda Sementara os.popen mengembalikan string teks, mereka tampaknya sedang diterjemahkan secara tidak benar untuk karakter non-ascii, setidaknya pada Windows. Misalnya menjalankan check_output("dir"), mengekstrak nama file dari output dan kemudian mencoba mengaksesnya dengan openakan gagal jika nama file tersebut berisi umlaut Jerman. Mungkin bug.
kdb

57

Seperti yang disebutkan sebelumnya, echo hisebenarnya kembali hi\n, yang merupakan perilaku yang diharapkan.

Tetapi Anda mungkin hanya ingin mendapatkan data dalam format yang "benar" dan tidak berurusan dengan pengkodean. Yang perlu Anda lakukan adalah memberikan universal_newlines=Trueopsi untuk subprocess.Popen()menyukainya:

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

Cara ini Popen()akan menggantikan simbol yang tidak diinginkan dengan sendirinya.


11
universal_newlines=Truebekerja seperti pesona. Ini harus menjadi jawaban yang diterima, menurut pendapat saya ...
Ethan Strider

3
Ini menghasilkan baris kosong ekstra.
LoMaPh

1
Anda mungkin perlu baik universal_newlines=True dalam Popen(untuk menyingkirkan b'') dan strip()pada string yang dihasilkan, jika Anda ingin memotong baris terminating.
arielf

FYI, dokumentasi mengatakan universal_newlinessekarang hanya alias yang kompatibel ke belakang untuk textparameter, yang lebih jelas tetapi hanya di Python 3.7 dan di atasnya.
Harry Cutts

Ini menghasilkan baris kosong ekstra karena tidak berfungsi. universal_newlines tidak menghapus \ n
kol23

8

b adalah representasi byte dan \ n adalah hasil dari output echo.

Berikut ini hanya akan mencetak data hasil

import subprocess
print(subprocess.Popen("echo hi", shell=True,stdout=subprocess.PIPE).communicate()[0].decode('utf-8').strip())
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.