Determine cuánto tiempo duran las pestañas '\ t' en una línea


10

En un campo de procesamiento de texto, ¿hay alguna manera de saber si una pestaña tiene 8 caracteres de longitud (la longitud predeterminada) o menos?

Por ejemplo, si tengo un archivo de muestra con delimitador de pestañas y el contenido de un campo cabe en menos de una pestaña (≤7), y si tengo una pestaña después de eso, esa pestaña será solo 'tamaño de pestaña - tamaño de campo ' en longitud.

¿Hay alguna manera de obtener la longitud total de las pestañas en una línea? No busco el número de pestañas (es decir, 10 pestañas no deberían devolver 10) sino la longitud de caracteres de esas pestañas.

Para los siguientes datos de entrada (pestaña delimitada entre campos y solo una pestaña):

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

Espero contar la longitud de las pestañas en cada línea, así que

11
9
9

Respuestas:


22

El TABcarácter es un carácter de control que cuando se envía a un terminal¹ hace que el cursor del terminal se mueva a la siguiente tabulación. Por defecto, en la mayoría de las terminales, las tabulaciones están separadas por 8 columnas, pero eso es configurable.

También puede tener tabulaciones a intervalos irregulares:

$ tabs 3 9 11; printf '\tx\ty\tz\n'
  x     y z

Solo el terminal sabe cuántas columnas a la derecha moverá el cursor una TAB.

Puede obtener esa información consultando la posición del cursor desde el terminal antes y después de que se haya enviado la pestaña.

Si desea hacer ese cálculo a mano para una línea determinada y suponiendo que esa línea se imprime en la primera columna de la pantalla, deberá:

  • saber dónde están las tabulaciones²
  • saber el ancho de visualización de cada personaje
  • saber el ancho de la pantalla
  • decida si desea manejar otros caracteres de control como \r(que mueve el cursor a la primera columna) o \bque mueve el cursor hacia atrás ...)

Se puede simplificar si supone que las tabulaciones son cada 8 columnas, la línea cabe en la pantalla y no hay otros caracteres de control o caracteres (o no caracteres) que su terminal no pueda mostrar correctamente.

Con GNU wc, si la línea se almacena en $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 -Lda el ancho de la línea más ancha en su entrada. Lo hace usando wcwidth(3)para determinar el ancho de los caracteres y suponiendo que las tabulaciones son cada 8 columnas.

Para los sistemas que no son GNU, y con los mismos supuestos, ver el enfoque de @ Kusalananda . Es incluso mejor, ya que le permite especificar las tabulaciones, pero desafortunadamente actualmente no funciona con GNU expand(al menos) cuando la entrada contiene caracteres de varios bytes o ancho 0 (como combinar caracteres) o caracteres de doble ancho.


Sin embargo, tenga en cuenta que si lo hace stty tab3, la disciplina de línea del dispositivo tty se hará cargo del procesamiento de la pestaña (convierta TAB en espacios basados ​​en su propia idea de dónde podría estar el cursor antes de enviarlo al terminal) y la pestaña de implementación se detiene cada 8 columnas. Al probar en Linux, parece manejar correctamente los caracteres CR, LF y BS, así como los multibyte UTF-8 (siempre iutf8que también esté activado), pero eso es todo. Asume que todos los demás caracteres que no son de control (incluidos los caracteres de ancho cero y doble ancho) tienen un ancho de 1, (obviamente) no maneja secuencias de escape, no se ajusta correctamente ... Eso probablemente esté destinado a terminales que No se puede hacer el procesamiento de pestañas.

En cualquier caso, la disciplina de línea tty necesita saber dónde está el cursor y usa las heurísticas anteriores, porque cuando usa el icanoneditor de línea (como cuando ingresa texto para aplicaciones como catesa, no implementa su propio editor de línea), cuando presione TabBackspace, la disciplina de línea necesita saber cuántos caracteres BS enviar para borrar ese carácter Tab para mostrar. Si cambia dónde están las tabulaciones (como con tabs 12), notará que las pestañas no se borran correctamente. Lo mismo si ingresa caracteres de doble ancho antes de presionar TabBackspace.


² Para eso, puede enviar caracteres de tabulación y consultar la posición del cursor después de cada uno. 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\33[6n")}
    $NF <= prev {out("\r"); exit}
    {print sep ($NF - 1); sep=","; prev = $NF; out("\t\33[6n")}'
  stty "$saved_settings"
)

Entonces, puedes usar eso como expand -t "$tabs"la solución de @ Kusalananda.


7
$ expand file | awk '{ print gsub(/ /, " ") }'
11
9
9

La expandutilidad POSIX expande las pestañas a espacios. El awkscript cuenta y genera el número de sustituciones necesarias para reemplazar todos los espacios en cada línea.

Para evitar contar los espacios preexistentes en el archivo de entrada:

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

donde @es un carácter que se garantiza que no existe en los datos de entrada.

Si desea 10 espacios por pestaña en lugar de los 8 normales:

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

3
Desea reemplazar espacios con algún otro carácter de un ancho (como x) antes de llamar de lo expandcontrario, también estaría contando los espacios que inicialmente estaban en la entrada también.
Stéphane Chazelas

1
expandtambién supone tabulaciones cada 8 columnas (aunque puede cambiar eso con las opciones). Tenga en cuenta que la implementación de GNU no admite caracteres de varios bytes (y mucho menos los de 0 o de doble ancho). IIRC el FreeBSD uno está bien.
Stéphane Chazelas

@ StéphaneChazelas A menos que, por supuesto, eso sea parte del plan para contar el ancho de los 0x09s con los 0x20s ;-)
can-ned_food

2

Con 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

Puede cambiar el 8 anterior con algún otro valor si desea que las TAB tengan una longitud diferente.


2

También se usa expand, pero con manipulación de parámetros bash para contar el número de espacios:

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.