Determine quanto tempo as guias '\ t' estão em uma linha

10

Em um campo de processamento de texto, há uma maneira de saber se uma guia tem 8 caracteres de comprimento (a duração padrão) ou menos?

Por exemplo, se eu tiver um arquivo de amostra com delimitador de tabulação e o conteúdo de um campo couber em menos de uma tabulação (≤7), e se eu tiver uma tabulação depois disso, essa guia será apenas 'tab size' - tamanho do campo 'em tamanho.

Existe uma maneira de obter o comprimento total das guias em uma linha? Não estou procurando o número de guias (ou seja, 10 guias não devem retornar 10), mas o comprimento de caracteres dessas guias.

Para os seguintes dados de entrada (tabulações delimitadas entre os campos e apenas uma guia):

field0  field00 field000        last-field
fld1    fld11   fld001  last-fld
fd2     fld3    last-fld

Espero contar o comprimento das guias em cada linha, então

11
9
9
    
por αғsнιη 30.08.2017 / 13:26

4 respostas

21

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.

    
por 30.08.2017 / 14:12
7
$ expand file | awk '{ print gsub(/ /, " ") }'
11
9
9

O utilitário POSIX expand expande as guias para espaços. O script awk conta e gera o número de substituições necessárias para substituir todos os espaços em cada linha.

Para evitar a contagem de espaços pré-existentes no arquivo de entrada:

$ tr ' ' '@' <file | expand | awk '{ print gsub(/ /, " ") }'

onde @ é um caractere que é garantido não existir nos dados de entrada.

Se você quiser 10 espaços por guia em vez do comum 8:

$ tr ' ' '@' <file | expand -t 10 | awk '{ print gsub(/ /, " ") }'
9 
15
13
    
por 30.08.2017 / 14:17
2

com perl :

perl -F/\t/ -lpe '$c = 0; $F[-1] eq "" or pop @F; $_ = (map { $c += 8 - (length) % 8 } @F)[-1]' file

Alternativamente:

perl -MList::Util=reduce -lpe \
    '@F = split /\t/, $_, -1; pop @F if $F[-1] ne ""; $_ = reduce { $a + $b } map { 8 - (length) % 8 } @F' file

Você pode alterar 8 acima com algum outro valor se quiser que os TABs tenham um tamanho diferente.

    
por 30.08.2017 / 14:49
2

Também usando expand , mas com a manipulação de parâmetros bash para contar o número de espaços:

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11
    
por 30.08.2017 / 17:11