Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 24/01/2011, à 23:29

FRUiT

[Résolu] Calcul de longueur d'une chaine

Bonjour,
Mon problème est :

Comment savoir la longueur réelle d'une chaine, une fois qu'elle a été interpretée par bash/sh ?

exemple :

\033[34mLes carottes\tsont \033(B\033[mgéantes

Une fois interpretée (echo -e par exemple), elle mesure 28 caractères (avec une tabulation de 8)

Pourtant (on va appeler cette chaine "STR") :

> wc -c <<<"$STR"
48
> wc -m <<<"$STR"
47
> echo ${#STR}
46
> awk '{print length()}' <<<"$STR"
46
> printf "%b" "$STR" | wc -m
36
> printf "%b" "$STR" | awk '{print length()}'
35
> STR2="$(printf "%b" "$STR")"; printf "%s\n" "${#STR2}"
35

J'ai même essayé avec des fichiers de stockage temporaire, ou d'écrire verticalement le texte pour compter les lignes, rien n'y fait c'est toujours à coté du résultat escompté.

Pour le moment je suis obligé de faire:

#!/bin/sh

STR="\033[34mLes carottes\tsont \033(B\033[mgéantes"
e="\033"
clip="((\\${e}|[[:cntrl:]])[\[\#][\?]?[0-9;]+?[ABCDfGHKsum]|(\\${e}|[[:cntrl:]])\(B(\\${e}|[[:cntrl:]])\[m)|[[:cntrl:]]"
cc=$(printf "%b" "$(printf "%s" "${STR}" | sed -r 's/\\t/    /g' | sed -r "s/${clip}//g")" | wc -m)
echo $cc
> strtest
28

C'est dégueu, et surtout, pas infaillible sad

Le pire de tout c'est les tabulations, va savoir par combien d'espaces elles seront remplacées une fois interpretées...

Difficulté supplémentaire, je souhaiterais vraiment y arriver en SH uniquement, et sans rien installer, que avec des outils par défaut comme ceux cités... sad

Si quelqu'un connait une solution miracle...

Dernière modification par FRUiT (Le 07/02/2011, à 10:07)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#2 Le 25/01/2011, à 11:48

Totor

Re : [Résolu] Calcul de longueur d'une chaine

Salut,

Normalement, col est fait pour supprimer toutes séquences de contrôle mais il semble ne pas le faire correctement ...
dommage !

Une autre solution serait d'afficher le texte, récupérer la position du curseur (je l'ai déjà fait mais faut que je retrouve le code !) puis d'effacer


mais c'est vraiment pas beau !


-- Lucid Lynx --

Hors ligne

#3 Le 25/01/2011, à 12:59

Totor

Re : [Résolu] Calcul de longueur d'une chaine

Re,

Voilà ce qu'il te faut :

#!/bin/bash

function getLen()
{
    stty -echo
    echo -en "\033[s${1}\033[6n"
    read -d R
    echo -en '\033[u\033[K'
    stty echo
    field=${REPLY#??}

    rowPos=${field%;*}
    colPos=${field#*;}

    echo $((colPos-1))
}

Exemple :

#FRUiT
$ getLen "\033[34mLes carottes\tsont \033(B\033[mgéantes"
28
#ehmicky
$ getLen "日日日日日日日日日日"
20

par contre, je n'ai pas poussé les tests !

Dernière modification par Totor (Le 25/01/2011, à 15:42)


-- Lucid Lynx --

Hors ligne

#4 Le 25/01/2011, à 15:13

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Je mets du temps à répondre, je dois tester, mais j'arrive smile Merci pour ces infos.


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#5 Le 25/01/2011, à 15:29

Totor

Re : [Résolu] Calcul de longueur d'une chaine

c pas le top comme solution, il y a des problèmes d'actualisation de l'écran... suivant la position du curseur, il y a un scroll qui se produit où l'on voit apparaitre le texte hmm
(j'ai interverti 2/3 lignes pour essayer de limiter cet effet)

Il y a également un bug ...
exemple :

printf "abcdef"
getLen "n'importe quoi"

je te laisse découvrir le résultat

Dernière modification par Totor (Le 25/01/2011, à 15:31)


-- Lucid Lynx --

Hors ligne

#6 Le 25/01/2011, à 17:38

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Premier constat :

printf "$(getLen "foo")"

Ou

cc=$(getLen "foo")

Font planter ma console obligé de la tuer. Apparemment ça ne supporte pas l'invocation dans un sous-shell (que ce soit dans le même script que la fonction, ou dans un autre, ou même directement en console), ce qui est ennuyeux pour ce que je veux en faire.

En tout cas ça reste instructif, et j'ai testé très rapidement pour le moment,

Je continue mes tests.

Dernière modification par FRUiT (Le 25/01/2011, à 17:51)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#7 Le 25/01/2011, à 17:39

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Totor a écrit :

Il y a également un bug ...
exemple :

printf "abcdef"
getLen "n'importe quoi"

je te laisse découvrir le résultat

Ah oui marrant (enfin... je me comprends).


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#8 Le 25/01/2011, à 17:44

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Pour ce qui est de col, bon ben à part le col -x qui peut servir pour les tabulations ça ne sert pas à grand chose. En tout cas je connaissais pas je vais tester de manière plus approfondie. Il y a aussi expand ou expand -i pour les tabulations, mais j'arrive TOUJOURS à trouver une chaine de caractères qui fausse le résultat.

Dernière modification par FRUiT (Le 25/01/2011, à 17:45)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#9 Le 25/01/2011, à 18:01

ehmicky

Re : [Résolu] Calcul de longueur d'une chaine

Tu veux dire une chaîne de caractères qui fait qu'expand ne fonctionne pas comme prévu ?


Stego++, bibliothèque libre de stéganographie (avec cryptographie), à venir !
Besoin de votre aide :
Stats sur les compilateurs C++ les plus utilisés
Comment utiliser les archetypes C++ ?

Hors ligne

#10 Le 25/01/2011, à 18:51

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Si expand marche, mais le comptage des caractères pas toujours.
soit le script 'putt' :

#!/bin/sh

: ${COLUMNS:=80}
STR="${*}"
e="\033"
clip="((\\${e}|[[:cntrl:]])[\[\#][\?]?[0-9;]+?[ABCDfGHKsum]|(\\${e}|[[:cntrl:]])\(B(\\${e}|[[:cntrl:]])\[m)|[[:cntrl:]]"
cc=$(printf "%b" "$(printf "%s" "${STR}" | sed -r "s/${clip}//g")" | expand | wc -m)
echo $cc
printf "%b\n" "${e}[$(($COLUMNS-$cc))G${STR}" | expand

essaye :

putt "foo bar"
putt "$(tput setaf 4)foo \033[31mbar"
putt "$(tput setaf 4)foo\t\033[31mbar"

Quand la tabulations apparait, l'alignement ne se fait plus.

Dernière modification par FRUiT (Le 25/01/2011, à 18:53)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#11 Le 25/01/2011, à 21:46

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Bon en fait je crois que tout ça est pas très clair alors je vais reprendre depuis le début et tenter d'expliquer exactement ce que j'attends.

Nouveau sous linux je me suis dit tiens pour apprendre le script et ses mécanismes je vais imiter les messages des démons :

Cpufreq bla bla
 * CPU O...                                     [ OK ]

le [ OK ] étant aligné à droite.

Je me suis également dit, que pour compliquer, j'allais aligner à droite ET le message ET son 'étiquette' (ou label) et ainsi faire une sorte de mini-messagebus pour ma console.

Ca me parraissait assez simple pour découvrir quelques notions de base lol.

Après quelques tests sur des chaines très simples je me suis vite rendu compte de la difficulté à aligner n'importe quel message à droite.

Pour le moment j'en suis rendu la :

#!/bin/sh

#set -x

put_formated () 
{
  [ "$1" ] || return 0

  #: ${COLUMNS:=$(stty -a 2>/dev/null | awk -F'[ ;]' 'NR==1 {print $9}')}
  : ${COLUMNS:=$(tput cols || printf "%s" "80")}
  : ${e:="\033"}                ; n="$(tput sgr0 || printf "%s" "${e}[m")"
  : ${M_PREFIX:=""}             ; r="$(tput bel  || printf "%s" "\007")"
  : ${M_BRACKETS:="[]"}         ; bl="${M_BRACKETS%?}"  br="${M_BRACKETS#?}"
  clip="((\\${e}|[[:cntrl:]])[\[\#][\?]?[0-9;]+?[ABCDfGHKsum]|(\\${e}|[[:cntrl:]])\(B(\\${e}|[[:cntrl:]])\[m)|[[:cntrl:]]"

  if [ ! "^${1#-}" = "^${1}" ]; then [ "$2" ] && {
    while getopts ":abdehilnoqry" m; do case ${m} in
        e) lb="fail"  cl="0;31"             ;;
        a) lb=" !! "  cl="0;33"             ;;
        d) lb="done"  cl="0;32"             ;;
        q) lb=" ?? "  cl="0;32"             ;;
        h) lb="help"  cl="1;30"             ;;
        r) lb="ring"  cl="1;35"             ;;
      y|o) lb=" OK "  cl="0;32"             ;;
        n) lb="info"  bs="${e}[1A\r${e}[K"  ;;
        l) M_LEFT=1                         ;;
        b) al=${r}                          ;;
        *) lb="info"                        ;;
    esac ; done ; shift $(($OPTIND-1)) ; } || return 0
    STR="${1}" lb="${M_LABEL:=${2:-${lb}}}"
    cl="${e}[${M_LABEL_COLOR:-${3:-${cl:-"0;34"}}}m"
    cb="${e}[${M_BRACKETS_COLOR:-${4:-${cb:-"0;0"}}}m"
  else
    STR="${*}"
  fi
  [ "${lb}" ] && lb="$(printf "%04b" "${lb}" | expand | sed -r "s/${clip}//g")" || { bl= br=  ; }
  [ "${M_PREFIX}" ] && M_PREFIX="${M_PREFIX}${n} "
  STR="$(printf "%s" "${M_PREFIX}${STR}" | sed -r 's/\\t/    /g')"
  cc=$(printf "%b" "$(printf "%s" "${STR}" | sed -r "s/${clip}//g")" | tail -n 1 | wc -m)
  co=$(($COLUMNS-${#lb}-${#bl}-${#br})); lr="${e}[${co}G"

  [ $co -le $(($cc%$COLUMNS)) ] && { M_LEFT=1 STR="${STR}\012" ; } || co=$(($co-($cc%$COLUMNS)+$(printf "%s" "$STR" | egrep -o '\\f' | wc -l)))
  [ $COLUMNS -lt $cc -o $(printf "%b" "${STR}" | wc -l) -ne 0 ] && M_LEFT=1
  [ "${M_LEFT}" ] || STR="${e}[$((${co}-${#bl}))G${STR}"

  STR="${bs}${n}${STR}${lr}${n}${cb}${bl}${n}${cl}${lb}${n}${cb}${br}${n}${al}"
  printf "%b\n" "${STR}"
}

put_formated "${@}"

Ce script, que j'ai baptisé 'putf' est censé aligner a droite n'importe quelle chaine, à moins que la chaine ne s'affiche sur plusieurs lignes auquel cas il aligne le message à gauche et l'étiquette à droite. Je m'en sers pour des choses variées, telles que des messages d'erreur, des informations colorées, des chemins, etc.

Il s'utilise comme ceci :

# Big up Sputnik à qui j'ai pompé tout ce qui est au dessus du 'case'

SEND="$(which putf)"

# Bash's builtin cd override
function cd ()
{
  shopt -s extglob
  [ -f "$1" ] && LOC="${1%/*}" || LOC="$1"
  if [ "$LOC" ]; then
    if [[ "$LOC" == "-+([0-9])" ]]; then
      index=$(tr -d '-' <<< "$LOC")
      builtin cd "${DIRSTACK[index]}" &>/dev/null
    else
      pushd "$LOC" &>/dev/null
    fi
  fi
  case $? in
    0) M_PREFIX="Entering" ${SEND:-"sendf"} -n "\033[4;37m$(\pwd)\033[m" "$(\ls -1a | awk 'END {print (NR-2)}')" ;;
    *) return $? ;;
  esac
}
alias sudo='sudo -i -p "$(${SEND} -i "Password request" "sudo" 31 0)"'
alias d='${SEND} -n "\033[m $(date --rfc-2822)" "$(d=$(date "+%j");echo ${d#0})" "34" "0"'
putf -i foo
putf -eb foo
putf -i "foo" "bar" "32" "31"
putf -nb "\033[34mLes carottes\tsont \033(B\033[mgéantes" "test" 35
while :; do putf -n "$(date)"; done
# Sleep to RAM
mem ()
{
  ${SEND:-"sendf"} -n "Suspending to" "ram"
  if [ "$(tty | grep tty)" ]; then
    echo mem | \sudo tee /sys/power/state >/dev/null
  else
    qdbus org.kde.powerdevil /modules/powerdevil suspend 2 >/dev/null 2>&1
  fi
}
# Pour les messages d'erreur (plusieurs lignes), on peut tester
putf -e "$(tar 2>&1)"
putf -e "$(sed 2>&1)"
> check bat
                                      BAT(1): 0: charging [ 95%]
> check fs
                        Filesystem check sheduled shortly [   4]
M_BRACKETS="{}" putf -i "foo"
M_BRACKETS_COLOR="1;34" putf -i "foo"

etc...

En fait je redirige même ponctuellement mes erreurs de bash dedans (encore merci totor qui comprendra sans doute mieux pourquoi le fifo ne me plaisait pas)

Mais les calculs sont par exemple faussés par :
* Les tabulations ne sont pas épandues en espaces, mais remplacées par 4 espaces (beurk). Si j'essaye avec expand, ça devient le bordel.
* La regexp géante (que je dois réécrire totalement, là c'est le résultats d'ajouts et ajustements successifs au fil de mes connaisssances en séquences d'échappement) peut ne pas matcher toutes les séquences d'échapement (fournies directement ou éventuellement par tput).
* Etc...

La solution que j'ai trouvé, c'est d'aligner indépendemment le message et son étiquette ($lr pour l'étiquette, et $co pour le message) de sorte que même si le message se trouvait mal aligné à droite, l'étiquette, elle, le serait toujours (car censée ne mesurer que 4 caractères max, et être d'une consistance plus basique).

