¿Cómo imprimir cadenas separadas por TAB en bash?

9

Estoy tratando de imprimir dos cadenas separadas por una TAB. Yo he tratado:

echo -e 'foo\tbar'
printf '%s\t%s\n' foo bar

Ambos imprimen:

foo     bar

Donde el espacio en blanco entre los dos es en realidad 5 espacios (según la selección de la salida con el mouse en Putty).

También intenté usar CTRL + V y presionar TAB al escribir el comando, con el mismo resultado.

¿Cuál es la forma correcta de forzar que la pestaña se imprima como pestaña, para que pueda seleccionar la salida y copiarla en otro lugar, con pestañas?

Y la pregunta secundaria: ¿por qué bash está expandiendo las pestañas en espacios?

Actualización : Aparentemente, este es un problema de Putty: /superuser/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-changing-them-to -espacios

Asu
fuente
¿Por qué no escapar? printf '%s\\t%s\n' foo bar
Valentin Bajrami
@steeldriver Gracias, eso es muy similar a lo que necesito, pero finalmente no hay una solución ...
Asu
1
@Valentin Eso genera foo\tbar...
wjandrea
1
A pesar de que ya sabes que tienes un problema con tu terminal: Bash por sí solo se interpreta $'\t'como un tabulador. Por lo tanto, siempre puede concatenar cadenas como esta, por ejemplo, como asignación: v='This is'$'\t''a test'y la imprime literalmente, por ejemploprintf '%s' "$v"
rexkogitans

Respuestas:

9

Como dijo ilkkachu, este no es un problema con bash, sino con el emulador de terminal que convierte las pestañas en espacios en la salida.

Verificando diferentes terminales, putty, xterm y konsole convierten las pestañas en espacios, mientras que urxvt y gnome-terminal no lo hacen. Entonces, otra solución es cambiar terminales.

JoL
fuente
3
También puede hacerlo el controlador tty después de ejecutarlo stty tab3.
Stéphane Chazelas
14

el espacio en blanco entre los dos es en realidad 5 espacios.

No, no es. No en la salida de echoo printf.

$ echo -e 'foo\tbar' | od -c
0000000   f   o   o  \t   b   a   r  \n
0000010

¿Cuál es la forma correcta de forzar que la pestaña se imprima como pestaña, para que pueda seleccionar la salida y copiarla en otro lugar, con pestañas?

Este es un problema diferente. No se trata del shell sino del emulador de terminal, que convierte las pestañas en espacios en la salida. Muchos, pero no todos lo hacen.

Puede ser más fácil redirigir la salida con pestañas a un archivo y copiarla desde allí, o usarla unexpanden la salida para convertir espacios en pestañas. (Aunque tampoco puede saber con qué espacios en blanco eran las pestañas, y lo convertirá todo en pestañas, si es posible). Esto, por supuesto, dependerá de qué, exactamente, necesita hacer con la salida.

ilkkachu
fuente
Quise decir que cuando trato de seleccionar la salida, se trata como 5 espacios. Gracias por 'od -c' para verificar el contenido de la salida del comando.
Asu
1
@Asu Creo que él entiende eso. Su solución es obtener la salida a través de otros medios, ya que no se garantiza que el emulador de terminal deje pestañas como pestañas cuando las seleccione en la ventana. Sin embargo, acabo de comprobar y aunque putty, xterm y konsole convierten las pestañas en espacios, urxvt y gnome-terminal no lo hacen. Entonces, otra solución es cambiar terminales.
JoL
@JoL Sí, esa es la conclusión a la que acabo de llegar hace un minuto, y creo que sería la respuesta aceptada si a alguien le interesa publicarlo como tal ...
Asu
1
@Asu, sí, pensé en solucionar el problema manualmente. Sería molesto tener que hacer eso, pero luego admito que no me había dado cuenta de que hay emuladores de terminal que admiten copiar pestañas. ¡Cambiar a uno que sí lo haga, por supuesto, sería una solución mucho mejor!
ilkkachu
4

En printf '%s\t%s\n' foo bar, printfhace salida foo<TAB>bar<LF>.

f, o, b, aY rson caracteres gráficos de un solo ancho.

Al recibir esos caracteres, el terminal mostrará el glifo correspondiente y moverá el cursor una columna hacia la derecha, a menos que ya haya alcanzado el borde derecho de la pantalla (papel en máquinas de escribir originales), en cuyo caso puede alimentar una línea y regrese al borde izquierdo de la pantalla (ajuste) o simplemente descarte el carácter según el terminal y cómo se haya configurado.

<Tab>y <LF>son dos personajes de control . <LF>(también conocido como nueva línea) es el delimitador de línea en texto Unix, pero para terminales, solo alimenta una línea (mueve el cursor una posición hacia abajo). Por lo tanto, el controlador de terminal en el kernel realmente lo traducirá a <CR>(volver al borde izquierdo de la pantalla), <LF>(cursor hacia abajo) ( stty onlcrgeneralmente activado por defecto).

<Tab> le dice al terminal que mueva el cursor a la siguiente parada de tabulación (que en la mayoría de los terminales están separados por 8 posiciones por defecto, pero también se puede configurar para que se configure en cualquier lugar) sin llenar el espacio con espacios en blanco.

