Longitud de la cuerda en bash

428

¿Cómo se obtiene la longitud de una cadena almacenada en una variable y se la asigna a otra variable?

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

¿Cómo se configura otra variable para la salida 11?

AJP
fuente

Respuestas:

270

Longitud de cadena UTF-8

Además de la respuesta correcta de fedorqui , me gustaría mostrar la diferencia entre la longitud de la cadena y la longitud 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

rendirá:

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

incluso podrías echar un vistazo a los caracteres almacenados:

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"

Responderé:

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

Nota: Según el comentario de Isabell Cowan , agregué configuración $LC_ALLjunto con $LANG.

Longitud de una discusión

El argumento funciona igual que las variables 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"
}

funcionará como

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

Útil printfherramienta de corrección:

Si 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

No es realmente bonito ... Para esto, hay una pequeña función:

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

Entonces ahora:

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

Desafortunadamente, esto no es perfecto!

Pero dejó un comportamiento extraño de UTF-8, como caracteres de doble espacio, caracteres de cero espacio, desplazamiento inverso y otros que no podrían ser tan simples ...

Eche un vistazo a diffU8test.sh o diffU8test.sh.txt para obtener más limitaciones.

F. Hauri
fuente
Agradezco esta respuesta, ya que los sistemas de archivos imponen limitaciones de nombre en bytes y no en caracteres.
Gid
1
Es posible que también deba configurar LC_ALL = C y quizás otros.
Isabell Cowan
1
@ F.Hauri Pero, sin embargo, se deduce que en algunos sistemas su solución no funcionará, porque deja a LC_ALL solo. Puede funcionar bien en las instalaciones predeterminadas de Debian y sus derivados, pero en otros (como Arch Linux) no dará la longitud correcta de bytes de la cadena.
Isabell Cowan
1
gracias por tomar algo simple y complicarlo :)
thistleknot
2
@thistleknot Lo siento, 對不起 En algún momento simple es solo una idea.
F. Hauri
474

Para obtener la longitud de una cadena almacenada en una variable, diga:

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

Para confirmar que se guardó correctamente, echose:

$ echo "$size"
11
fedorqui 'así que deja de dañar'
fuente
8
Con las picaduras UTF-8, puede tener una longitud de cadena y una longitud de bytes. mira mi respuesta
F. Hauri
También puede usarlo directamente en otras expansiones de parámetros; por ejemplo, en esta prueba $rulename$RULE_PREFIX[ "${rulename:0:${#RULE_PREFIX}}" == "$RULE_PREFIX" ]
verifico
¿Podría explicar un poco las expresiones de #myvary {#myvar}?
Lerner Zhang
1
@lerneradams ver Bash Manual de referencia → 3.5.3 Shell Parámetro de expansión en ${#parameter}: La longitud en caracteres del valor ampliado de parámetro está sustituido .
fedorqui 'SO dejar de dañar'
25

Puedes usar:

MYSTRING="abc123"
MYLENGTH=$(printf "%s" "$MYSTRING" | wc -c)
  • wc -co wc --bytespara recuentos de bytes = los caracteres Unicode se cuentan con 2, 3 o más bytes.
  • wc -mo wc --charspara recuentos de caracteres = los caracteres Unicode se cuentan solo hasta que usan más bytes.
atesina
fuente
3
¿Seriamente? ¿Una tubería, un subshell y un comando externo para algo tan trivial?
gniourf_gniourf
esto maneja algo así como mylen=$(printf "%s" "$HOME/.ssh" | wc -c)mientras que la solución aceptada falla y usted necesita myvar=$HOME/.sshprimero.
JL Peyret
23

Quería el caso más simple, finalmente este es un resultado:

echo -n 'Tell me the length of this sentence.' | wc -m;
36
dmatej
fuente
44
lo siento amigo :( Esto es bash ... el martillo maldito que ve todo como un clavo, particularmente tu pulgar. 'Dime la longitud de esta oración.' contiene 36 caracteres. echo '' | wc -m=> 1. Necesitarías usar -n: echo -n '' | wc -m=> 0... en cuyo caso es una buena solución :)
AJP
1
¡Gracias por la corrección! La página del manual dice: -n do not output the trailing newline
dmatej
17

Si desea usar esto con argumentos de línea de comando o función, asegúrese de usarlo en size=${#1}lugar de size=${#$1}. El segundo puede ser más instintivo pero es una sintaxis incorrecta.

Dick Guertin
fuente
14
Parte del problema con "no se puede hacer <sintaxis no válida>" es que, como la sintaxis no es válida, no está claro qué debe interpretar un lector. size=${#1}Ciertamente es válido.
Charles Duffy
Bueno, eso es inesperado. No sabía que el # 1 era un sustituto de $ 1 en este caso.
Dick Guertin
16
No lo es #no reemplaza el $- el $exterior de las llaves sigue siendo el operador de expansión. El #es el operador de longitud, como siempre.
Charles Duffy
He arreglado esta respuesta ya que es un consejo útil pero no una excepción a la regla: sigue la regla exactamente, como lo señaló @CharlesDuffy
Zane Hooper
16

En respuesta al inicio de la publicación:

Si desea usar esto con la línea de comandos o argumentos de función ...

con el código:

size=${#1}

Puede haber un caso en el que solo desee verificar un argumento de longitud cero y no tenga necesidad de almacenar una variable. Creo que puedes usar este tipo de sintaxis:

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

Consulte GNU y wooledge para obtener una lista más completa de expresiones condicionales de Bash.

JGFMK
fuente
11

Usando su ejemplo proporcionado

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

Aquí hay un par de formas de calcular la longitud de la variable:

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

y para establecer el resultado en otra variable simplemente asigne el comando anterior con comillas inversas en otra variable de la siguiente manera:

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

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

Mukesh Shakya
fuente