Come allineare l'elenco a un carattere specifico?


13

Esiste un comando o una serie di comandi che posso usare per allineare orizzontalmente righe di testo a un carattere arbitrario? Ad esempio, con un elenco di indirizzi e-mail l'output produrrebbe un file di testo con tutti i caratteri "@" allineati verticalmente.

Per avere successo, credo che un numero variabile di spazi vuoti debba essere aggiunto all'inizio della maggior parte delle righe. Non voglio colonne separate poiché richiedono uno sforzo maggiore per leggere (ad esempio column -t -s "@" < file.txt).

Prima:

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

Dopo:

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

In altre parole: posso specificare un carattere come punto di ancoraggio, attorno al quale il testo circostante è centrato orizzontalmente? Il mio caso d'uso per questo è indirizzi e-mail, per renderli più facili da scansionare visivamente.


1
Cosa dovrebbe succedere se ci sono più @simboli?
Zeta,

Buona domanda, i @simboli multipli non dovrebbero essere un problema con gli indirizzi e-mail ma un utente dovrebbe essere in grado di selezionare quale istanza di un carattere per riga deve essere l'ancora attorno al quale è centrato l'altro testo.
Tom Brossman,

1
Sono @consentiti più simboli negli indirizzi e-mail, ad es tom"@brossmann"@example.com. Ecco perché ho chiesto cosa dovrebbe succedere se ce ne sono più@ simboli :).
Zeta,

@Zeta @Non sono ammessi più simboli in una varietà di servizi di posta elettronica. È del tutto ragionevole aspettarsi email "normali" che si adattano a uno standard più rigoroso di quello "reale", a meno che non si tratti di input utente non filtrato, nel qual caso è più probabile che si tratti di linee senza @.
Fondi Monica's Lawsuit

Risposte:


3

NO Awk. Solo sede column:

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

Produzione:

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

Ora, a cui penso, questo è quasi lo stesso della soluzione Sundeep, sembra più breve / ha meno chiamate sede presume che ciò @avvenga solo una volta su ogni linea.


1
Può essere ancora più breve:column -ts@ input.txt | sed -r 's/([^ ]+)( *)\s\s/\2\1@/'
MiniMax il

11

Nella sua forma più semplice, potresti semplicemente stampare il primo campo in una larghezza di campo adeguatamente grande, ad es

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

AFAIK qualsiasi metodo che non presupponga una larghezza di campo massima specifica richiederà la conservazione del file in memoria o l'esecuzione di due passaggi.


buono, per ottenere la lunghezza si può anche usare cw=$(cut -d@ -f1 file | wc -L)e poiawk -v w="$cw" 'BEGIN{OFS=FS="@"} {$1 = sprintf("%*s", w, $1)} 1'
Sundeep

Provando questo contro un elenco di 328 indirizzi, dieci mancano in qualche modo dall'output (ora 318 righe). Per chiarezza, ho corso 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. Ha formattato bene il resto, ma mancano alcuni dati.
Tom Brossman,

1
@TomBrossman grazie Ho appena realizzato che ha un difetto piuttosto grave - non gestirà i campi con nomi identici - lo eliminerò
Steeldriver il

Lo stesso risultato, ma in modo più concisoawk -F@ '{printf "%12s@%s\n", $1, $2}' input.txt
MiniMax il

6

soluzione hacky, assume molto sul testo di input

$ # 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 soluzione Python rapida che utilizza la lunghezza di imbottitura più corta possibile che allinea a destra tutte le stringhe a sinistra del separatore:

#!/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

Un'altra soluzione GNU awk+ column:

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

Il risultato:

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

Potresti aggiungere qualcosa su come funziona?
Joe,

2

Questo può funzionare anche con la manipolazione di stringhe 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

Il risultato:

   123@example.com
456789@example.net
 01234@something-else.com
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.