Comprimento da corda na festança


428

Como você obtém o comprimento de uma string armazenada em uma variável e atribui isso a outra variável?

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

Como você define outra variável para a saída 11?

Respostas:


270

Comprimento da corda UTF-8

Além da resposta correta de fedorqui , gostaria de mostrar a diferença entre o comprimento da string e o comprimento de bytes:

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

renderizará:

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

você pode até dar uma olhada nos caracteres armazenados:

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"

Vai responder:

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

Nota: De acordo com o comentário de Isabell Cowan , eu adicionei a configuração $LC_ALLjunto com $LANG.

Duração de um argumento

O argumento funciona da mesma forma que as variáveis ​​regulares

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

vai funcionar como

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

printfFerramenta de correção útil :

Se vocês:

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

Não é realmente bonito ... Para isso, há uma pequena função:

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

Então agora:

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

Infelizmente, isso não é perfeito!

Mas deixou um comportamento UTF-8 estranho, como caracteres com espaço duplo, caracteres com espaço zero, deslocamento reverso e outros que não poderiam ser tão simples ...

Dê uma olhada em diffU8test.sh ou diffU8test.sh.txt para obter mais limitações.


Agradeço esta resposta, pois os sistemas de arquivos impõem limitações de nome em bytes e não em caracteres.
Gid

1
Você também pode precisar definir LC_ALL = C e talvez outros.
Isabell Cowan

1
@ F.Hauri Mas, em todo o caso, em alguns sistemas sua solução não funcionará, porque deixa LC_ALL em paz. Pode funcionar bem nas instalações padrão do Debian e seus derivados, mas em outros (como o Arch Linux) ele falhará em fornecer o tamanho correto de bytes da string.
Isabell Cowan

1
obrigado por ter tomado algo simples e convoluting it :)
thistleknot

2
@thistleknot Me desculpe, 對不起 Às vezes simples é apenas uma idéia.
F. Hauri

474

Para obter o comprimento de uma string armazenada em uma variável, diga:

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

Para confirmar se foi salvo corretamente, echoele:

$ echo "$size"
11

8
Com picadas UTF-8, você pode ter um comprimento de sequência e um comprimento de bytes. veja minha resposta
F. Hauri 23/06

Você também pode usá-lo diretamente em outras expansões de parâmetros - por exemplo, neste teste, verifico que $rulenamecomeça com o $RULE_PREFIXprefixo: [ "${rulename:0:${#RULE_PREFIX}}" == "$RULE_PREFIX" ]
Thomas Guyot-Sionnest

Você poderia explicar um pouco as expressões de #myvare {#myvar}?
Lerner Zhang

1
@lerneradams ver manual de referência → 3.5.3 Shell Parâmetro Expansão Bash em ${#parameter}: O comprimento em caracteres do valor de parâmetro é expandido substituído .
fedorqui 'ASSIM, pare de prejudicar' 21/10

25

Você pode usar:

MYSTRING="abc123"
MYLENGTH=$(printf "%s" "$MYSTRING" | wc -c)
  • wc -cou wc --bytespara contagens de bytes = caracteres Unicode são contados com 2, 3 ou mais bytes.
  • wc -mou wc --charspara contagem de caracteres = caracteres Unicode são contados como únicos até que usem mais bytes.


3
A sério? um pipe, um subshell e um comando externo para algo tão trivial?
gniourf_gniourf

isso lida com algo como mylen=$(printf "%s" "$HOME/.ssh" | wc -c)considerando que a solução aceita falha e você precisa myvar=$HOME/.sshprimeiro.
JL Peyret

23

Eu queria o caso mais simples, finalmente este é um resultado:

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

4
desculpe companheiro :( Este é o bash ... o martelo amaldiçoado que vê tudo como um prego, particularmente o seu polegar. 'Diga-me o comprimento desta frase.' contém 36 caracteres. echo '' | wc -m=> 1. Você precisaria usar -n: echo -n '' | wc -m=> 0... caso em que ele é uma boa solução :)
AJP

1
Obrigado pela correção! A página do manual diz: -n do not output the trailing newline
dmatej 11/11

17

Se você quiser usar isso com argumentos de linha de comando ou função, certifique-se de usar em size=${#1}vez de size=${#$1}. O segundo pode ser mais instintivo, mas é uma sintaxe incorreta.


14
Parte do problema com "você não pode fazer <sintaxe inválida>" é que, sendo essa sintaxe inválida, não está claro o que o leitor deve interpretar. size=${#1}é certamente válido.
Charles Duffy

Bem, isso é inesperado. Eu não sabia que o nº 1 era um substituto para US $ 1 neste caso.
Dick Guertin

16
Não é. #não está substituindo o $- a parte $externa do aparelho ainda é o operador de expansão. O #é o operador de comprimento, como sempre.
Charles Duffy

Eu fixo esta resposta, uma vez que é uma dica útil, mas não é uma exceção à regra - segue-se a regra exatamente como fora apontado por @CharlesDuffy
Zane Hooper

16

Em resposta ao início da postagem:

Se você deseja usar isso com argumentos de linha de comando ou função ...

com o código:

size=${#1}

Pode haver o caso em que você apenas deseja verificar um argumento de comprimento zero e não precisa armazenar uma variável. Eu acredito que você pode usar esse tipo de sintaxe:

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

Veja GNU e wooledge para obter uma lista mais completa de expressões condicionais do Bash.


11

Usando seu exemplo fornecido

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.