Longueur de chaîne en bash

428

Comment obtenir la longueur d'une chaîne stockée dans une variable et l'affecter à une autre variable?

myvar="some string"
echo ${#myvar}  
# 11

Comment définissez-vous une autre variable sur la sortie 11?

AJP
la source

Réponses:

270

Longueur de chaîne UTF-8

En plus de la bonne réponse de fedorqui , je voudrais montrer la différence entre la longueur de chaîne et la longueur d'octet:

myvar='Généralités'
chrlen=${#myvar}
oLang=$LANG oLcAll=$LC_ALL
LANG=C LC_ALL=C
bytlen=${#myvar}
LANG=$oLang LC_ALL=$oLcAll
printf "%s is %d char len, but %d bytes len.\n" "${myvar}" $chrlen $bytlen

rendra:

Généralités is 11 char len, but 14 bytes len.

vous pouvez même consulter les caractères stockés:

myvar='Généralités'
chrlen=${#myvar}
oLang=$LANG oLcAll=$LC_ALL
LANG=C LC_ALL=C
bytlen=${#myvar}
printf -v myreal "%q" "$myvar"
LANG=$oLang LC_ALL=$oLcAll
printf "%s has %d chars, %d bytes: (%s).\n" "${myvar}" $chrlen $bytlen "$myreal"

répondra:

Généralités has 11 chars, 14 bytes: ($'G\303\251n\303\251ralit\303\251s').

Nota: Selon le commentaire d'Isabell Cowan , j'ai ajouté un paramètre à $LC_ALLavec $LANG.

Longueur d'un argument

L'argument fonctionne de la même manière que les variables régulières

strLen() {
    local bytlen sreal oLang=$LANG oLcAll=$LC_ALL
    LANG=C LC_ALL=C
    bytlen=${#1}
    printf -v sreal %q "$1"
    LANG=$oLang LC_ALL=$oLcAll
    printf "String '%s' is %d bytes, but %d chars len: %s.\n" "$1" $bytlen ${#1} "$sreal"
}

fonctionnera comme

strLen théorème
String 'théorème' is 10 bytes, but 8 chars len: $'th\303\251or\303\250me'

Utile printfoutil de correction:

Si vous:

for string in Généralités Language Théorème Février  "Left: ←" "Yin Yang ☯";do
    printf " - %-14s is %2d char length\n" "'$string'"  ${#string}
done

 - 'Généralités' is 11 char length
 - 'Language'     is  8 char length
 - 'Théorème'   is  8 char length
 - 'Février'     is  7 char length
 - 'Left: ←'    is  7 char length
 - 'Yin Yang ☯' is 10 char length

Pas vraiment joli ... Pour cela, il y a une petite fonction:

strU8DiffLen () { 
    local bytlen oLang=$LANG oLcAll=$LC_ALL
    LANG=C LC_ALL=C
    bytlen=${#1}
    LANG=$oLang LC_ALL=$oLcAll
    return $(( bytlen - ${#1} ))
}

Alors maintenant:

for string in Généralités Language Théorème Février  "Left: ←" "Yin Yang ☯";do
    strU8DiffLen "$string"
    printf " - %-$((14+$?))s is %2d chars length, but uses %2d bytes\n" \
        "'$string'" ${#string} $((${#string}+$?))
  done 

 - 'Généralités'  is 11 chars length, but uses 14 bytes
 - 'Language'     is  8 chars length, but uses  8 bytes
 - 'Théorème'     is  8 chars length, but uses 10 bytes
 - 'Février'      is  7 chars length, but uses  8 bytes
 - 'Left: ←'      is  7 chars length, but uses  9 bytes
 - 'Yin Yang ☯'   is 10 chars length, but uses 12 bytes

Malheureusement, ce n'est pas parfait!

Mais il restait un comportement UTF-8 étrange, comme les caractères à double interligne, les caractères à zéro, le déplacement inverse et d'autres qui ne pouvaient pas être aussi simples ...

Jetez un œil à diffU8test.sh ou diffU8test.sh.txt pour plus de limitations.

F. Hauri
la source
J'apprécie cette réponse, car les systèmes de fichiers imposent des limitations de nom en octets et non en caractères.
Gid
1
Vous devrez peut-être également définir LC_ALL = C et peut-être d'autres.
Isabell Cowan
1
@ F.Hauri Mais, il s'ensuit néanmoins que sur certains systèmes votre solution ne fonctionnera pas, car elle laisse LC_ALL tranquille. Cela peut fonctionner correctement sur les installations par défaut de Debian et de ses dérivés, mais sur d'autres (comme Arch Linux), il ne donnera pas la longueur d'octet correcte de la chaîne.
Isabell Cowan
1
merci d'avoir pris quelque chose de simple et de le compliquer :)
thistleknot
2
@thistleknot Je suis désolé, 對不起 Parfois simple, c'est juste une idée.
F.Hauri
475

Pour obtenir la longueur d'une chaîne stockée dans une variable, dites:

myvar="some string"
size=${#myvar} 

Pour confirmer qu'il a été correctement enregistré, echoil:

$ echo "$size"
11
fedorqui 'SO arrête de nuire'
la source
8
Avec les piqûres UTF-8, vous pouvez avoir une longueur de chaîne et une longueur d'octets. voir ma réponse
F. Hauri
Vous pouvez également l'utiliser directement dans d'autres extensions de paramètres - par exemple dans ce test que je vérifie qui $rulenamecommence par le $RULE_PREFIXpréfixe: [ "${rulename:0:${#RULE_PREFIX}}" == "$RULE_PREFIX" ]
Thomas Guyot-Sionnest
Pourriez-vous s'il vous plaît expliquer un peu les expressions de #myvaret {#myvar}?
Lerner Zhang
1
@lerneradams voir le manuel de référence Bash → 3.5.3 Expansion des paramètres du shell sur ${#parameter}: La longueur en caractères de la valeur étendue du paramètre est remplacée .
fedorqui 'SO arrête de nuire'
25

Vous pouvez utiliser:

MYSTRING="abc123"
MYLENGTH=$(printf "%s" "$MYSTRING" | wc -c)
  • wc -cou wc --bytespour le nombre d'octets = les caractères Unicode sont comptés avec 2, 3 octets ou plus.
  • wc -mou wc --charspour le nombre de caractères = les caractères Unicode sont comptés individuellement jusqu'à ce qu'ils utilisent plus d'octets.
atésine
la source
3
Sérieusement? un tuyau, un sous-shell et une commande externe pour quelque chose d'aussi banal?
gniourf_gniourf
cela gère quelque chose comme mylen=$(printf "%s" "$HOME/.ssh" | wc -c)alors que la solution acceptée échoue et vous devez d' myvar=$HOME/.sshabord.
JL Peyret
23

Je voulais le cas le plus simple, enfin voici un résultat:

echo -n 'Tell me the length of this sentence.' | wc -m;
36
dmatej
la source
4
désolé mec :( Ceci est bash ... le marteau maudit qui voit tout comme un clou, en particulier votre pouce. "Dites-moi la longueur de cette phrase." contient 36 caractères. echo '' | wc -m=> 1. Vous devez utiliser -n: echo -n '' | wc -m=> 0... auquel cas c'est une bonne solution :)
AJP
1
Merci pour la correction! La page de manuel dit: -n do not output the trailing newline
dmatej
17

Si vous souhaitez utiliser ceci avec des arguments de ligne de commande ou de fonction, assurez-vous d'utiliser size=${#1}au lieu de size=${#$1}. Le second peut être plus instinctif mais est une syntaxe incorrecte.

Dick Guertin
la source
14
Une partie du problème avec "vous ne pouvez pas faire <syntaxe invalide>" est que, cette syntaxe étant invalide, il n'est pas clair ce qu'un lecteur devrait interpréter comme signifiant. size=${#1}est certainement valable.
Charles Duffy
Eh bien, c'est inattendu. Je ne savais pas que # 1 était un substitut à 1 $ dans ce cas.
Dick Guertin
16
Ça ne l'est pas. #ne remplace pas le $- l' $extérieur des accolades est toujours l'opérateur d'expansion. L' #opérateur est la longueur, comme toujours.
Charles Duffy
J'ai corrigé cette réponse car c'est une astuce utile mais pas une exception à la règle - elle suit exactement la règle, comme l'a souligné @CharlesDuffy
Zane Hooper
16

En réponse au début du post:

Si vous souhaitez l'utiliser avec des arguments de ligne de commande ou de fonction ...

avec le code:

size=${#1}

Il peut y avoir le cas où vous voulez simplement vérifier un argument de longueur nulle et n'avez pas besoin de stocker une variable. Je pense que vous pouvez utiliser ce type de syntaxe:

if [ -z "$1" ]; then
    #zero length argument 
else
    #non-zero length
fi

Voir GNU et wooledge pour une liste plus complète des expressions conditionnelles Bash.

JGFMK
la source
11

En utilisant votre exemple fourni

#KISS (Keep it simple stupid)
size=${#myvar}
echo $size
chardon
la source
9

Voici quelques façons de calculer la longueur d'une variable:

echo ${#VAR}
echo -n $VAR | wc -m
echo -n $VAR | wc -c
printf $VAR | wc -m
expr length $VAR
expr $VAR : '.*'

et pour définir le résultat dans une autre variable, attribuez simplement la commande ci-dessus avec guillemet arrière dans une autre variable comme suit:

otherVar=`echo -n $VAR | wc -m`   
echo $otherVar

http://techopsbook.blogspot.in/2017/09/how-to-find-length-of-string-variable.html

Mukesh Shakya
la source