Ký TAB
tự này là một ký tự điều khiển mà khi được gửi đến một thiết bị đầu cuối¹ làm cho con trỏ của thiết bị đầu cuối di chuyển đến điểm dừng tab tiếp theo. Theo mặc định, trong hầu hết các thiết bị đầu cuối, các điểm dừng tab cách nhau 8 cột, nhưng đó là cấu hình.
Bạn cũng có thể có các điểm dừng tab trong khoảng thời gian không đều:
$ tabs 3 9 11; printf '\tx\ty\tz\n'
x y z
Chỉ có thiết bị đầu cuối biết có bao nhiêu cột bên phải một TAB sẽ di chuyển con trỏ.
Bạn có thể lấy thông tin đó bằng cách truy vấn vị trí con trỏ từ thiết bị đầu cuối trước và sau khi tab được gửi.
Nếu bạn muốn thực hiện phép tính đó bằng tay cho một dòng nhất định và giả sử rằng dòng đó được in ở cột đầu tiên của màn hình, bạn sẽ cần:
- biết nơi dừng của tab²
- biết chiều rộng hiển thị của mỗi ký tự
- biết chiều rộng của màn hình
- quyết định xem bạn muốn xử lý các ký tự điều khiển khác như
\r
(di chuyển con trỏ đến cột đầu tiên) hay \b
điều đó di chuyển con trỏ trở lại ...)
Nó có thể được đơn giản hóa nếu bạn giả sử các điểm dừng của tab là cứ sau 8 cột, dòng vừa với màn hình và không có ký tự điều khiển hoặc ký tự nào (hoặc không phải ký tự) mà thiết bị đầu cuối của bạn không thể hiển thị đúng.
Với GNU wc
, nếu dòng được lưu trữ trong $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
cho chiều rộng của đường rộng nhất trong đầu vào của nó. Nó thực hiện điều đó bằng cách sử dụng wcwidth(3)
để xác định độ rộng của các ký tự và giả sử các điểm dừng của tab là mỗi 8 cột.
Đối với các hệ thống không phải GNU và với các giả định tương tự, hãy xem phương pháp của @ Kusalananda . Thậm chí còn tốt hơn khi nó cho phép bạn chỉ định các điểm dừng tab nhưng tiếc là hiện tại không hoạt động với GNU expand
(ít nhất) khi đầu vào chứa các ký tự nhiều byte hoặc 0 chiều rộng (như kết hợp các ký tự) hoặc các ký tự có độ rộng gấp đôi.
Lưu ý rằng nếu bạn làm như vậy stty tab3
, kỷ luật dòng thiết bị tty sẽ đảm nhận việc xử lý tab (chuyển đổi TAB thành khoảng trắng dựa trên ý tưởng của chính nó về nơi con trỏ có thể ở trước khi gửi đến thiết bị đầu cuối) và tab thực hiện dừng mỗi 8 cột. Thử nghiệm trên Linux, nó dường như xử lý đúng các ký tự CR, LF và BS cũng như các UTF-8 đa nhân (cung cấp iutf8
cũng được bật) nhưng đó là về nó. Nó giả sử tất cả các ký tự không điều khiển khác (bao gồm các ký tự có độ rộng bằng không, độ rộng gấp đôi) có chiều rộng là 1, rõ ràng là nó không xử lý các chuỗi thoát, không bao bọc đúng cách ... Điều đó có lẽ dành cho các thiết bị đầu cuối không thể xử lý tab.
Trong mọi trường hợp, kỷ luật dòng tty không cần biết con trỏ ở đâu và sử dụng các heuristic ở trên, bởi vì khi sử dụng trình chỉnh sửa icanon
dòng (như khi bạn nhập văn bản cho các ứng dụng như cat
thế không thực hiện trình chỉnh sửa dòng riêng của chúng), khi bạn nhấn TabBackspace, kỷ luật dòng cần biết có bao nhiêu ký tự BS để gửi để xóa ký tự Tab đó để hiển thị. Nếu bạn thay đổi nơi dừng của tab (như với tabs 12
), bạn sẽ nhận thấy rằng Tab không bị xóa đúng cách. Tương tự nếu bạn nhập các ký tự có chiều rộng gấp đôi trước khi nhấn TabBackspace.
² Vì thế, bạn có thể gửi các ký tự tab và truy vấn vị trí con trỏ sau mỗi ký tự. Cái gì đó như:
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"
)
Sau đó, bạn có thể sử dụng expand -t "$tabs"
giải pháp đó như sử dụng giải pháp của @ Kusalananda.
x
) trước khi gọiexpand
khác, bạn cũng sẽ đếm các khoảng trắng ban đầu trong đầu vào.