Quel serait le plus proche d'un moyen portable pour obtenir la largeur d'affichage (sur un terminal au moins (celui qui affiche les caractères dans les paramètres régionaux actuels avec la bonne largeur)) d'une chaîne de caractères à partir d'un script shell.
Je m'intéresse principalement à la largeur des caractères non contrôlables, mais les solutions qui prennent en compte les caractères de contrôle comme le retour arrière, le retour chariot, la tabulation horizontale sont également les bienvenues.
En d'autres termes, je recherche une API shell autour de la wcswidth()
fonction POSIX.
Cette commande devrait retourner:
$ that-command 'unix' # 4 fullwidth characters
8
$ that-command 'Stéphane' # 9 characters, one of which zero-width
8
$ that-command 'もで 諤奯ゞ' # 5 double-width Japanese characters and a space
11
On pourrait utiliser ksh93
l » printf '%<n>Ls'
qui prend en compte la largeur des caractères pour le rembourrage à <n>
colonnes, ou la col
commande (avec , par exemple printf '++%s\b\b--\n' <character> | col -b
) pour essayer de déduire que, il y a un texte :: charwidth le perl
module au moins, mais sont là des approches plus directes ou portables.
C'est plus ou moins un suivi de cette autre question qui concernait l'affichage du texte à droite de l'écran pour lequel vous auriez besoin de ces informations avant d'afficher le texte.
la source
Réponses:
Dans un émulateur de terminal, on pourrait utiliser le rapport de position du curseur pour obtenir des positions avant / après, par exemple, à partir de
et découvrez la largeur des caractères imprimés sur le terminal. Comme il s'agit d'une séquence de contrôle ECMA-48 (ainsi que VT100) prise en charge par presque tous les terminaux que vous êtes susceptible d'utiliser, elle est assez portable.
Pour référence
En fin de compte, l'émulateur de terminal détermine la largeur imprimable, en raison de ces facteurs:
wcswidth
seul ne dit pas comment les combinaisons de caractères sont gérées; POSIX ne mentionne pas cet aspect dans la description de cette fonction.wcswidth
seule (voir par exemple le Chapitre 2. Configuration de Cygwin ).xterm
par exemple, il est possible de sélectionner des caractères à double largeur pour les configurations nécessaires.Les API Shell appelant
wcswidth
sont prises en charge à des degrés divers:Celles-ci sont plus ou moins directes: simuler
wcswidth
dans le cas de Perl, appeler le runtime C depuis Ruby et Python. Vous pouvez même utiliser des malédictions, par exemple, à partir de Python (qui gérerait la combinaison de caractères):filter
fonction (pour les lignes simples)addstr
, en vérifiant les erreurs (au cas où il serait trop long), puis la position de finendwin
(qui ne devrait pas faire unrefresh
)Utiliser des malédictions pour la sortie (plutôt que de renvoyer les informations à un script ou d'appeler directement
tput
) effacerait toute la ligne (lafilter
limite à une ligne).la source
wcswidth()
a à dire sur quoi que ce soit.plink
, qui se déclencheTERM=xterm
même s'il ne répond à aucune séquence de contrôle. Mais je n'utilise pas de terminaux très exotiques.fold
est apparemment spécifié pour gérer les caractères multi-octets et de largeur étendue . Voici comment il doit gérer le retour arrière: le nombre actuel de largeur de ligne doit être décrémenté de un, bien que le nombre ne devienne jamais négatif. L'utilitaire de pliage ne doit pas insérer de <newline> immédiatement avant ou après un <backspace>, sauf si le caractère suivant a une largeur supérieure à 1 et entraînerait une largeur de ligne supérieure à la largeur. peutfold -w[num]
- être etpr +[num]
pourrait être associé en quelque sorte?Pour les chaînes d'une ligne, l'implémentation GNU de
wc
a une option-L
(aka--max-line-length
) qui fait exactement ce que vous recherchez (à l'exception des caractères de contrôle).la source
tab
(suppose que les tabulations s'arrêtent toutes les 8 colonnes).wc -L <<< 'unix'
→ 8,wc -L <<< 'Stéphane'
→ 8 etwc -L <<< 'もで 諤奯ゞ'
→ 11. PS Vous considérez «Stéphane» comme neuf caractères, dont un de largeur nulle? Cela me ressemble à huit caractères, dont l'un est multi-octet.Dans mon
.profile
, j'appelle un script pour déterminer la largeur d'une chaîne sur un terminal. Je l'utilise lorsque je me connecte sur la console d'une machine où je ne fais pas confiance à l'ensemble du systèmeLC_CTYPE
, ou lorsque je me connecte à distance et que je ne peux pas faire confianceLC_CTYPE
pour correspondre au côté distant. Mon script interroge le terminal, plutôt que d'appeler n'importe quelle bibliothèque, car c'était tout l'intérêt de mon cas d'utilisation: déterminer l'encodage du terminal.Ceci est fragile à plusieurs égards:
plink
méthode, et je l'ai résolu en utilisant laplinkx
méthode à la place .)Cela peut ou non correspondre à votre cas d'utilisation.
Le script renvoie la largeur dans son état de retour, tronquée à 100. Exemple d'utilisation:
la source
printf "\r%*s\r" $((${#text}+8)) " ";
à la fin decleanup
(l'ajout de 8 est arbitraire; il doit être suffisamment long pour couvrir la sortie plus large des anciens environnements locaux mais assez étroit pour éviter un retour à la ligne). Cela rend le test invisible, mais il suppose également que rien n'a été imprimé sur la ligne (ce qui est bien dans un~/.profile
)text="Éé"
et${#text}
vous donnera ensuite la largeur d'affichage (j'obtiens4
un terminal non unicode et2
un terminal compatible unicode). Ce n'est pas vrai pour bash.${#text}
ne vous donne pas la largeur d'affichage. Il vous donne le nombre de caractères dans l'encodage utilisé par les paramètres régionaux actuels. Ce qui est inutile pour mon objectif puisque je veux déterminer l'encodage du terminal. C'est utile si vous voulez la largeur d'affichage pour une autre raison, mais elle n'est pas précise car tous les caractères n'ont pas une unité de largeur. Par exemple, la combinaison des accents a une largeur de 0, et les idéogrammes chinois ont une largeur de 2.Eric Pruitt a écrit une implémentation impressionnante de
wcwidth()
etwcswidth()
dans Awk disponible sur wcwidth.awk . Il fournit principalement 4 fonctionsoù
wcscolumns()
tolère également les caractères non imprimables.J'ai ouvert un problème concernant la gestion des TAB car il
wcscolumns($'My sign is\t鼠鼠')
devrait être supérieur à 14. Mise à jour: Eric a ajouté la fonctionwcsexpand()
pour étendre les TAB aux espaces:la source
Pour développer les conseils sur les solutions possibles en utilisant
col
etksh93
dans ma question:Utiliser le
col
frombsdmainutils
sur Debian (peut ne pas fonctionner avec d'autrescol
implémentations), pour obtenir la largeur d'un seul caractère non-contrôle:Exemple:
Étendu pour une chaîne:
Utilisation
ksh93
deprintf '%Ls'
:Utilisation
perl
deText::CharWidth
:la source