TAB
Znak jest znak kontrolny, który gdy wysłany do terminal¹ powoduje ruch kursora terminala do następnej karty-stop. Domyślnie w większości terminali tabulatory są oddalone od siebie o 8 kolumn, ale można je konfigurować.
Możesz także ustawić tabulatory w nieregularnych odstępach czasu:
$ tabs 3 9 11; printf '\tx\ty\tz\n'
x y z
Tylko terminal wie, ile kolumn po prawej stronie TAB poruszy kursor.
Możesz uzyskać te informacje, sprawdzając pozycję kursora z terminala przed i po wysłaniu zakładki.
Jeśli chcesz wykonać te obliczenia ręcznie dla danej linii i zakładając, że linia jest drukowana w pierwszej kolumnie ekranu, musisz:
- wiedzieć, gdzie są tabulatory²
- znać szerokość wyświetlania każdego znaku
- znać szerokość ekranu
- zdecyduj, czy chcesz obsługiwać inne znaki sterujące, takie jak
\r
(który przesuwa kursor do pierwszej kolumny), czy \b
też przesuwa kursor do tyłu ...)
Można to uprościć, zakładając, że tabulatory są co 8 kolumn, linia mieści się na ekranie i nie ma żadnych innych znaków sterujących ani znaków (lub znaków innych niż), których terminal nie może poprawnie wyświetlić.
W GNU wc
, jeśli wiersz jest przechowywany w $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
podaje szerokość najszerszej linii na wejściu. Robi to za pomocą wcwidth(3)
określania szerokości znaków i zakładając, że tabulatory są co 8 kolumn.
W przypadku systemów innych niż GNU i przy takich samych założeniach, patrz podejście @ Kusalanandy . Jest to nawet lepsze, ponieważ pozwala określić tabulatory, ale niestety obecnie nie działa z GNU expand
(przynajmniej), gdy wejście zawiera znaki wielobajtowe lub szerokość 0 (jak łączenie znaków) lub znaki o podwójnej szerokości.
¹ zauważ jednak, że jeśli to zrobisz stty tab3
, dyscyplina linii urządzenia tty przejmie przetwarzanie tabulatorów (konwertuje TAB na spacje w oparciu o własne wyobrażenie o tym, gdzie może być kursor przed wysłaniem do terminala) i implementuje tabulatory co 8 kolumn. Testując na Linuksie, wydaje się, że poprawnie obsługuje znaki CR, LF i BS, a także znaki wielobajtowe UTF-8 (pod warunkiem, że iutf8
jest włączony), ale o to chodzi. Zakłada, że wszystkie inne znaki niekontrolowane (w tym znaki o zerowej szerokości i podwójnej szerokości) mają szerokość 1, to (oczywiście) nie obsługuje sekwencji specjalnych, nie jest poprawnie zawijane ... To prawdopodobnie jest przeznaczone dla terminali, które nie można przetwarzać kart.
W każdym razie dyscyplina linii tty musi wiedzieć, gdzie jest kursor, i korzysta z powyższych heurystyk, ponieważ podczas korzystania z icanon
edytora linii (np. Podczas wprowadzania tekstu dla aplikacji takich jak cat
ten nie implementuje własnego edytora linii), gdy naciśnij TabBackspace, dyscyplina linii musi wiedzieć, ile znaków BS należy wysłać, aby usunąć ten znak Tab do wyświetlenia. Jeśli zmienisz miejsce zatrzymania tabulatorów (podobnie jak w przypadku tabs 12
), zauważysz, że Tabs nie zostały poprawnie usunięte. To samo, jeśli przed naciśnięciem wprowadzisz znaki o podwójnej szerokości TabBackspace.
² W tym celu można wysyłać znaki tabulacji i sprawdzać pozycję kursora po każdym z nich. Coś jak:
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"
)
Następnie możesz użyć tego jako expand -t "$tabs"
rozwiązania @ Kusalananda.
x
Przed wywołaniem chcesz zamienić spacje na inny znak o jednej szerokości (np. )expand
, Policzysz również spacje, które początkowo były w danych wejściowych.