Bash พยายามเขียน prompt สองตัวหรือไม่


11

ฉันกำลังดูผลลัพธ์ strace ของกระบวนการทุบตีทำงานที่เชื่อมต่อกับเทอร์มินัลเพื่อการศึกษา

กระบวนการทุบตีของฉันมี PID 2883

ฉันพิมพ์

[OP@localhost ~]$ strace -e trace=openat,read,write,fork,vfork,clone,execve -p 2883 2> bash.strace

เข้าสู่อาคารผู้โดยสาร จากนั้นฉันก็เข้าสู่กระบวนการทุบตีของฉันและมีปฏิสัมพันธ์ต่อไปนี้:

[OP@localhost ~]$ ls

ฉันมองเห็นผลลัพธ์

strace: Process 2883 attached
read(0, "l", 1)                         = 1
write(2, "l", 1)                        = 1
read(0, "s", 1)                         = 1
write(2, "s", 1)                        = 1
read(0, "\r", 1)                        = 1
write(2, "\n", 1)                       = 1
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fec6b1d8e50) = 3917
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3917, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
write(1, "\33]0;OP@localhost:~\7", 23) = 23
write(2, "[OP@localhost ~]$ ", 22)  = 22
...

ฉันสับสนในสองบรรทัดสุดท้าย ดูเหมือนว่า bash พยายามที่จะเขียน shell prompt สองตัว? เกิดอะไรขึ้นที่นี่?

คำตอบ:


24

<ESC>]0;ลำดับ (แสดงเป็น\33]0;โดย strace) เป็นลำดับหนีเพื่อตั้งชื่อหน้าต่าง terminal มันถูกยกเลิกด้วยอักขระ BEL ( \7) ดังนั้นอันดับแรกwriteจะตั้งชื่อหน้าต่าง ครั้งที่สองพิมพ์พรอมต์ที่เกิดขึ้นจริง โปรดทราบว่านอกเหนือจากลำดับหลบหนีพวกมันไม่เหมือนกันทั้งหมด พรอมต์มีสภาพแวดล้อม[..]ในขณะที่ชื่อหน้าต่างไม่ได้

นอกจากนี้เรายังสามารถเห็นได้ว่าการเขียนครั้งแรกไปที่ stdout (fd 1, อาร์กิวเมนต์แรกที่write()) และที่สองถึง stderr Bash พิมพ์พรอมต์ไปที่ stderr ดังนั้นการเขียนครั้งแรกจึงมาจากที่อื่น อาจPROMPT_COMMANDเป็นที่ใดที่หนึ่งเหมือนกับสคริปต์เริ่มต้นของ Debian สำหรับ Bash มีบางอย่างเช่นนี้:

case "$TERM" in
xterm*|rxvt*)
    PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
    ;;
*)
    ;;
esac

มันตั้งว่าPROMPT_COMMANDถ้าทำงานxtermหรือrxvtซึ่งควรสนับสนุนลำดับการหลบหนีนั้น


คุณรู้หรือไม่ว่าทำไมทุบตีจึงปรากฏขึ้นเพื่ออ่านสิ่งที่ตัวละครโดยตัวอักษรมากกว่าการอ่านในบรรทัดในเวลา? นอกจากนี้ทำไมทุบตีเขียน "l" และ "s" เพื่อ stdout? ถ้าฉันทำ strace ที่คล้ายกันcatมีสองความแตกต่าง: มันอ่านบรรทัดทีละบรรทัดและในขณะที่มันสะท้อนอินพุตของมันกลับไปที่ stdout ฉันเห็นอินพุตสองครั้ง (เมื่อฉันพิมพ์และเมื่อแมว echoes มัน)
extremeaxe5 5

@ Extremeaxe5 นั้นเป็นเพราะ Bash (หรือมากกว่านั้นคือ readline library) จะจัดการกับการประมวลผลบรรทัดคำสั่งทั้งหมดแทนการพึ่งพาการประมวลผลที่ จำกัด โดยเทอร์มินัล ต้องรับอินพุตทันทีเพื่อตัดสินใจว่าจะทำอย่างไรเมื่อเช่นอักขระ TAB หรือ^A(Ctrl-A) หรือกดอักขระพิเศษต่าง ๆ นอกจากนี้มันจะปิดเสียงสะท้อนของเทอร์มินัลเพื่อให้สามารถตัดสินใจได้ว่าจะส่งออกอะไรสำหรับอักขระอินพุตแต่ละตัว (อีกครั้ง TAB มักจะไม่ส่งออก TAB) catไม่ทำเช่นนั้น ถ้าเป็นเช่นนั้นลองใช้งานdashซึ่งจะไม่ทำการจัดการบรรทัดคำสั่งใด ๆ
ilkkachu

จริงๆแล้วเหตุผลที่ Bash เรียกread()ให้อ่านครั้งละหนึ่งไบต์เท่านั้นคือมันไม่สามารถอ่านบรรทัดใหม่ได้ ขึ้นบรรทัดใหม่อาจทำให้เรียกใช้โปรแกรมภายนอกซึ่งอาจอ่านจากอินพุตเดียวกัน (และโปรแกรมนั้นควรจะสามารถอ่านอักขระใด ๆ หลังจากขึ้นบรรทัดใหม่) หากไม่จำเป็นต้องสนใจมันสามารถเรียกได้read()ด้วยขีด จำกัด ที่ใหญ่กว่าและเมื่อเทอร์มินัลอยู่ในโหมด raw ก็มักจะได้รับอินพุต ทีละตัวอักษร (มันจะขึ้นอยู่กับว่าอักขระอินพุตจะมาถึงเร็วแค่ไหนและกำหนดเวลาดำเนินการอย่างไร)
ilkkachu

ความคิดเห็นที่สองของคุณดูเหมือนจะเป็นจริงเพราะ Bash จัดการกับบรรทัดคำสั่งเอง
Extremeaxe5

@ Extremeaxe5 ใช่ฉันคิดว่ามันเป็นกรณีทั่วไปอยู่แล้ว แต่ถึงแม้ว่าเชลล์จะอาศัยการแก้ไขบรรทัดของเทอร์มินัลเวลาก็อาจเป็นปัญหาได้ หากสองบรรทัดถูกส่งต่อเนื่องกันอย่างรวดเร็ว (คิดว่าการวางข้อมูล) และระบบถูกโหลดเพียงพอดังนั้นเชลล์จะไม่ได้รับการจัดตารางทันที (หรือแย่กว่านั้นคือเชลล์หยุดทำงาน) จากนั้นread()บัฟเฟอร์ที่มีขนาดใหญ่กว่า สายเดียวกัน ผมไม่คิดว่ามีการรับประกันว่าread()จะเสมอกลับมาเพียงหนึ่งบรรทัดในโหมดสุก
ilkkachu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.