Cette fonction doit être courte (de sorte que je puisse l'intégrer dans un autre script par exemple), exécutée rapidement, utiliser le moins d'outils possibles et pouvoir aligner n'importe quoi, vu que je m'en sers activement pour tout et rien (voir les exemples au dessus), alors je cherche constamment comment l'améliorer. Par exemple pour le moment je trouve qu'il y a beaucoup trop de 'printf' dedans. Le topic de ehmicky sur la longueur des caractères m'a décidé à créer ce topic, pour voir si d'autres auraient de bonnes idées que je n'aurais pas eues...

En gros ça marche pas mal mais j'aurais aimé pouvoir écrire fièrement "et oui ma fonction elle aligne N'IMP quelle string !!" mais j'en suis encore loin. alors je serais ravi si quelqu'un trouve mieux ET en moins de lignes.

Dernière modification par FRUiT (Le 27/01/2011, à 13:12)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#12 Le 26/01/2011, à 13:27

Totor

Re : [Résolu] Calcul de longueur d'une chaine

salut,
j'ai une autre solution mais je ne peux pas la poster aujourd'hui. Enfin, c pas facile car j'i juste mon smartphone hmm Demain !

Dernière modification par Totor (Le 26/01/2011, à 13:29)


-- Lucid Lynx --

Hors ligne

#13 Le 27/01/2011, à 09:35

Totor

Re : [Résolu] Calcul de longueur d'une chaine

Salut,

FRUiT a écrit :

Premier constat :

printf "$(getLen "foo")"

Ou

cc=$(getLen "foo")

Font planter ma console obligé de la tuer. [...]

En fait, ta console n'est pas plantée. Elle est bloquée par le "read -d R" qui attend une saisie clavier. Or l'option -echo ayant désactivée l'affichage de toute saisie clavier, tu as l'impression que ta console est plantée. Il suffit de taper d'effectuer un CTRL+C puis de taper à l'aveugle stty echo et tout revient à la normale.

Ce comportement est liée au détachement du terminal au processus par l'exécution d'un processus indépendant (utilisation de $(...))

Concernant ce problème de tabulation : le nombre d'espaces qui sont remplacés pour une tabulation dépend de l'emplacement d'affichage du texte. Il faut voire le terminal comme étant divisé en colonne dont la largeur est une tabulation. Le caractère \t permet d'aligner au début de la prochaine colonne. Donc si le curseur est positionné en début de colonne, une tabulation sera remplacée par 8 espaces. Si le curseur se trouve 2 "cases" avant la prochaine colonne, la tabulation sera remplacée par 2 espaces....
--> La longueur d'une chaine contenant une tabulation dépend de la position du curseur.

Le plus simple serait donc de remplacer toute tabulation par le nombre d'espaces que tu souhaites avant de procéder au calcul du nombre de caractères et à son affichage.

Pour revenir à une solution :
Ma première solution serait de ne pas renvoyer le résultat mais de l'affecter à une variable. Cela évite donc d'utiliser le script dans un processus différent :

#!/bin/bash
function getLen()
{
    # la longueur de la chaine dépend de la position du curseur et des tabulations, on convertie donc ces derniers en espace (ceci devrait être fait en amont de la fonction)
    myString="$(printf "$1"|expand)"
    # Sauvegarde de la position du curseur et récupération de la position actuelle du curseur
    stty -echo
    printf "\033[s\033[6n"
    read -d R 
    field=${REPLY#??}
    rowPosInitiale=${field%;*}
    colPosInitiale=${field#*;}
    
    # affichage du texte pour récupérer la position futur du curseur
    echo -en "${myString}\033[6n"
    read -d R 
    # on reposition le curseur où il se trouvait avant d'afficher le texte et on efface le texte affiché
    printf  "\033[u\033[K" 
    stty echo
    field=${REPLY#??}
    rowPos=${field%;*}
    colPos=${field#*;}
    
    longueur=$((colPos-colPosInitiale))
}

Le nombre de caractères est donc contenu dans la variable longueur après utilisation de la fonction.

getLen "\033[34mLes carottes\tsont \033(B\033[mgéantes"
echo "La longueur de la chaine est : ${longueur}"

2nde solution : utilisation d'un fd pour récupérer les infos :

#!/bin/bash
function getLen()
{
    # la longueur de la chaine dépend de la position du curseur et des tabulations, on convertie donc ces derniers en espace
    myString="$(printf "$1"|expand)"
    # Sauvegarde de la position du curseur et récupération de la position actuelle du curseur
    stty -echo
    printf "\033[s\033[6n" >&2
    read -d R -u 2
    field=${REPLY#??}
    rowPosInitiale=${field%;*}
    colPosInitiale=${field#*;}
    
    # affichage du texte pour récupérer la position futur du curseur
    echo -en "${myString}\033[6n" >&2
    read -d R -u 2
    # on reposition le curseur où il se trouvait avant d'afficher le texte et on efface le texte affiché
    printf  "\033[u\033[K" 
    stty echo
    field=${REPLY#??}
    rowPos=${field%;*}
    colPos=${field#*;}
    
    echo $((colPos-colPosInitiale))
}

Cela ne fait bloque pas l'exécution du script mais les résultats sont étranges lors de l'affichage de la 1ère ligne après exécution de la fonction et je n'ai pas pris le temps de rechercher pourquoi ...

En tout cas, la solution d'affecter le résultat dans une variable fonctionne.

Dernière modification par Totor (Le 27/01/2011, à 10:24)


-- Lucid Lynx --

Hors ligne

#14 Le 28/01/2011, à 19:50

T10-9D

Re : [Résolu] Calcul de longueur d'une chaine

pour la longueur de ta chaine :

aq=`printf "\033[34mLes carottes\tsont \033(B\033[grantes\n" |wc -c` 
## le ` s'obtient avec AltGr+7. cmd identique aq=$(cmd) ##
echo $aq

Pour afficher le text sur la droite : (quelque soit la taille de ton terminal)

printf "%${COLUMNS}s" "je suis  sur le bord D"

pour afficher du text à gauche et sur la mm ligne du test a droite

var1="action en cours"
var2="[etat]"
sm=$var1$var2
sm=`echo $sm | wc -m`
printf "$var1%$((COLUMNS-sm+1))s$var2\n"

j'espère avoir correctement répondu à tes attentes.

Hors ligne

#15 Le 28/01/2011, à 20:14

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Non smile

T10-9D a écrit :

pour la longueur de ta chaine :

aq=`printf "\033[34mLes carottes\tsont \033(B\033[grantes\n" |wc -c` 
## le ` s'obtient avec AltGr+7. cmd identique aq=$(cmd) ##
echo $aq

Hum je suppose que tu as essayé ? Comme je l'ai écrit plus haut ça ne donne pas 28 caractères mais 36... (de plus les backticks ` sont obsolètes il faut leur préférer la notation $(commande) ).

Dès lors :

T10-9D a écrit :

Pour afficher le text sur la droite : (quelque soit la taille de ton terminal)

printf "%${COLUMNS}s" "je suis  sur le bord D"

Ne marche pas avec mon exemple des carottes... Seulement avec une chaine ultra-basique comme la tienne. Dès qu'un caractère spécial apparait l'alignement est faussé. D'autant que %s n'interprète pas les caractères spéciaux (et les séquences d'échappement) et je rappelle que je veux la longueur une fois la ligne interpretée (dans le but de l'affichée interpretée)... %b à la rigueur mais.bon ça ne marchera pas mieux avec l'exemple tu peux essayer.

T10-9D a écrit :

pour afficher du text à gauche et sur la mm ligne du test a droite

var1="action en cours"
var2="[etat]"
sm=$var1$var2
sm=`echo $sm | wc -m`
printf "$var1%$((COLUMNS-sm+1))s$var2\n"

j'espère avoir correctement répondu à tes attentes.

Pas mieux.

En tout cas merci pour ton aide et d'avoir essayé.

Dernière modification par FRUiT (Le 28/01/2011, à 21:04)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#16 Le 31/01/2011, à 21:47

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Bonsoir tout le monde, bonsoir Totor.

Bon j'ai testé avec la methode no1. Soit « put2 » tout pareil que putf sauf que j'ai inséré ta methode de calcul :

#!/bin/bash

#set -x

getL ()
{
  stty -echo
  printf "%b" "\033[s\r$(tail -n 1 <<<"${*}")\033[6n"
  read -d R
  printf  "\033[u\033[K"
  stty echo
  SIZE="${REPLY//[^0-9;]}"
  [ "${SIZE}" ] && cc=$((${SIZE#*;}-1))
  [ $cc -eq $cc 2>/dev/null ]
}

put_formated () 
{
  [ "$1" ] || return 0

  : ${COLUMNS:=$(tput cols || printf "%s" "80")}
  : ${e:="\033"}                ; n="$(tput sgr0 || printf "%s" "${e}[m")"
  : ${M_PREFIX:=""}             ; r="$(tput bel  || printf "%s" "\007")"
  : ${M_BRACKETS:="[]"}         ; bl="${M_BRACKETS%?}"  br="${M_BRACKETS#?}"

  if [ ! "^${1#-}" = "^${1}" ]; then [ "$2" ] && {
    while getopts ":abdehilnoqry" m; do case ${m} in
        e) lb="fail"  cl="0;31"             ;;
        a) lb=" !! "  cl="0;33"             ;;
        d) lb="done"  cl="0;32"             ;;
        q) lb=" ?? "  cl="0;32"             ;;
        h) lb="help"  cl="1;30"             ;;
        r) lb="ring"  cl="1;35"             ;;
      y|o) lb=" OK "  cl="0;32"             ;;
        n) lb="info"  bs="${e}[1A\r${e}[K"  ;;
        l) M_LEFT=1                         ;;
        b) al=${r}                          ;;
        *) lb="info"                        ;;
    esac ; done ; shift $(($OPTIND-1)) ; } || return 0
    STR="${1}" lb="${M_LABEL:-${2:-${lb}}}"
    cl="${e}[${M_LABEL_COLOR:-${3:-${cl:-"0;34"}}}m"
    cb="${e}[${M_BRACKETS_COLOR:-${4:-${cb:-"0;0"}}}m"
  else
    STR="${*}"
  fi
  [ "${lb}" ] && lb="$(printf "%04b" "${lb}")" || { bl= br= ; }
  [ "${M_PREFIX}" ] && STR="${M_PREFIX}${n} ${STR}"
  getL "${lb}"; co=$(($COLUMNS-${cc}-${#bl}-${#br})); lr="${e}[${co}G"
  getL "${STR}" || return 0

  [ $co -le $(($cc%$COLUMNS)) ] && { M_LEFT=1 STR="${STR}\012" ; } || co=$(($co-($cc%$COLUMNS)))
  [ $COLUMNS -lt $cc -o $(printf "%b" "${STR}" | wc -l) -ne 0 ] && M_LEFT=1
  [ "${M_LEFT}" ] || STR="${e}[$((${co}-${#bl}))G${STR}"

  STR="${bs}${n}${STR}${lr}${n}${cb}${bl}${n}${cl}${lb}${n}${cb}${br}${n}${al}"
  printf "%b\n" "${STR}"
}

put_formated "${@}"

Je me suis permis de réduire un peu en enlevant un read -d et en assignant pas la hauteur.

Je suis satisfait du résultat, pas besoin de regex géante, ça cacule vraiment très bien (même pour une chaine contenant des « \f », la classe). En revanche quelques petites remarques :

1) ça bugge
Par exemple :

alias sudo='sudo -i -p "$(put2 -i "Password request" "sudo" 31 0)"'

Ne marche plus, ou bien (le fichier essai contient la chaine « abcdef ») :

\cat essai | while read line; do put2 -i "$line"; done

Surement du fait que read n'aime pas être imbriqué. De même la commande stty n'aime pas les shells non interactifs et à environement particulier, tels que lors d'une boucle while, ce qui m'avait déjà amené à commenter cette ligne dans putf original :

#: ${COLUMNS:=$(stty -a 2>/dev/null | awk -F'[ ;]' 'NR==1 {print $9}')}

Mis à part ces deux cas spécifiques j'ai pas remarqué de bugs d'affichage particuliers (j'ai testé notemment un affichage de données continuel, voir plus bas), et ça compte vraiment bien.

2) C'est plus lent
Put2 est plus lent que le putf original. Soit la commande :

while :; do put2 -n "Load" "$(\cat /proc/loadavg)"; done

Dans mon eeepc 901, lançant la commande tour à tour avec putf ou put2 il y a une différence de vitesse visible a l'oeil nu (surement du au read car depuis que la fonction getL ne fait plus qu'un seul appel à read, c'est nettement plus rapide). Mais bon c'est un peu du chipotage car ça reste quand même très correct.

3) #!/bin/bash
Aie, j'aimerais tant remettre #!/bin/sh comme avant... Alors j'ai tenté de traduire ceci en sh :

printf "\033[6n"
read -d R

Et je dois avouer que j'y arrive AAAAAAAbsolument pas du tout big_smile et je me demande même si c'est faisable tellement la commande read de sh est pourrie. En tout cas je veux bien un coup de main si c'est possible.

4) pour les tabulations
C'est super on peut faire un expand puisqu'on doit justement afficher (enfin ou plutôt ne pas afficher pendant le stty -echo) le message interpreté. Moi j'avais toujours des ennuis car pour couper les séquences d'échappement j'étais obligé de faire un printf "%s" à un moment donné. Et j'étais bloqué parce que comme tu dis :

Totor a écrit :

--> La longueur d'une chaine contenant une tabulation dépend de la position du curseur.

Et donc calculer sur un alignement à gauche pour ensuite afficher au milieu de l'écran, ça ne pouvait que foirer. C'était un peu la conclusion que je m'étais faite sans pouvoir l'exprimer avec tant de discernement et de clarté.



Donc globalement une excellente methode, et astucieuse. dommage pour ces petits inconvénients mais je ne désespère pas et vais continuer mes petites expérimentations. Je vais aussi tester ta deuxième méthode, quand je trouverai le temps. Merci en tout cas, et bravo.

Dernière modification par FRUiT (Le 01/02/2011, à 16:37)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#17 Le 01/02/2011, à 23:01

Totor

Re : [Résolu] Calcul de longueur d'une chaine

Salut FRUiT,

Désolé de ma réponse tardive mais TAF oblige .... En tout cas, je pense que la suite de ma réponse va te réjouir (en partie)... wink

Points 1 et 2 :
Si l'on considère le script suivant (getLen.sh) :

#!/bin/bash
getL ()
{
  stty -echo
  printf "%b" "\033[s\r$(tail -n 1 <<<"${*}")\033[6n"
  read -d R
  printf  "\033[u\033[K"
  stty echo
  SIZE="${REPLY//[^0-9;]}"
  [ "${SIZE}" ] && cc=$((${SIZE#*;}-1))
  [ $cc -eq $cc 2>/dev/null ]
}

# réduction aux même fonctionnalités que getL
function getLen()
{
    (
        stty -echo
        printf "\033[s${*##*$'\n'}\033[6n\033[u\033[K" >&0
        IFS=';' read -d R rowPos colPos 
        stty echo 
        echo $((colPos-1)) 
    )
}

width=$(getLen "\033[34mLes caRottes\tsont \033(B\033[mgéantes" )
echo "longueur=${width}"
echo Totor
time {
    for((x=0;x<=1000;x++)); do width=$(getLen "\033[34mLes caRottes\tsont \033(B\033[mgéantes" );done
}
echo FRUiT
time {
    for((x=0;x<=1000;x++)); do getL "\033[34mLes caRottes\tsont \033(B\033[mgéantes";done
}

Les résultats sont les suivants :

% ./getLen.sh 
longueur=28
Totor

real    0m13.453s
user    0m0.053s
sys    0m0.150s
FRUiT

real    0m16.716s
user    0m0.777s
sys    0m0.263s

Je te laisse en déduire les conclusions !

Point 3 :
Alors là, c'est pas gagné...
je n'ai pas réussi à faire mieux que ceci :

#!/bin/dash

getLenDash()
{
        (
            stty -echo
            # stty -echo eof \n
            printf "\033[s${*##*$'\n'}\033[6n\033[u\033[K">&0
            # printf "\033[s${*##*$'\n'}\033[6n\033[u\033[K\n">&0
            awk  ' BEGIN { RS="R"; FS=";"} { print $2-1; exit;} '  <&0
            stty echo ek
        ) 
}
width=$(getLenDash "\033[34mLes caRottes\tsont \033(B\033[mgéantes" )
echo "longueur=${width}"

où il faut appuyer sur [ENTER] pour obtenir le résultat (correcte) hmm
j'ai bien tenté de forcer la fin de la saisie mais sans résultat (lignes en commentaire)
mais je n'ai pas encore abandonné wink


-- Lucid Lynx --

Hors ligne

#18 Le 01/02/2011, à 23:58

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Ah bah oui, ${*##*$'\n'} au lieu d'ouvrir un sous-shell + tail, c'est carrément mieux big_smile. J'y aurais pas pensé. Pas mal aussi le coup de l'IFS.

Pour sh, ben j'avais réussi à lire la valeur aussi (avec read), mais suis arrivé au même résultat : il faut appuyer sur entrée, quelle chienlit... Enfin bon je vais continuer à expérimenter ce serait fou qu'un trux aussi 'simple' soit manquant doit y avoir une astuce. En tout cas la commande read de sh elle suxe dur. D'ailleurs puisque j'en suis à read j'implémente de pouvoir lire l'entrée standard pour pouvoir piper dedans genre echo "foo" | putf.

Dernière modification par FRUiT (Le 02/02/2011, à 00:07)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#19 Le 02/02/2011, à 00:04

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Et sinon la commande read c'est depuis quelle version de bash qu'elle est si bien ?

Dernière modification par FRUiT (Le 02/02/2011, à 00:05)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#20 Le 02/02/2011, à 14:58

Totor

Re : [Résolu] Calcul de longueur d'une chaine

FRUiT a écrit :

Et sinon la commande read c'est depuis quelle version de bash qu'elle est si bien ?

alors là, tu m'en demandes "trop" wink
ca ne fait que 3 ans que j'utilise bash !


-- Lucid Lynx --

Hors ligne

#21 Le 02/02/2011, à 15:37

ehmicky

Re : [Résolu] Calcul de longueur d'une chaine

Je crois que les seules modifications qu'a eu read depuis Bash2 est avec Bash4 : la possibilité d'utiliser des décimales avec -t, et l'ajout des options -i et -N. A confirmer !


Stego++, bibliothèque libre de stéganographie (avec cryptographie), à venir !
Besoin de votre aide :
Stats sur les compilateurs C++ les plus utilisés
Comment utiliser les archetypes C++ ?

Hors ligne

#22 Le 02/02/2011, à 17:08

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

En fait pour la rapidité apparemment ce n'est pas read le coupable.

Soit put10 :

#!/bin/sh

#set -x

put_formated () 
{
  : ${COLUMNS:=$(tput cols || printf "80")}
  : ${e:="\033"}                ; n="$(tput sgr0 || printf "%s" "${e}[m")"
  : ${M_PREFIX:=""}             ; r="$(tput bel  || printf "%s" "\007")"
  : ${M_BRACKETS:="[]"}         ; bl="${M_BRACKETS%?}"  br="${M_BRACKETS#?}"
  clip="((\\${e}|[[:cntrl:]])[\[\#][\?]?[0-9;]+?[ABCDfGHKsum]|(\\${e}|[[:cntrl:]])\(B(\\${e}|[[:cntrl:]])\[m)|[[:cntrl:]]"

  if [ ! "^${1#-}" = "^${1}" ]; then while getopts ":abc:dehilnoqrt:y" m; do case ${m} in
        e) lb="fail"  cl="0;31"                   ;;
        a) lb=" !! "  cl="0;33"                   ;;
        d) lb="done"  cl="0;32"                   ;;
        q) lb=" ?? "  cl="0;32"                   ;;
        h) lb="help"  cl="1;30"                   ;;
        r) lb="ring"  cl="1;35"                   ;;
      y|o) lb=" OK "  cl="0;32"                   ;;
        n) lb="info"  bs="$(tput cuu 1)\r${e}[K"  ;;
        l) M_LEFT=1                               ;;
        b) al=${r}                                ;;
        t) lb="${OPTARG}"                         ;;
        c) cl="${OPTARG}"                         ;;
        *) lb="info"                              ;;
       \?)                                        ;;
    esac ; done ; shift $(($OPTIND-1))
    STR="${1}"
    lb="${M_LABEL:-${2:-${lb}}}"
    cl="${e}[${M_LABEL_COLOR:-${3:-${cl:-"0;34"}}}m"
    cb="${e}[${M_BRACKETS_COLOR:-${4:-${cb:-"0;0"}}}m"
  else
    STR="${*}"
  fi
  [ "${STR}" ] || read -r STR
  [ "${lb}" ] && lb="$(printf "%04b" "${lb}" | expand | sed -r "s/${clip}//g")" || { bl= br= ; }
  [ "${M_PREFIX}" ] && STR="${M_PREFIX}${n} ${STR}"
  [ "${STR##*\\n}" = "${STR}" ] || M_LEFT=1

  STR="$(printf "%b" "${STR}" | expand)"
  cc=$(printf "%b" "${STR}" | sed -r "s/${clip}//g" | wc -m)
  co=$(($COLUMNS-${#lb}-${#bl}-${#br})); lr="${e}[${co}G"

  [ $co -le $(($cc%$COLUMNS)) ] && { M_LEFT=1 STR="${STR}\012" ; } || co=$(($co-$cc-${#br}))
  [ $COLUMNS -lt $cc ] && M_LEFT=1
  [ "$M_LEFT" ] || STR="${e}[${co}G${STR}"

  STR="${bs}${n}${STR}${lr}${n}${cb}${bl}${n}${cl}${lb}${n}${cb}${br}${n}${al}"
  printf "%b\n" "${STR}"
}

put_formated "${@}"

A peu près le script original, avec cependant un "read -r STR". Je fais un test sur le même script, juste en utilisant read, ou pas :

# read skippé
> time { for i in $(seq 1 1000); do put10 -n "Load" "$(\cat /proc/loadavg)"; done ; }

real    0m33.890s
user    0m3.104s
sys     0m5.580s
# read utilisé
> time { for i in $(seq 1 1000); do echo "Load" | put10 -n -t "$(\cat /proc/loadavg)"; done ; }

real    0m34.843s
user    0m1.684s
sys     0m5.304s

C'est quasiment pareil, donc ce serait peut-être plutôt la commande stty qui est longue ?
Il me reste a comparer la vitesse entre read sh et read bash (dès que j'ai le temps) car c'est aussi peut-être read bash qui est long.

@ehmicky: ah alors si je comprends correctement, le read -d daterait, lui, de au moins bash2 ?

Dernière modification par FRUiT (Le 02/02/2011, à 21:03)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#23 Le 02/02/2011, à 17:51

FRUiT

Re : [Résolu] Calcul de longueur d'une chaine

Et aussi je me suis rendu compte que :

STR="$(printf "%b" "${STR}" | expand)"
cc=$(printf "%b" "${STR}" | sed -r "s/${clip}//g" | wc -m)

Fonctionne, alors qu'avant non. La différence par rapport à quand ça ne fonctionnait pas c'est je pense l'ajout récent de [[:cntrl:]] dans la regex. Avant j'étais obligé de faire :

STR="$(printf "%s" "${STR}" | sed -r 's/\\t/    /g')"
cc=$(printf "%b" "$(printf "%s" "${STR}" | sed -r "s/${clip}//g")" | tail -n 1 | wc -m)

Soit un printf %b d'un printf %s, ce qui était totalement atroce.

Dernière modification par FRUiT (Le 02/02/2011, à 18:00)


Neon Suite by FRUiT (kde4.6) [url]http://[Merci de relire les règles]/yzm7cee[/url]
"Pour la carotte, le lapin est la plus parfaite incarnation du mal" (R. Sheckley)
clean

Hors ligne

#24 Le 04/02/2011, à 12:19

Totor

Re : [Résolu] Calcul de longueur d'une chaine

Salut,

J'ai trouvé une solution compatible dash pour trouver la position du curseur.

par contre : il faut rediriger la sortie d'erreur de dd vers /dev/null.

il te reste plus qu'à l'adapter pour calculer la longueur de la chaine.

note : je n'ai pas fait de test de perf


-- Lucid Lynx --

Hors ligne

#25 Le 04/02/2011, à 12:43

Totor

Re : [Résolu] Calcul de longueur d'une chaine

Bon
je n'ai pas pu m'empêcher :

#!/bin/dash

getL()
{
(
    saved_settings=$(stty -g)
    stty -icanon -echo time 10 min 6 < /dev/tty
    printf "\033[s${*##*$'\n'}\033[6n\033[u\033[K" > /dev/tty
    result=$(dd if=/dev/tty bs=20 count=1 2>/dev/null)
    stty "$saved_settings"
    IFS='[;R'
    set -f
    set -- $result
    echo $(($3-1))
)
}

x=$(getL "\033[34mLes caRottes\tsont \033(B\033[mgéantes")
echo $x

-- Lucid Lynx --

Hors ligne