viernes, 30 de junio de 2017

Operaciones con cadenas

Hay una gran colección de expansiones que pueden usarse para operar con cadenas. Muchas de estas expansiones son particularmente adecuadas para operaciones con rutas.

${#parámetro}

se expande en la longitud de la cadena contenida en parámetro. Normalmente, parámetro es una cadena; sin embargo, si parámetro es @ o *, la expansión da como resultado el número de parámetros posicionales.

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo "'$foo' is ${#foo} characters long."
'This string is long.' is 20 characters long.



${parámetro:margen}
${parámetro:margen:longitud}

Estas expansiones se usan para extraer una porción de la cadena contenida en parámetro. La extracción comienza en margen caracteres desde el principio de la cadena y continúa hasta el final de la cadena, a no ser que se especifique longitud.

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo ${foo:5}
string is long.
[me@linuxbox ~]$ echo ${foo:5:6}
string

Si el valor de margen es negativo, se considera que empieza por el final de la cadena en lugar de por el principio. Fíjate que los valores negativos deben ir precedidos por un espacio para evitar la confusión con la expansión ${parámetro:-palabra}. longitud, si está presente, no debe ser menor de cero.

Si parámetro es @, el resultado de la expansión es longitud parámetros posicionales, comenzando en margen.

[me@linuxbox ~]$ foo="This string is long."
[me@linuxbox ~]$ echo ${foo: -5}
long.
[me@linuxbox ~]$ echo ${foo: -5:2}
lo



${parámetro#patrón}
${parámetro##patrón}

Estas expansiones eliminan la parte delantera de la cadena contenida en parámetro definida por patrón. patrón es un patrón comodín como los que se usan en expansiones de rutas. La diferencia entre las dos formas es que la # elimina el resultado más corto, mientras que la forma ## elimina el resultado más largo.

[me@linuxbox ~]$ foo=file.txt.zip
[me@linuxbox ~]$ echo ${foo#*.}
txt.zip
[me@linuxbox ~]$ echo ${foo##*.}
zip



${parámetro%patrón}
${parámetro%%patrón}

Estas expansiones son iguales que las # y ## anteriores, excepto que eliminan texto desde el final de la cadena contenida en parámetro en lugar que desde el principio.

[me@linuxbox ~]$ foo=file.txt.zip
[me@linuxbox ~]$ echo ${foo%.*}
file.txt
[me@linuxbox ~]$ echo ${foo%%.*}
file



${parámetro/patrón/cadena}
${parámetro//patrón/cadena}
${parámetro/#patrón/cadena}
${parámetro/%patrón/cadena}

Esta expansión realiza un "buscar y reemplazar" dentro del contenido de parámetro. Si se encuentra texto que coincida con el comodín patrón, se reemplaza con el contenido de cadena. En la forma normal, solo se reemplaza la primera coincidencia de patrón. En la forma //, se reemplazan todas las coincidencias. La forma /# requiere que la coincidencia ocurra al principio de la cadena, y la forma /% requiere que la coincidencia ocurra al final de la cadena. /cadena puede omitirse, lo que causa que el texto señalado por patrón se borre.

[me@linuxbox ~]$ foo=JPG.JPG
[me@linuxbox ~]$ echo ${foo/JPG/jpg}
jpg.JPG
[me@linuxbox ~]$ echo ${foo//JPG/jpg}
jpg.jpg
[me@linuxbox ~]$ echo ${foo/#JPG/jpg}
jpg.JPG
[me@linuxbox ~]$ echo ${foo/%JPG/jpg}
JPG.jpg

Es bueno saber la expansión de parámetros. Las expansiones con manipulación de cadenas pueden usarse como sustitutos de otros comandos comunes como sed o cut. Las expansiones mejoran la eficiencia de los scripts eliminando el uso de programas externos. Como ejemplo, modificaremos el programa longest-word que vimos en el capítulo anterior para usar la expansión de parámetros ${#j} en lugar de la sustitución de comandos $(echo $j | wc -c) y su subshell resultante, así:

#!/bin/bash

# longest-word3 : find longest string in a file

for i; do
    if [[ -r $i ]]; then
        max_word=
        max_len=
        for j in $(strings $i); do
            len=${#j}
            if (( len > max_len )); then
                max_len=$len
                max_word=$j
            fi
        done
        echo "$i: '$max_word' ($max_len characters)"
    fi
    shift
done

A continuación, compararemos la eficiencia de las dos versiones usando el comando time:

[me@linuxbox ~]$ time longest-word2 dirlist-usr-bin.txt
dirlist-usr-bin.txt: 'scrollkeeper-get-extended-content-list' (38 characters)

real 0m3.618s
user 0m1.544s
sys 0m1.768s
[me@linuxbox ~]$ time longest-word3 dirlist-usr-bin.txt
dirlist-usr-bin.txt: 'scrollkeeper-get-extended-content-list' (38 characters)

real 0m0.060s
user 0m0.056s
sys 0m0.008s

La versión original del script tarda 3,618 segundos en escanear el archivo, mientras que la nueva versión, usando expansión de parámetros, tarda sólo 0,06 segundos - una mejora significativa.

No hay comentarios:

Publicar un comentario