Comment aligner la liste sur un caractère spécifique?

13

Existe-t-il une commande ou un ensemble de commandes que je peux utiliser pour aligner horizontalement des lignes de texte sur un caractère arbitraire? Par exemple, avec une liste d'adresses e-mail, la sortie produira un fichier texte avec tous les caractères '@' alignés verticalement.

Pour réussir, je crois qu'un nombre variable d'espaces vides doit être ajouté au début de la plupart des lignes. Je ne veux pas de colonnes séparées car elles prennent plus d'effort à lire (par exemple, column -t -s "@" < file.txt).

Avant:

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

Après:

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

Autrement dit: puis-je spécifier qu'un caractère soit un point d'ancrage autour duquel le texte environnant est centré horizontalement? Mon cas d'utilisation est celui des adresses e-mail, pour les rendre plus faciles à scanner visuellement.

Tom Brossman
la source
1
Que doit-il se passer s'il y a plusieurs @symboles?
Zeta
Bonne question, plusieurs @symboles ne devraient pas être un problème avec les adresses e-mail, mais un utilisateur devrait être en mesure de sélectionner l'instance d'un caractère par ligne qui sera l'`` ancre '' autour de laquelle l'autre texte sera centré.
Tom Brossman
1
Plusieurs @symboles sont autorisés dans les adresses e-mail, par exemple tom"@brossmann"@example.com. C'est pourquoi j'ai demandé ce qui devrait arriver s'il y a plusieurs @symboles :).
Zeta
@Zeta Plusieurs @symboles ne sont pas autorisés dans une variété de services de messagerie. Il est tout à fait raisonnable de s'attendre à des e-mails "normaux" qui correspondent à un standard plus strict que le "vrai", à moins que vous n'ayez affaire à des entrées utilisateur brutes et non filtrées, auquel cas vous êtes plus susceptible de traiter des lignes sans réponse @.
Fund Monica's Lawsuit

Réponses:

3

NON Awk. Uniquement sedet column:

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

Production:

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

Maintenant, à mon avis, c'est presque la même que la solution de Sundeep, elle semble juste plus courte / a moins d'appels vers sed, et elle suppose également que @cela ne se produit qu'une seule fois sur chaque ligne.

wvxvw
la source
1
Il peut être encore plus court:column -ts@ input.txt | sed -r 's/([^ ]+)( *)\s\s/\2\1@/'
MiniMax
11

Au plus simple, vous pouvez simplement imprimer le premier champ dans une largeur de champ suffisamment grande, par exemple

awk -F@ 'BEGIN{OFS=FS} {$1 = sprintf("%12s", $1)} 1' file
         [email protected]
      [email protected]
       [email protected]

AFAIK toute méthode qui ne suppose pas une largeur de champ maximale spécifique nécessitera soit de conserver le fichier en mémoire, soit d'effectuer deux passes.

tournevis
la source
bon, pour obtenir la longueur, on peut également utiliser cw=$(cut -d@ -f1 file | wc -L)puisawk -v w="$cw" 'BEGIN{OFS=FS="@"} {$1 = sprintf("%*s", w, $1)} 1'
Sundeep
En testant cela contre une liste de 328 adresses, dix sont en quelque sorte manquantes dans la sortie (maintenant 318 lignes). Pour plus de clarté, j'ai couru 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. Il a bien formaté le reste, mais certaines données sont manquantes.
Tom Brossman
1
@TomBrossman merci Je viens de réaliser qu'il a un défaut assez sérieux - il ne gérera pas les champs de nom identiques - je vais le supprimer
steeldriver
Le même résultat, mais de manière plus conciseawk -F@ '{printf "%12s@%s\n", $1, $2}' input.txt
MiniMax
6

solution hacky, suppose beaucoup de choses sur le texte d'entrée

$ # 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
la source
4

Une solution Python rapide qui utilise la longueur de remplissage la plus courte possible qui aligne à droite toutes les chaînes à gauche du séparateur:

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

Usage:

python3 align-field.py < data.txt
David Foerster
la source
2

Cela peut également fonctionner avec la manipulation de chaînes Bash.

Script bash (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

Le résultat:

   [email protected]
[email protected]
 [email protected]
zero2cx
la source