¿Cómo alinear la lista a un personaje específico?


13

¿Hay algún comando o conjunto de comandos que pueda usar para alinear horizontalmente líneas de texto con un carácter arbitrario? Por ejemplo, con una lista de direcciones de correo electrónico, la salida produciría un archivo de texto con todos los caracteres '@' alineados verticalmente.

Para tener éxito, creo que se debe agregar un número variable de espacios vacíos al comienzo de la mayoría de las líneas. No quiero columnas separadas, ya que requieren más esfuerzo para leer (por ejemplo, column -t -s "@" < file.txt).

Antes de:

123@example.com
456789@example.net
01234@something-else.com

Después:

   123@example.com
456789@example.net
 01234@something-else.com

Dicho de otra manera: ¿puedo especificar que un carácter sea un punto de anclaje, alrededor del cual el texto circundante está centrado horizontalmente? Mi caso de uso para esto son las direcciones de correo electrónico, para que sean más fáciles de escanear visualmente.


1
¿Qué debería pasar si hay varios @símbolos?
Zeta

Buena pregunta, los @símbolos múltiples no deberían ser un problema con las direcciones de correo electrónico, pero un usuario debería poder seleccionar qué instancia de un carácter por línea será el 'ancla' alrededor del cual se centra el otro texto.
Tom Brossman

1
Se @permiten varios símbolos en las direcciones de correo electrónico, por ejemplo tom"@brossmann"@example.com. Por eso pregunté qué debería pasar si hay varios @símbolos :).
Zeta

@Zeta @No se permiten múltiples símbolos en una variedad de servicios de correo electrónico. Es completamente razonable esperar correos electrónicos "normales" que se ajusten a un estándar más estricto que el "real", a menos que se trate de una entrada de usuario sin filtrar, en cuyo caso es más probable que trate con líneas sin @.
Financia la demanda de Mónica el

Respuestas:


3

NO Awk. Solo sedy column:

column -ts@ file.txt | sed -E 's/([^ ]+)([ ]+) (.+)/\2\1@\3/'

Salida:

   123@example.com
456789@example.net
 01234@something-else.com

Ahora, en lo que pienso, esto es casi lo mismo que la solución de Sundeep ', solo parece más corto / tiene menos llamadas sed, y también supone que @sucede solo una vez en cada línea.


1
Puede ser aún más corto:column -ts@ input.txt | sed -r 's/([^ ]+)( *)\s\s/\2\1@/'
MiniMax

11

En su forma más simple, puede imprimir el primer campo en un ancho de campo adecuadamente grande, por ejemplo

awk -F@ 'BEGIN{OFS=FS} {$1 = sprintf("%12s", $1)} 1' file
         123@example.com
      456789@example.net
       01234@something-else.com

AFAIK cualquier método que no asuma un ancho de campo máximo específico requerirá mantener el archivo en la memoria o hacer dos pases.


bueno, para obtener longitud también se puede usar cw=$(cut -d@ -f1 file | wc -L)y luegoawk -v w="$cw" 'BEGIN{OFS=FS="@"} {$1 = sprintf("%*s", w, $1)} 1'
Sundeep

Al probar esto contra una lista de 328 direcciones, faltan de alguna manera diez de la salida (ahora 318 líneas). Para mayor claridad, corrí awk -F@ '{a[$1] = $2; w = length($1) > w? length($1) : w; next} END {for (i in a) printf("%*s%c%s\n", w, i, FS, a[i])}' INPUT-FILE.txt > OUT.txt. Formateó bien el resto, pero faltan algunos datos.
Tom Brossman

1
@TomBrossman, gracias. Me acabo de dar cuenta de que tiene un defecto bastante serio: no manejará campos de nombre idénticos. Voy a eliminarlo
Steeldriver

El mismo resultado, pero de manera más concisaawk -F@ '{printf "%12s@%s\n", $1, $2}' input.txt
MiniMax

6

solución hacky, supone mucho sobre el texto de entrada

$ # four commas to reduce chance of it affecting actual email address
$ sed 's/@/,,,,@/' ip.txt | column -t -s,,,,
123     @example.com
456789  @example.net
01234   @something-else.com

$ sed 's/@/,,,,@/' ip.txt | column -t -s,,,, | sed -E 's/^([^ ]+)( +)/\2\1/'
     123@example.com
  456789@example.net
   01234@something-else.com

4

Una solución rápida de Python que utiliza la longitud de relleno más corta posible que alinea a la derecha todas las cadenas a la izquierda del separador:

#!/usr/bin/env python3
import sys
fieldsep = '@'
records = [line.rstrip('\n').split(fieldsep, 1) for line in sys.stdin]
col1_len = max((len(r[0]) for r in records), default=0)
for r in records:
    print(r[0].rjust(col1_len), r[1], sep=fieldsep)

Uso:

python3 align-field.py < data.txt

2

Otra solución GNU awk+ column:

awk '{ split($0,a,/ +/,sep); printf "%*s@%s\n",length($1 sep[1])-2,$1,$2 }' <(column -ts'@' file)

La salida:

   123@example.com
456789@example.net
 01234@something-else.com

¿Podría agregar un poco sobre cómo funciona este?
Joe

2

Esto también puede funcionar con la manipulación de cadenas Bash.

Bash script (4.x):

#!/bin/bash

read -d '' -r -a data <"data.txt"

for ((pos=0, i=0; i<${#data[@]}; i++)); do
    locl=${data[$i]%@*}                         # The local-part.
    [[ ${#locl} -gt $pos ]] && pos=${#locl}     # Determine the lengthiest $locl.
done

for ((i=0; i<${#data[@]}; i++)); do
    email=${data[$i]}
    locl=${email%@*}                            # The local-part.
    domain=${email#*@}                          # The email domain.
    printf '%*s@%s\n' $pos $locl $domain        # Align $locl to the right, at $pos.
done

El resultado:

   123@example.com
456789@example.net
 01234@something-else.com
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.