วิธีแสดงอักขระควบคุม (^ C, ^ D, ^ [, …) ในเชลล์แตกต่างกัน


13

เมื่อคุณพิมพ์อักขระควบคุมในเชลล์พวกมันจะปรากฏขึ้นโดยใช้สิ่งที่เรียกว่า "เครื่องหมายรูปสัณฐานวิทยา" หลบหนีเช่นได้รับการเขียน^[ในรูปสัญกรณ์

ฉันชอบปรับแต่ง bash shell ของฉันเพื่อทำให้มันดูเท่ห์ ตัวอย่างเช่นฉันเปลี่ยนของฉันPS1และPS2กลายเป็นสี ตอนนี้ฉันต้องการให้ตัวควบคุมมีลักษณะที่ไม่เหมือนใครและทำให้พวกมันแตกต่างจากตัวละครปกติมากขึ้น

$ # Here I type CTRL-C to abort the command.
$ blahblah^C
          ^^ I want these two characters to be displayed differently

มีวิธีที่ทำให้ตัวควบคุมไฮไลท์เปลือกของฉันแตกต่างไปจากนี้หรือไม่

เป็นไปได้หรือไม่ที่จะทำให้มันแสดงเป็นตัวหนาหรืออาจทำให้มันปรากฏเป็นสีต่าง ๆ จากข้อความปกติ?

ฉันกำลังใช้bash shell ที่นี่ แต่ฉันไม่ได้ติดแท็กคำถามด้วยbashเพราะอาจมีวิธีแก้ปัญหาที่ใช้กับเชลล์ที่แตกต่างกันจำนวนมาก

โปรดทราบว่าฉันไม่ทราบว่าจะเน้นการควบคุมอักขระที่ระดับใด ฉันแรกคิดว่ามันอยู่ในเปลือกตัวเอง ตอนนี้ผมเคยได้ยินว่ามันเป็นที่ ReadLineว่าการควบคุมวิธีการควบคุมตัวอักษรที่อยู่ในเปลือกหอยเช่นทุบตี ดังนั้นตอนนี้คำถามจะถูกแท็กด้วยreadlineและฉันยังคงมองหาคำตอบ


ทำไมคุณถึงคิดว่าเชลล์กำลังไฮไลต์อยู่? สำหรับbashมันคือการreadlineจัดการสิ่งนั้นและสำหรับคนอื่น ๆ ส่วนใหญ่มันเป็นไดรเวอร์ tty
mikeserv

ฉันไม่รู้ นั่นคือเหตุผลที่ฉันเขียนบันทึก มันเป็นเพียงการคาดเดาของฉันเพราะแอปพลิเคชันอื่น ๆ เช่นviและlessมักจะเน้นอักขระควบคุมที่แตกต่างจากข้อความปกติ ฉันจะแก้ไขคำถามด้วยข้อมูลนี้ ขอขอบคุณ!
wefwefa3

ที่เหมาะสม สิ่งนั้นเป็นเรื่องยากสำหรับเชลล์เพราะมันมีลำดับความสำคัญที่สอง กับบรรณาธิการงานทั้งหมดของพวกเขาคือการให้หน้าจอ <> อินเตอร์เฟซไฟล์ แต่เปลือกเป็นล่ามคำสั่งสำหรับการแปลภาษาเป็นครั้งแรกและครั้งที่สองแก้ไขบรรทัด(หรือไม่ทั้งหมด)
mikeserv

Readline ถูกใช้โดยโปรแกรมอื่น แต่มันก็เป็นส่วนหนึ่งของ bash: มันเป็นไลบรารี่ที่ลิงค์เข้าไป และกระสุนของวันนี้โดยเฉพาะอย่างยิ่งทุบตีทำอะไรก็ได้ที่คุณคิดได้
alexis

เพียงแค่ใช้zshซึ่งทำนอกกรอบ
Stéphane Chazelas

คำตอบ:


20

เมื่อคุณกดCtrl+Xเทอร์มินัลอีมูเลเตอร์ของคุณจะเขียนไบต์ 0x18 ไปที่ฝั่งมาสเตอร์ของคู่หลอก - เทอร์มินัล

สิ่งที่เกิดขึ้นต่อไปขึ้นอยู่กับวิธีการลงโทษของ tty line (โมดูลซอฟต์แวร์ในเคอร์เนลที่อยู่ระหว่างฝั่งมาสเตอร์ (ภายใต้การควบคุมของอีมูเลเตอร์) และด้านทาส (แอปพลิเคชันที่ทำงานในเทอร์มินัลโต้ตอบด้วย))

คำสั่งเพื่อกำหนดระเบียบวินัยบรรทัด ttyนั้นคือsttyคำสั่ง

เมื่อเรียกใช้โปรแกรมประยุกต์ใบ้เหมือนcatที่ไม่ทราบและไม่ได้สนใจว่า stdin ที่เป็นขั้วหรือไม่, terminal อยู่ในเริ่มต้นที่ยอมรับโหมดที่สาย TTY วินัยดำเนินน้ำมันดิบแก้ไขบรรทัด

แอปพลิเคชั่นแบบโต้ตอบบางอย่างที่ต้องการมากกว่าตัวแก้ไขบรรทัดหยาบนั้นมักจะเปลี่ยนการตั้งค่าเหล่านั้นเมื่อเริ่มต้นและเรียกคืนได้ Modern shells ที่พรอมต์เป็นตัวอย่างของแอปพลิเคชันดังกล่าว พวกเขาใช้เครื่องมือแก้ไขบรรทัดขั้นสูงของตนเอง

โดยทั่วไปในขณะที่คุณป้อนบรรทัดคำสั่งเชลล์จะวางระเบียบบรรทัด tty ในโหมดนั้นและเมื่อคุณกด Enter เพื่อเรียกใช้คำสั่งปัจจุบันเชลล์จะเรียกคืนโหมด tty ปกติ (เช่นเดียวกับก่อนที่จะออกพรอมต์)

ถ้าคุณเรียกใช้stty -aคำสั่งคุณจะเห็นการตั้งค่าปัจจุบันในการใช้งานสำหรับการใช้งานที่เป็นใบ้ คุณมีแนวโน้มที่จะเห็นicanon, echoและechoctlการตั้งค่าถูกเปิดใช้งาน

หมายความว่าอะไร:

  • icanon: เปิดใช้งานเครื่องมือแก้ไขรายการน้ำมันดิบ
  • echo: ตัวอักษรที่คุณพิมพ์ (ที่เทอร์มินัลอีมูเลเตอร์เขียนไปที่ฝั่งมาสเตอร์) ถูกสะท้อนกลับ (ทำให้พร้อมสำหรับการอ่านโดยเทอร์มินัลอีมูเลเตอร์)
  • echoctl: แทนที่จะเป็นสะท้อน ASIS, ตัวควบคุมที่มีการสะท้อนความ^Xเป็น

A B Backspace-aka-Ctrl+H/? C Ctrl+X Backspace Returnดังนั้นสมมติว่าคุณพิมพ์

เทอร์มินัลอีมูเลเตอร์ของคุณจะส่ง: AB\bC\x18\b\r. วินัยเส้นจะสะท้อนกลับAB\b \bC^X\b \b\b \b\r\nและการประยุกต์ใช้ที่อ่านข้อมูลจากด้านทาสที่ ( /dev/pts/x) AC\nจะอ่าน

แอปพลิเคชั่นทั้งหมดที่เห็นคือAC\nและเฉพาะเมื่อกดของคุณEnterดังนั้นจึงไม่สามารถมีการควบคุมใด ๆ ในการส่งออก^Xมี

คุณจะสังเกตเห็นว่าสำหรับเสียงก้องแรก^H( ^?กับบางขั้วดูการeraseตั้งค่า) ส่งผลให้\b \bถูกส่งกลับไปยังสถานี นั่นคือลำดับการเลื่อนเคอร์เซอร์ไปข้างหลังเขียนทับด้วยช่องว่างเลื่อนเคอร์เซอร์กลับมาอีกครั้งในขณะที่อันดับที่สอง^Hส่งผล\b \b\b \bให้ลบทั้งสอง^และXตัวอักษรเหล่านั้น

The ^X(0x18) นั้นกำลังถูกแปล^และXส่งออก เช่นเดียวกับBมันไม่ได้ทำให้แอปพลิเคชันเกิดขึ้นเนื่องจากเราลบด้วย Backspace

\r(aka ^M) แปลเป็น\r\n( ^M^J) สำหรับ echo และ\n( ^J) สำหรับแอปพลิเคชัน

ดังนั้นตัวเลือกของเราสำหรับแอปพลิเคชันโง่เหล่านั้นคือ

  • ปิดการใช้งานecho( stty -echo) นั่นเปลี่ยนวิธีการควบคุมตัวละครอย่างมีประสิทธิภาพโดย ... ไม่ได้สะท้อนอะไรเลย ไม่ใช่ทางออกจริงๆ
  • echoctlปิดการใช้งาน ที่เปลี่ยนวิธีการควบคุมตัวละคร (นอกเหนือจาก^H, ^M... และอื่น ๆ ทั้งหมดที่ใช้โดยตัวแก้ไขบรรทัด) จะถูกสะท้อน พวกเขาจะสะท้อนตามที่เป็นอยู่ ตัวอย่างเช่นตัวละคร ESC จะส่งเป็น\e( ^[/ 0x1b) ไบต์ (ซึ่งได้รับการยอมรับว่าเป็นจุดเริ่มต้นของลำดับการหลบหนีโดยเทอร์มินัล) ^Gคุณส่ง\a(เบลทำให้เสียงบี๊บปลายทางของคุณ) ... ไม่ใช่ตัวเลือก .
  • ปิดการใช้งานเครื่องมือแก้ไขเส้นหยาบ ( stty -icanon) ไม่ใช่ตัวเลือกจริงๆเนื่องจากแอปพลิเคชันที่หยาบจะกลายเป็นประโยชน์น้อยกว่ามาก
  • แก้ไขรหัสเคอร์เนลเพื่อเปลี่ยนพฤติกรรมของวินัยของ tty line ดังนั้นเสียงก้องของอักขระควบคุมส่ง\e[7m^X\e[mแทนที่จะเป็นเพียงแค่^X( \e[7mโดยปกติแล้วจะเปิดใช้งานวิดีโอย้อนกลับในเทอร์มินัลส่วนใหญ่)

ตัวเลือกอาจใช้ wrapper rlwrapที่เป็นแฮ็คสกปรกเพื่อเพิ่มตัวแก้ไขบรรทัดแฟนซีในแอปพลิเคชันที่เป็นใบ้ wrapper นั้นจะพยายามแทนที่วิread()จากอุปกรณ์เทอร์มินัลไปเป็นการเรียกไปยังตัวแก้ไขบรรทัด readline (ซึ่งจะเปลี่ยนโหมดของวินัยของบรรทัด tty)

ยิ่งไปกว่านั้นคุณยังสามารถลองวิธีการแก้ปัญหาเช่นนี้ได้ซึ่งจะทำการไฮแจ็คอินพุตทั้งหมดจากเทอร์มินัลเพื่อให้ผ่านตัวแก้ไขบรรทัดของ zsh (ซึ่งเกิดจากการเน้นสี^Xในวิดีโอย้อนกลับ) โดยอาศัย:execคุณสมบัติของหน้าจอ GNU

ตอนนี้สำหรับแอปพลิเคชันที่ใช้ตัวแก้ไขบรรทัดของตัวเองมันขึ้นอยู่กับพวกเขาในการตัดสินใจว่าจะทำเสียงสะท้อนอย่างไร bashใช้ readline สำหรับสิ่งที่ไม่มีการสนับสนุนใด ๆ สำหรับการปรับแต่งวิธีการควบคุมตัวละครที่สะท้อน

สำหรับzshดู:

info --index-search='highlighting, special characters' zsh

zshจะเน้นอักขระที่ไม่สามารถพิมพ์ได้ตามค่าเริ่มต้น คุณสามารถปรับแต่งการเน้นด้วยตัวอย่างเช่น:

zle_highlight=(special:fg=white,bg=red)

สำหรับสีขาวที่เน้นสีแดงสำหรับอักขระพิเศษเหล่านั้น

การแสดงข้อความของตัวละครเหล่านั้นไม่สามารถปรับแต่งได้

ใน UTF-8 สถานที่เกิดเหตุ, 0x18 จะกลายเป็น^X, \u378, \U7fffffff(สองจุดรหัส Unicode ที่ไม่ได้กำหนด) เช่น<0378>, <7FFFFFFF>, \u200b(ที่ไม่จริงๆอักขระ Unicode พิมพ์) <200B>ในฐานะ

\x80ใน iso8859-1 locale จะแสดงผลเป็น^�... เป็นต้น


3

ฉันมีรหัสนี้ในไฟล์. bashrc ของฉัน:

function get_exit_status()
{
        local code=$?
        if [ $code -ne 0 ]
        then
                printf $'\001\033[31m\002'"($code)"$'\001\033[0m\002'" "
        fi
}

แล้วฉันจะเรียกใช้ฟังก์ชันนี้ใน PS1 ของฉัน

PS1='\u@\h \w $(get_exit_status)'

ด้วยวิธีนี้ถ้าคุณกด ^ C คุณจะเห็นมันในการแจ้งของคุณ

I@mycomputer ~ ^C
I@mycomputer ~ (130)

รหัสสถานะการออกทั้งหมดที่ไม่ใช่ "0" จะได้รับแจ้ง


$? ขยายออกเป็นสถานะใน PS1 เช่น PS1 = '$? \ u @ \ h \ w'
teknopaul
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.