The TAB
karakter adalah karakter kontrol yang ketika dikirim ke terminal¹ yang membuat terminal kursor pindah ke tab-stop berikutnya. Secara default, di sebagian besar terminal, tab berhenti terpisah 8 kolom, tetapi itu dapat dikonfigurasi.
Anda juga dapat memiliki penghentian tab pada interval tidak teratur:
$ tabs 3 9 11; printf '\tx\ty\tz\n'
x y z
Hanya terminal yang tahu berapa kolom di sebelah kanan TAB yang akan memindahkan kursor.
Anda dapat memperoleh informasi itu dengan menanyakan posisi kursor dari terminal sebelum dan setelah tab dikirim.
Jika Anda ingin membuat perhitungan dengan tangan untuk garis yang diberikan dan dengan asumsi garis itu dicetak pada kolom pertama layar, Anda harus:
- tahu di mana tab-stops adalah²
- tahu lebar tampilan setiap karakter
- tahu lebar layar
- putuskan apakah Anda ingin menangani karakter kontrol lain seperti
\r
(yang memindahkan kursor ke kolom pertama) atau \b
yang mengembalikan kursor ...)
Ini dapat disederhanakan jika Anda mengasumsikan tab berhenti setiap 8 kolom, garis pas di layar dan tidak ada karakter atau karakter kontrol lain (atau non-karakter) yang terminal Anda tidak dapat ditampilkan dengan benar.
Dengan GNU wc
, jika saluran disimpan di $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
memberikan lebar garis terluas di inputnya. Itu dilakukan dengan menggunakan wcwidth(3)
untuk menentukan lebar karakter dan dengan asumsi tab berhenti setiap 8 kolom.
Untuk sistem non-GNU, dan dengan asumsi yang sama, lihat pendekatan @ Kusalananda . Ini bahkan lebih baik karena memungkinkan Anda menentukan berhenti tab tetapi sayangnya saat ini tidak bekerja dengan GNU expand
(setidaknya) ketika input berisi karakter multi-byte atau 0-lebar (seperti menggabungkan karakter) atau karakter lebar ganda.
¹ catat bahwa jika Anda melakukannya stty tab3
, disiplin garis perangkat tty akan mengambil alih pemrosesan tab (konversikan TAB ke spasi berdasarkan gagasannya sendiri tentang di mana kursor mungkin sebelum dikirim ke terminal) dan implementasikan tab berhenti setiap 8 kolom. Pengujian di Linux, tampaknya menangani dengan benar karakter CR, LF dan BS serta multibyte UTF-8 (disediakan iutf8
juga aktif) tetapi hanya itu saja. Ini mengasumsikan semua karakter non-kontrol lainnya (termasuk nol-lebar, karakter lebar ganda) memiliki lebar 1, itu (jelas) tidak menangani urutan pelarian, tidak membungkus dengan benar ... Itu mungkin dimaksudkan untuk terminal yang tidak dapat melakukan pemrosesan tab.
Bagaimanapun, disiplin garis tty memang perlu tahu di mana kursor berada dan menggunakan heuristik di atas, karena ketika menggunakan icanon
editor baris (seperti ketika Anda memasukkan teks untuk aplikasi seperti cat
itu tidak menerapkan editor baris mereka sendiri), ketika Anda tekan TabBackspace, garis disiplin perlu tahu berapa banyak karakter BS untuk mengirim untuk menghapus karakter Tab untuk tampilan. Jika Anda mengubah tempat tab berhenti (seperti dengan tabs 12
), Anda akan melihat bahwa Tab tidak terhapus dengan benar. Sama jika Anda memasukkan karakter lebar ganda sebelum menekan TabBackspace.
² Untuk itu, Anda dapat mengirim karakter tab dan menanyakan posisi kursor setelah masing-masing. Sesuatu seperti:
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"
)
Kemudian, Anda dapat menggunakannya dengan expand -t "$tabs"
menggunakan solusi @ Kusalananda.
x
) sebelum memanggilexpand
sebaliknya, Anda juga akan menghitung spasi yang awalnya di input juga.