Entonces, si esos caracteres se envían a una terminal con tabulaciones cada 8 columnas mientras el cursor está al comienzo de una línea vacía, eso dará como resultado:

foo     bar

impreso en la pantalla en esa línea. Si se envían mientras el cursor está en la tercera posición en una línea que contiene xxxxyyyyzzzz, eso dará como resultado:

xxfooyyybarz

En terminales que no admiten tabulación, el controlador de terminal se puede configurar para traducir esas pestañas a secuencias de espacios. ( stty tab3)

El carácter SPC, en las máquinas de escribir telescópicas originales, movería el cursor hacia la derecha, mientras que la tecla de retroceso ( \b) lo movería hacia la izquierda. Ahora en las terminales modernas, SPC se mueve hacia la derecha y también borra (escribe un carácter de espacio como es de esperar). Entonces el colgante de \btenía que ser algo más nuevo que ASCII. En la mayoría de los terminales modernos, en realidad es una secuencia de caracteres: <Esc>, [, C.

Hay más secuencias de escape para mover npersonajes a la izquierda, derecha, arriba, abajo o en cualquier posición de la pantalla. Hay otras secuencias de escape para borrar (llenar con espacios en blanco) partes de líneas o regiones de la pantalla, etc.

Esas secuencias se utilizan normalmente por aplicaciones visuales como vi, lynx, mutt, dialogdonde el texto está escrito en posiciones arbitrarias en la pantalla.

Ahora, todos los emuladores de terminal X11 y algunos otros no X11 como GNU le screenpermiten seleccionar áreas de la pantalla para copiar y pegar. Cuando selecciona una parte de lo que ve en el vieditor, no desea copiar todas las secuencias de escape que se han utilizado para producir esa salida. Desea seleccionar el texto que ve allí.

Por ejemplo, si ejecutas:

printf 'abC\rAC\bB\t\e[C\b\bD\n'

Que simula una sesión de editor en la que ingresa abC, vuelve al principio, reemplaza abcon AC, Ccon B, mueve a la siguiente tabulación, luego una columna más a la derecha, luego dos columnas a la izquierda, luego ingresa D.

Lo ves:

ABC    D

Es decir, ABCun espacio de 4 columnas y D.

Si selecciona eso con el mouse en xtermo putty, se almacenarán en la selección ABC, 4 caracteres de espacio y D, no abC<CR>AC<BS>B<Tab><Esc>[C<BS><BS>D.

Lo que termina en la selección es lo que ha sido enviado printfpero procesado posteriormente tanto por el controlador de terminal como por el emulador de terminal.

Para otros tipos de transformación, vea el <U+0065><U+0301>( eseguido de un acento agudo combinado) cambiado a <U+00E9>( éla forma precompuesta) por xterm.

O echo abceso termina siendo traducido ABCpor el controlador de terminal antes de enviarlo a la terminal después de a stty olcuc.

Ahora, <Tab>like <LF>es uno de esos pocos caracteres de control que a veces se encuentran en los archivos de texto (también <CR>en los archivos de texto MSDOS y, a veces, <FF>en el salto de página).

Por lo tanto, algunos emuladores de terminal eligen copiarlos cuando sea posible en los búferes de copiar y pegar para preservarlos (generalmente ese no es el caso <CR>ni tampoco <LF>).

Por ejemplo, en terminales basados ​​en VTE como gnome-terminal, puede ver que, cuando selecciona la salida de printf 'a\tb\n'en una línea vacía, en gnome-terminalrealidad se almacena a\tben la selección X11 en lugar de a7 espacios y b.

Sin embargo, para la producción de printf 'a\t\bb\n', almacena a, 6 y espacios b, y para printf 'a\r\tb\n', a, 7 y espacios b.

Hay otros casos en los que los terminales intentarán copiar la entrada real, como cuando selecciona dos líneas después de ejecutar printf 'a \nb\n'donde se preservará ese espacio final invisible. O cuando se seleccionan dos líneas no incluye un carácter LF cuando las dos líneas resultan del ajuste en el margen derecho.

Ahora, si desea almacenar la salida de printfen el CLIPBOARD X11select, lo mejor es hacerlo directamente como:

printf 'foo\tbar\n' | xclip -sel c

Tenga en cuenta que cuando pega eso en la xtermmayoría de los otros terminales, en xtermrealidad lo reemplaza \ncon \rporque ese es el carácter que xtermenvía cuando presiona Enter(y el controlador del terminal puede volver a traducirlo \n).

Stéphane Chazelas
fuente
Esto es muy perspicaz, gracias. He probado la solución xclip y funciona. Pero no hace exactamente lo que tenía en mente y requiere X11. Puede ser útil en algún momento, ¡gracias!
Asu
@Asu, X11es lo que maneja la selección de copiar y pegar en emuladores de terminal como xtermo puttyen Unix. Otros emuladores de terminal pueden tener su propio mecanismo de copiar y pegar y formas de almacenar contenido arbitrario allí, como los comandos readbuf y registeren la pantalla GNU.
Stéphane Chazelas