¿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:

[email protected]
[email protected]
[email protected]

Después:

   [email protected]
[email protected]
 [email protected]

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.

Tom Brossman
fuente
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:

   [email protected]
[email protected]
 [email protected]

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.

wvxvw
fuente
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
         [email protected]
      [email protected]
       [email protected]

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.

conductor de acero
fuente
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/'
     [email protected]
  [email protected]
   [email protected]
Sundeep
fuente
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
David Foerster
fuente
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:

   [email protected]
[email protected]
 [email protected]
zero2cx
fuente