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:

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

Après:

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

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.


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:

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

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.


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

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.


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

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

2

Une autre solution GNU awk+ column:

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

Le résultat:

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

Pourriez-vous ajouter un peu sur le fonctionnement de celui-ci?
Joe

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:

   123@example.com
456789@example.net
 01234@something-else.com
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.