O caractere TAB
é um caractere de controle que, quando enviado para um terminal¹, faz com que o cursor do terminal se mova para a próxima parada de tabulação. Por padrão, na maioria dos terminais, as paradas de tabulação estão separadas por 8 colunas, mas isso é configurável.
Você também pode ter paradas de tabulação em intervalos irregulares:
$ tabs 3 9 11; printf '\tx\ty\tz\n'
x y z
Apenas o terminal sabe quantas colunas à direita uma TAB moverá o cursor.
Você pode obter essa informação consultando a posição do cursor no terminal antes e depois do envio da guia.
Se você quiser fazer esse cálculo manualmente para uma determinada linha e assumir que a linha é impressa na primeira coluna da tela, será necessário:
- sabe onde estão as paradas de tabulação²
- conheça a largura de exibição de todos os caracteres
- sabe a largura da tela
- decida se você deseja manipular outros caracteres de controle como
\r
(que move o cursor para a primeira coluna) ou\b
que move o cursor de volta ...)
Pode ser simplificado se você assumir que as paradas de tabulação estão a cada 8 colunas, a linha se encaixa na tela e não há outros caracteres de controle ou caracteres (ou não caracteres) que seu terminal não possa exibir corretamente.
Com o GNU wc
, se a linha estiver armazenada em $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
fornece a largura da linha mais larga em sua entrada. Ele faz isso usando wcwidth(3)
para determinar a largura dos caracteres e assumindo que as paradas de tabulação estão a cada 8 colunas.
Para sistemas não-GNU, e com as mesmas suposições, veja a abordagem do Kusalananda . É ainda melhor, pois permite especificar as paradas de tabulação, mas infelizmente não funciona com o GNU expand
(pelo menos) quando a entrada contém caracteres multibyte ou 0 largura (como caracteres combinados) ou caracteres de largura dupla.
¹ observe que, se você usar stty tab3
, a disciplina de linha de dispositivo tty assumirá o processamento da guia (converter TAB em espaços baseados em sua própria ideia de onde o cursor pode estar antes de enviar para o terminal) e implementar guia pára a cada 8 colunas. Testando no Linux, ele parece manipular corretamente os caracteres CR, LF e BS, bem como os UTF-8 multibyte (desde que iutf8
também esteja ativado), mas é sobre isso. Ele assume que todos os outros caracteres que não são de controle (incluindo caracteres de largura zero e largura dupla) têm uma largura de 1, (obviamente) não manipula seqüências de escape, não envolve corretamente ... Isso provavelmente é destinado a terminais que não pode fazer o processamento de guias.
Em qualquer caso, a disciplina de linha tty precisa saber onde o cursor está e usa as heurísticas acima, porque ao usar o editor de linha icanon
(como quando você insere texto para aplicativos como cat
que não implementar seu próprio editor de linha), quando você pressionar Tab Backspace , a disciplina de linha precisará saber quantos caracteres BS enviar para apagar essa tabulação personagem para exibição. Se você alterar onde as tabulações são (como com tabs 12
), você notará que as Guias não são apagadas corretamente. O mesmo se você digitar caracteres de largura dupla antes de pressionar a tecla Tab Backspace .
² Para isso, você poderia enviar caracteres de tabulação e consultar a posição do cursor depois de cada um. Algo como:
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[6n")}
$NF <= prev {out("\r"); exit}
{print sep ($NF - 1); sep=","; prev = $NF; out("\t[6n")}'
stty "$saved_settings"
)
Então, você pode usar isso como expand -t "$tabs"
usando a solução de @ Kusalananda.