เหตุใด Popen.communicate () จึงส่งคืน b'hi \ n 'แทน' hi '


93

มีใครช่วยอธิบายได้ไหมว่าทำไมผลลัพธ์ที่ฉันต้องการคือ "hi" จึงมีตัวอักษร "b" และตามด้วยขึ้นบรรทัดใหม่

ฉันใช้Python 3.3

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

'b' พิเศษนี้จะไม่ปรากฏหากฉันรันด้วย python 2.7


1
คุณใช้ Python เวอร์ชันใด
Necrolyte2

2
ไม่แน่ใจว่าเกี่ยวกับ 'B' แต่ขึ้นบรรทัดใหม่เป็นเพราะพิมพ์echo hi hi\r\nเพื่อหลีกเลี่ยงปัญหานี้คุณสามารถเพิ่ม .strip () ต่อท้ายหรือแก้ไขที่คล้ายกัน
azhrei

7
คุณสามารถใช้check_output()แทนที่.communicate()นี่:print(subprocess.check_output("echo hi", shell=True, universal_newlines=True), end="")
jfs

คำตอบ:


21

คำสั่ง echo โดยค่าเริ่มต้นจะส่งคืนอักขระขึ้นบรรทัดใหม่

เปรียบเทียบกับสิ่งนี้:

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

สำหรับb ที่นำหน้าสตริงนั้นบ่งชี้ว่าเป็นลำดับไบต์ซึ่งเทียบเท่ากับสตริงปกติใน Python 2.6+

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


5
คุณไม่จำเป็นต้องมี "\" ในวงเล็บ
jfs

95

bแสดงให้เห็นว่าสิ่งที่คุณมีคือbytesซึ่งเป็นลำดับไบนารีไบต์มากกว่าสตริงของอักขระ Unicode ประมวลผลย่อยไบต์เอาต์พุตไม่ใช่อักขระนั่นคือสิ่งที่communicate()ส่งคืน

bytesประเภทไม่ได้โดยตรงprint()สามารถเพื่อให้คุณได้รับการแสดงreprของbytesคุณมี หากคุณทราบการเข้ารหัสของไบต์ที่คุณได้รับจากกระบวนการย่อยคุณสามารถใช้decode()เพื่อแปลงเป็นไบต์ที่พิมพ์ได้str:

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

แน่นอนว่าตัวอย่างเฉพาะนี้ใช้ได้เฉพาะเมื่อคุณได้รับ ASCII จากกระบวนการย่อยเท่านั้น หากไม่ใช่ ASCII คุณจะได้รับข้อยกเว้น:

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

การขึ้นบรรทัดใหม่เป็นส่วนหนึ่งของสิ่งที่echo hiมีเอาต์พุต echoหน้าที่ของคือการส่งออกพารามิเตอร์ที่คุณส่งผ่านตามด้วยขึ้นบรรทัดใหม่ หากคุณไม่สนใจช่องว่างรอบ ๆ ผลลัพธ์ของกระบวนการคุณสามารถใช้strip()ดังนี้:

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

1
คุณจะได้รับฟังก์ชัน print () เพื่อพิมพ์สตริงไบต์โดยไม่ต้องนำหน้า 'b' ได้อย่างไร? หรือคุณต้องแปลงเป็นสตริงยูนิโคดก่อน?
จินตนาการ

ฉันอยากรู้ว่าเมื่อos.popenส่งคืนสตริงข้อความว่ามีวิธีsubprocess.Popenส่งคืนหรือไม่แทนที่จะเป็นสตริงไบต์
Pavel Šimerda

11
ฉันจะตอบตัวเองว่ามีตัวเลือกที่เรียกชื่อที่คลุมเครือuniversal_newlinesซึ่งทำให้Popenออบเจ็กต์ยอมรับและส่งคืนสตริงข้อความ
Pavel Šimerda

3
@ PavelŠimerdaในขณะที่ os.popen ส่งคืนสตริงข้อความดูเหมือนว่าจะถูกถอดรหัสอย่างไม่ถูกต้องสำหรับอักขระที่ไม่ใช่ ascii อย่างน้อยก็ใน Windows เช่นการเรียกใช้check_output("dir")การแตกชื่อไฟล์จากเอาต์พุตแล้วพยายามเข้าถึงด้วยopenจะล้มเหลวหากชื่อไฟล์มีเครื่องหมาย umlauts ภาษาเยอรมัน อาจเป็นข้อบกพร่อง
kdb

57

ตามที่กล่าวไว้ก่อนหน้านี้echo hiจะกลับมาจริงhi\nซึ่งเป็นพฤติกรรมที่คาดหวัง

แต่คุณอาจต้องการเพียงแค่รับข้อมูลในรูปแบบที่ "ถูกต้อง" และไม่ต้องจัดการกับการเข้ารหัส สิ่งที่คุณต้องทำคือผ่านuniversal_newlines=Trueตัวเลือกที่จะsubprocess.Popen()ชอบ:

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

วิธีนี้Popen()จะแทนที่สัญลักษณ์ที่ไม่ต้องการเหล่านี้ด้วยตัวเอง


11
universal_newlines=Trueทำงานเหมือนมีเสน่ห์ นี่ควรเป็นคำตอบที่ได้รับการยอมรับในความเห็นที่ต่ำต้อยของฉัน ...
Ethan Strider

3
มันสร้างบรรทัดว่างพิเศษ
LoMaPh

1
คุณอาจต้องใช้ทั้ง universal_newlines=TrueในPopen(เพื่อกำจัดb'') และstrip()ในสตริงผลลัพธ์หากคุณต้องการตัดบรรทัดใหม่ที่สิ้นสุด
arielf

FYI เอกสารระบุว่าuniversal_newlinesตอนนี้เป็นเพียงนามแฝงที่เข้ากันได้แบบย้อนกลับสำหรับtextพารามิเตอร์ซึ่งชัดเจนกว่า แต่เฉพาะใน Python 3.7 ขึ้นไป
Harry Cutts

มันสร้างบรรทัดว่างพิเศษเนื่องจากไม่ทำงาน universal_newlines ไม่ลบ \ n
kol23

8

b คือการแทนค่าไบต์และ \ n เป็นผลลัพธ์ของเอาท์พุตเสียงสะท้อน

ต่อไปนี้จะพิมพ์เฉพาะข้อมูลผลลัพธ์

import subprocess
print(subprocess.Popen("echo hi", shell=True,stdout=subprocess.PIPE).communicate()[0].decode('utf-8').strip())
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.