Lunghezza della corda in bash


428

Come si ottiene la lunghezza di una stringa memorizzata in una variabile e assegnarla a un'altra variabile?

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

Come si imposta un'altra variabile sull'output 11?

Risposte:


270

Lunghezza della stringa UTF-8

Oltre alla risposta corretta di fedorqui , vorrei mostrare la differenza tra lunghezza della stringa e lunghezza del byte:

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

renderà:

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

potresti anche dare un'occhiata ai caratteri memorizzati:

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"

risponderà:

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

Nota: Secondo il commento di Isabell Cowan , ho aggiunto impostazione $LC_ALLinsieme $LANG.

Lunghezza di una discussione

L'argomento funziona come le variabili regolari

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"
}

funzionerà come

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

Utile printfstrumento di correzione:

Se tu:

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

Non proprio carino ... Per questo, c'è una piccola funzione:

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

Quindi ora:

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

Sfortunatamente, questo non è perfetto!

Ma rimanevano alcuni strani comportamenti UTF-8, come caratteri a doppia spaziatura, caratteri a spaziatura zero, spostamento inverso e altro che non poteva essere così semplice ...

Dai un'occhiata a diffU8test.sh o diffU8test.sh.txt per ulteriori limitazioni.


Apprezzo questa risposta, poiché i file system impongono limiti di nome in byte e non in caratteri.
Gid

1
Potrebbe anche essere necessario impostare LC_ALL = C e forse altri.
Isabell Cowan,

1
@ F.Hauri Ma, comunque, segue che su alcuni sistemi la tua soluzione non funzionerà, perché lascia LC_ALL da solo. Potrebbe funzionare bene su installazioni predefinite di Debian e dei suoi derivati, ma su altri (come Arch Linux) non riuscirà a fornire la lunghezza corretta di byte della stringa.
Isabell Cowan,

1
grazie per aver preso qualcosa di semplice e contorto :)
thistleknot

2
@thistleknot Mi dispiace, 對不起 Qualche volta semplice è solo un'idea.
F. Hauri,

474

Per ottenere la lunghezza di una stringa memorizzata in una variabile, dire:

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

Per confermare che è stato correttamente salvato, echoesso:

$ echo "$size"
11

8
Con le punture UTF-8, potresti avere una lunghezza di stringa e una lunghezza di byte. vedi la mia risposta
F. Hauri,

Puoi anche usarlo direttamente in altre espansioni di parametri - ad esempio in questo test controllo che $rulenameinizi con il $RULE_PREFIXprefisso: [ "${rulename:0:${#RULE_PREFIX}}" == "$RULE_PREFIX" ]
Thomas Guyot-Sionnest

Potresti spiegare un po 'le espressioni di #myvare {#myvar}?
Lerner Zhang,

1
@lerneradams vedi Bash riferimento manuale → 3.5.3 Shell Parametro di espansione su ${#parameter}: La lunghezza in caratteri del valore espanso di parametro viene sostituito .
fedorqui "SO smettere di danneggiare",

25

Puoi usare:

MYSTRING="abc123"
MYLENGTH=$(printf "%s" "$MYSTRING" | wc -c)
  • wc -co wc --bytesper conteggi byte = i caratteri Unicode vengono conteggiati con 2, 3 o più byte.
  • wc -mo wc --charsper il conteggio dei caratteri = i caratteri Unicode vengono conteggiati singoli fino a quando non usano più byte.


3
Sul serio? una pipe, una subshell e un comando esterno per qualcosa di così banale?
gniourf_gniourf,

questo gestisce qualcosa del genere mylen=$(printf "%s" "$HOME/.ssh" | wc -c)mentre la soluzione accettata fallisce e devi myvar=$HOME/.sshprima farlo .
JL Peyret,

23

Volevo il caso più semplice, finalmente questo è un risultato:

echo -n 'Tell me the length of this sentence.' | wc -m;
36

4
scusa amico :( Questo è bash ... il martello maledetto che vede tutto come un chiodo, in particolare il pollice. "Dimmi la lunghezza di questa frase." contiene 36 caratteri. echo '' | wc -m=> 1. Dovresti usare -n: echo -n '' | wc -m=> 0... nel qual caso è una buona soluzione :)
AJP,

1
Grazie per la correzione! La pagina del manuale dice: -n do not output the trailing newline
dmatej,

17

Se si desidera utilizzarlo con gli argomenti della riga di comando o della funzione, assicurarsi di utilizzare size=${#1}invece di size=${#$1}. Il secondo può essere più istintivo ma è sintassi errata.


14
Parte del problema con "non si può fare <sintassi non valida>" è che, essendo questa sintassi non valida, non è chiaro cosa significhi interpretare un lettore. size=${#1}è certamente valido.
Charles Duffy,

Bene, è inaspettato. Non sapevo che il numero 1 fosse un sostituto di $ 1 in questo caso.
Dick Guertin,

16
Non lo è. #non sta sostituendo il $- l' $esterno delle parentesi graffe è ancora l'operatore di espansione. Il #è l'operatore lunghezza, come sempre.
Charles Duffy,

Ho risolto questa risposta poiché è un suggerimento utile ma non un'eccezione alla regola: segue esattamente la regola, come sottolineato da @CharlesDuffy
Zane Hooper

16

In risposta all'avvio del post:

Se vuoi usarlo con argomenti da riga di comando o funzione ...

con il codice:

size=${#1}

Potrebbe esserci il caso in cui si desidera solo verificare un argomento di lunghezza zero e non è necessario memorizzare una variabile. Credo che tu possa usare questo tipo di sintassi:

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

Vedi GNU e Wooledge per un elenco più completo delle espressioni condizionali di Bash.


11

Usando il tuo esempio fornito

#KISS (Keep it simple stupid)
size=${#myvar}
echo $size

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.