TAB
ตัวอักษรเป็นตัวควบคุมซึ่งเมื่อส่งไปยังterminal¹ทำให้ย้ายเคอร์เซอร์ของสถานีถัดไปของแท็บหยุด ตามค่าเริ่มต้นในเทอร์มินัลส่วนใหญ่แท็บหยุดอยู่ห่างกัน 8 คอลัมน์ แต่สามารถกำหนดค่าได้
นอกจากนี้คุณยังสามารถหยุดแท็บตามช่วงเวลาที่ผิดปกติ:
$ tabs 3 9 11; printf '\tx\ty\tz\n'
x y z
เฉพาะเทอร์มินัลเท่านั้นที่รู้ว่ามีกี่คอลัมน์ทางด้านขวา TAB ที่จะย้ายเคอร์เซอร์
คุณสามารถรับข้อมูลนั้นได้โดยการสอบถามตำแหน่งเคอร์เซอร์จากเทอร์มินัลก่อนและหลังแท็บได้ถูกส่งไป
หากคุณต้องการคำนวณด้วยมือสำหรับบรรทัดที่กำหนดและสมมติว่าบรรทัดนั้นถูกพิมพ์ที่คอลัมน์แรกของหน้าจอคุณจะต้อง:
- ทราบว่าแท็บหยุดอยู่ที่ใด
- รู้ถึงความกว้างของจอแสดงผลของตัวละครทุกตัว
- ทราบความกว้างของหน้าจอ
- ตัดสินใจว่าคุณต้องการจัดการอักขระควบคุมอื่น ๆ เช่น
\r
(ซึ่งเลื่อนเคอร์เซอร์ไปที่คอลัมน์แรก) หรือ\b
เลื่อนเคอร์เซอร์กลับไป ... )
มันสามารถลดความซับซ้อนได้ถ้าคุณคิดว่าแท็บหยุดคือทุก ๆ 8 คอลัมน์บรรทัดนั้นพอดีกับหน้าจอและไม่มีอักขระควบคุมหรืออักขระอื่น ๆ (หรือไม่ใช่อักขระ) ที่เทอร์มินัลของคุณไม่สามารถแสดงได้อย่างถูกต้อง
ด้วย GNU wc
หากบรรทัดถูกเก็บไว้ใน$line
:
width=$(printf %s "$line" | wc -L)
width_without_tabs=$(printf %s "$line" | tr -d '\t' | wc -L)
width_of_tabs=$((width - width_without_tabs))
wc -L
ให้ความกว้างของบรรทัดที่กว้างที่สุดในอินพุต มันทำได้โดยการใช้wcwidth(3)
เพื่อกำหนดความกว้างของตัวละครและสมมติว่าแท็บหยุดทุก 8 คอลัมน์
สำหรับระบบที่ไม่ GNU และมีสมมติฐานเดียวกันเห็น@ วิธี จะดียิ่งขึ้นเพราะจะช่วยให้คุณระบุแท็บหยุด แต่น่าเสียดายที่ในปัจจุบันไม่สามารถทำงานกับ GNU expand
(อย่างน้อย) เมื่ออินพุตมีอักขระหลายไบต์หรือ 0 ความกว้าง (เช่นการรวมอักขระ) หรืออักขระความกว้างสองเท่า
¹โปรดทราบว่าถ้าคุณทำเช่นstty tab3
นั้นวินัยของบรรทัดอุปกรณ์ tty จะเข้าควบคุมกระบวนการแท็บ (แปลง TAB เป็นช่องว่างตามแนวคิดของที่เคอร์เซอร์อาจอยู่ก่อนส่งไปยังเทอร์มินัล) และใช้แท็บหยุดทุก 8 คอลัมน์ การทดสอบบน Linux ดูเหมือนว่าจะจัดการกับอักขระ CR, LF และ BS อย่างถูกต้องรวมถึงตัวเลือก UTF-8 แบบมัลติไบต์ (ที่มีให้iutf8
เช่นกัน) แต่ก็เกี่ยวกับมัน มันถือว่าตัวละครที่ไม่ใช่การควบคุมอื่น ๆ (รวมถึงตัวอักษรความกว้างเป็นศูนย์, ความกว้างสองเท่า) มีความกว้าง 1 มัน (ชัด) ไม่ได้จัดการลำดับหนีไม่ห่ออย่างถูกต้อง ... นั่นอาจจะมีไว้สำหรับอาคารที่ ไม่สามารถทำการประมวลผลแท็บได้
ไม่ว่าในกรณีใดวินัยของ tty line ไม่จำเป็นต้องรู้ว่าเคอร์เซอร์อยู่ที่ไหนและใช้ฮิวริสติกด้านบนเพราะเมื่อใช้ตัวicanon
แก้ไขบรรทัด (เช่นเมื่อคุณป้อนข้อความสำหรับแอปพลิเคชันเช่นcat
นั้น กดTabBackspace, วินัยของสายจำเป็นต้องรู้จำนวนอักขระ BS ที่จะส่งเพื่อลบอักขระแท็บนั้นเพื่อแสดง หากคุณเปลี่ยนตำแหน่งที่แท็บหยุด (เช่นเดียวกับtabs 12
) คุณจะสังเกตเห็นว่าแท็บไม่ถูกลบอย่างถูกต้อง เดียวกันถ้าคุณป้อนตัวอักษรแบบ double-width TabBackspaceก่อนที่จะกด
²คุณสามารถส่งอักขระแท็บและสอบถามตำแหน่งเคอร์เซอร์หลังจากแต่ละอัน สิ่งที่ต้องการ:
tabs=$(
saved_settings=$(stty -g)
stty -icanon min 1 time 0 -echo
gawk -vRS=R -F';' -vORS= < /dev/tty '
function out(s) {print s > "/dev/tty"; fflush("/dev/tty")}
BEGIN{out("\r\t\33[6n")}
$NF <= prev {out("\r"); exit}
{print sep ($NF - 1); sep=","; prev = $NF; out("\t\33[6n")}'
stty "$saved_settings"
)
จากนั้นคุณสามารถใช้สิ่งนั้นเป็นการexpand -t "$tabs"
ใช้โซลูชันของ @ Kusalananda
x
) ก่อนที่จะโทรexpand
มิฉะนั้นคุณจะนับช่องว่างที่อยู่ในอินพุตเช่นกัน