J'ai un fichier texte qui a le format suivant et je veux ajouter une ligne verticale après ces lignes, suivie d'une augmentation des nombres:
c4-1 d e c
c d e c
e-2 f g2
e4 f g2
g8-4\( a-5 g f\) e4 c
g'8\( a g f\) e4 c
c-1 r c2
c4 r c2
J'obtiens la ligne et la numérotation avec le while-loop
:
#!/bin/bash
while read -r line; do
if [ -z "$line" ]; then
echo
continue
fi
n=$((++n)) \
&& grep -vE "^$|^%" <<< "$line" \
| sed 's/$/\ \|\ \%'$(("$n"))'/'
done < file
et obtenez une sortie comme:
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
maintenant je veux que l'addition soit alignée verticalement et obtienne une sortie comme celle-ci:
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
cela signifierait que je dois en quelque sorte obtenir la longueur de ligne de la ligne la plus longue (ici: 21 caractères) et la longueur de ligne de chaque ligne et ajouter la différence avec les espaces, comment pourrais-je y parvenir?
Vous pouvez imprimer les lignes sans alignement et formater la sortie avec column -t
et un caractère de délimiteur factice:
#!/bin/bash
while read -r line; do
if [ -z "$line" ]; then
echo
continue
fi
printf '%s@| %%%s\n' "$line" "$((++n))"
done < file | column -e -s'@' -t | sed 's/ |/|/'
Ici, j'ai ajouté un @
comme caractère fictif avant le |
indiquant la fin de la colonne. La commande sed
à la fin est utilisée pour supprimer un caractère d'espace supplémentaire avant le |
. Option -e
est nécessaire pour conserver les lignes vides dans la sortie.
Production:
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
Utilisation de awk
+ GNU wc
en supposant que tous les caractères de l'entrée sont à simple largeur:
$ awk -v f="$(wc -L < ip.txt)" '{printf "%-*s | %%%s\n", f, $0, NR}' ip.txt
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
Bash simple: fonctionne avec la version bash> = 4.0
#!/bin/bash
mapfile -t lines < file
max=0
for line in "${lines[@]}"; do
max=$(( ${#line} > max ? ${#line} : max ))
done
for i in "${!lines[@]}"; do
printf "%-*s | %%%d\n" $max "${lines[i]}" $((i+1))
done
Pour les anciennes versions de bash, remplacez mapfile par une boucle while-read: cela fonctionne avec la version 3.2
#!/bin/bash
lines=()
max=0
while IFS= read -r line || [[ -n "line" ]]; do
lines+=("$line")
max=$(( ${#line} > max ? ${#line} : max ))
done < file
for i in "${!lines[@]}"; do
printf "%-*s | %%%d\n" $max "${lines[i]}" $((i+1))
done
juste pour les enregistrements: (c'est horriblement lent, mais c'était ma première tentative en utilisant wc -L
)
va certainement chercher la réponse de @Freddy en utilisant column
!
#!/bin/bash
file="$1"
ll=$(wc -L < "$file")
while read -r line; do
if [ -z "$line" ]; then
echo
continue
fi
sl=$(wc -L <<< "$line")
if [ "$ll" = "$sl" ]; then
as=$(echo "$ll - $sl" | bc)
else
as=$(echo "$ll - $sl + 1" | bc)
fi
space=$(printf '\ %.0s' $(seq "$as") )
n=$((++n)) \
&& grep -vE "^$|^%" <<< "$line" \
| sed "s/$/$space\ \|\ \%$(printf "%s" "$n")/"
done < "$file"
bien qu'il fonctionne avec un espace supplémentaire:
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
En supposant qu'il n'y ait pas de @
caractères dans les données (remplacez simplement les deux @
utilisé ici avec un autre caractère dans ce cas):
$ awk -v OFS='@| %' '{ print $0, FNR }' file | column -s '@' -t
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
Cela utilise la chaîne @| %
comme séparateur de champ de sortie et imprime l'entrée suivie du numéro de ligne de chaque ligne (séparés par ce séparateur), puis utilise column
pour l'aligner sur le @
caractères (ceux-ci seront supprimés).
Si vous aimez sed
ou les expressions régulières maladroites, vous pouvez toujours numéroter les lignes avec cat -n
ou nl -b a
, puis déplacez les numéros de ligne à la fin de la ligne et insérez @| %
en utilisant sed
, avant d'appeler column
:
$ cat -n file | sed -E 's/^[[:blank:]]*([[:digit:]]+)[[:blank:]]*(.*)$/\2@| \%\1/' | column -s '@' -t
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
Utilisez awk
pour lire votre fichier deux fois, une fois pour déterminer la longueur maximale de ligne (m
) et une nouvelle fois pour formater les lignes à cette longueur. column
n'est pas utilisé ici (ou dans la dernière solution):
$ awk 'FNR==NR { m=(length>m?length:m); next } { printf("%-*s | %%%d\n", m, $0, FNR) }' file file
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8
Notez que le nom de fichier est donné deux fois sur la ligne de commande.
Comme ci-dessus, mais en stockant le fichier en mémoire sous forme de tableau (a
) et en l'imprimant en fonction de la longueur de ligne la plus longue à la fin. L'accès au disque bénéficie de la diminution de la consommation mémoire:
$ awk '{ a[FNR]=$0; m=(length>m?length:m) } END { for (i=1; i<=FNR; ++i) printf("%-*s | %%%d\n", m, a[i], i) }' file
c4-1 d e c | %1
c d e c | %2
e-2 f g2 | %3
e4 f g2 | %4
g8-4\( a-5 g f\) e4 c | %5
g'8\( a g f\) e4 c | %6
c-1 r c2 | %7
c4 r c2 | %8