Este capítulo trata sobre dos conceptos separados pero relacionados: buscar y sustituir. Muchas veces, los textos que estás buscando no son simples y debes buscar un patrón común. Aprender cómo utilizar patrones significativos en las búsquedas y sustituciones en vez de cadenas de texto literales, hará que seas capaz de localizar cualquier texto rápidamente.
Como nota complementaria, en este capítulo, utilizará principalmente `/` cuando me refiera a la búsqueda. Todo lo que puedes realizar con el comando `/` puede también ser realizado con `?`.
Puede ser complicado hacer coincidir las mayúsculas y minúsculas en el término buscado. Si estás buscando el texto "Aprende Vim", te puedes confundir fácilmente al escribir una letra y obtener unos resultados de la búsqueda falsos. ¿No sería más sencillo y fácil si pudieras hacer coincidir tanto si está en mayúsculas o minúsculas? Aquí es donde la opción `ignorecase` es más útil. Simplemente añade `set ignorecase` en tu archivo de configuración vimrc y todos tus términos de búsqueda no tendrán en cuenta las mayúsculas o minúsculas. Ahora ya no será necesario que escribas `/Learn Vim` nunca mas. `/learn vim` será más que necesario para realizar la misma búsqueda.
Sin embargo, hay ocasiones en las que necesitas buscar una frase específica con diferenciación entre mayúsculas o minúsculas. Una forma de hacer eso es inhabilitando la opción `ignorecase` mediante `set noignorecase`, pero es muy laborioso el inhabilitar y volver a habilitar la opción cada vez que necesites una u otra forma de realizar búsquedas.
¿Existe un ajuste que te permita realizar búsquedas que no tengan en cuenta las mayúsculas la mayor parte de las veces, pero también sepa cuando una búsqueda requiere buscar las mayúsculas cuando lo necesites? Resulta que hay una manera.
Vim tiene una opción llamada `smartcase` para anular `ignorecase` si el patrón de búsqueda *contiene al menos una letra en mayúscula*. Puedes combinar tanto `ignorecase` y `smartcase` para realizar búsquedas que no tengan en cuenta las mayúsculas cuando introduzcas caracteres en minúsculas y tenga en cuenta las mayúsculas y minúsculas cuando introduzcas una o más letras en mayúsculas.
Solo hay una pega. ¿Qué pasa cuando necesitas solo encontrar la cadena de texto en minúscula? Cuando buscas `/hola`, Vim siempre encontrará sus variantes con mayúsculas. ¿Qué pasa si no quieres que se muestren en la búsqueda? Puedes utilizar el patrón `\c` anteponiéndolo a tu búsqueda para decir que en el término de búsqueda siguiente tenga en cuenta las minúsculas. Ahora si ejecutas `/\chola`, restringirá las coincidencias únicamente a "hola" y no aparecerán ni "HOLA" ni "Hola".
Puedes llegar al primer "hola" con `/^hello`. El caracter que siga a `^` deberá ser el primer caracter de la línea. Para buscar el último "hola", ejecuta `/hello$`. El caracter anterior a `$` deberá se el último caracter de la línea.
Puedes repetir la búsqueda con `//`. Si has buscado `/hola`, al ejecutar `//` será equivalente a ejecutar `/hola`. Este atajo de teclado te puede ahorrar varias pulsaciones de teclas, especialmente si anteriormente hiciste una búsqueda de un término bastante largo. También recordar que puedes utilizar `n` y `N` para repetir la última búsqueda en la misma dirección o en la dirección contraria, respectivamente.
¿qué pasa si quieres volver a ejecutar rápidamente la búsqueda anterior *n*? Puedes navegar rápidamente por el historial de búsquedas pulsando primero la tecla `/`, y después las teclas `arriba`/`abajo` (o `Ctrl-N`/`Ctrl-P`) hasta que encuentres el término de búsqueda que necesitas. Para ver todo el historial de búsquedas, puedes ejecutar `:history /`.
Cuando llegues al final de un archivo mientras estás buscando, Vim te mostrará un error: `"Search hit the BOTTOM without match for: <your-search>"`. A veces esto puede ser una buena protección para no hacer una búsqueda sin fin, pero otras veces puedes necesitar que el ciclo de búsqueda vuelva a empezar desde la parte superior de nuevo. Puedes utilizar la opción `set wrapscan` para hacer que Vim vuelva a buscar desde el inicio de un archivo cuando llega al final de un archivo. Para inhabilitar esta opción simplemente ejecuta `set nowrapscan`.
Es común buscar múltiples palabras a la vez. Si necesitas buscar *tanto* "hello vim" u "hola vim", pero no "salve vim" o "bonjour vim", puedes utilizar la tubería `|`.
Para encontrar tanto "hello" como "hola", puedes ejecutar `/hello\|hola`. Es necesario *escapar* (`\`) el operador de la tubería (`|`), de lo contrario Vim literalmente buscará la cadena "|".
Si no quieres escribir `\|` cada vez, puedes utilizar la sintaxis `magic` (`\v`) al comienzo de la búsqueda: `/\vhello|hola`. No se va a tratar `magic` en este capítulo, pero con `\v`, ya no necesitará escapar caracteres especiales nunca más. Para aprender más sobre `\v`, echa un vistazo a la ayuda de Vim mediante `:h \v`.
Si necesitas seleccionar "vim" pero solo cuando esta comienza con "11" y termina con "22", puedes utilizar los comandos `\zs` (coincidencia de comienzo) y `\ze` (coincidencia final). Ejecuta:
Todos tus términos de búsqueda hasta este punto han sido una búsqueda literal de una palabra. En la vida real, puede que tengas que utilizar un patrón general para encontrar tu texto. El patrón básico es el rango de caracteres, `[ ]`.
si necesitas buscar cualquier dígito, lo más probable es que no quieras escribir `/0\|1\|2\|3\|4\|5\|6\|7\|8\|9\|0` cada vez que lo necesites. En vez de eso, utiliza `/[0-9]` para encontrar cualquier dígito entre ese rango. La expresión `0-9` representa un rangos de números entre 0-9 que Vim intentará encontrar, así que si estás buscando dígitos entre 1 y 5, en ese caso utilizaríamos `/[1-5]`.
Los dígitos no son el único tipo de datos que Vim puede buscar. También puedes buscar entre `/[a-z]` para buscar letras en minúsculas y `/[A-Z]` para letras en mayúsculas.
También puedes combinar estos rangos juntos. Si necesitas buscar dígitos entre 0-9 y a la vez letras en mayúsculas y minúsculas de la "a" a la "f" (una cifra en hexadecimal), puedes ejecutar `/[0-9a-fA-F]`.
Para realizar una búsqueda inversa, puedes añadir `^` dentro de los corchetes dentro del rango. Para buscar algo que no sea un dígito, ejecuta `/[^0-9]`. Vim encontrará cualquier caracter mientras que no sea un dígito. Ten en cuenta que el caracter (`^`) dentro de los corchetes del rango es diferente del caracter al principio de una línea (ejemplo: `/^hello`). Si el caracter está fuera del par de corchetes y es el primer caracter en el término de búsqueda, esto significa "el primer caracter en una línea". Si el caracter está dentro de un par de corchetes y es el primer caracter dentro de los corchetes, esto significa un operador de búsqueda negativo. `/^abc` encontrará el primer "abc" en una línea y `/[^abc]` encontrará cualquier caracter excepto una "a", "b" o "c".
Puedes utilizar `/[0-9][0-9]` para encontrar una pareja de dos dígitos, pero este método no es escalable. ¿Qué pasa si necesitas encontrar veinte dígitos? Escribir `[0-9]` veinte veces no es una solución muy elegante. Por eso necesitas el argumento `count`.
Por cierto, estos llaves de `count` necesitan ser *escapadas* cuando las utilizas en Vim. El operador `count` es ubicado después del caracter que quieres incrementar.
Los argumentos de contaje `\{0,\}` (cero o más) y `\{1,\}` (uno o más) son patrones de búsqueda comunes y Vim tiene operadores especiales para estos casos: `*` y `+` (`+` necesita ser *escapado* mientras que `*` funciona bien sin necesidad de añadir símbolos de escape). Si ejecutas `/[0-9]*`, esto es lo mismo que ejecutar `/[0-9]\{0,\}`. Buscará cero o más dígitos. Esto encontrará "", "1", "123". Por cierto, también encontrará caracteres que no sean dígitos como "a", porque técnicamente hay un dígito cero en la letra "a". Piénsalo detenidamente antes de utilizar `*`. Si ejecutas `/[0-9]\+`, es lo mismo que ejecutar `/[0-9]\{1,\}`. Esto buscará uno o más dígitos. Encontrará "1" y "12".
Vim tiene unos rangos predefinidos para los caracteres más comunies como dígitos o letras. No vamos a entrar en detalle de todos, pero si lo necesitas puedes encontrar la lista ejecutando: `:h /character-classes`. Aquí tienes los más útiles:
Los puedes utilizar como utilizarías un rango de caracteres. Para buscar un único dígito, en vez de utilizar `/[0-9]`, puedes utilizar `/\d` para una sintaxis más concisa.
-`"` es una doble comilla literal. Encontrará la primera comilla doble.
-`[^"]` significa cualquier caracter excepto una comilla doble. Encontrará cualquier caracter alfanumérico y espacios en blanco mientras que no sea una comilla doble.
-`\+` significa uno o más. Como está precedido por `[^"]`, Vim busca uno o más caracteres que no sean una comilla doble.
-`"` es una doble comilla literal. Encontrará las comillas dobles de cierre.
Cuando ve el primer `"`, comenzará el patrón de captura. En el momento que Vim ve las segundas comillas dobles en una línea, encuentra el segunto patrón de `"` y detiene el patrón de captura. Mientras, todos los caracteres que no sean `"` entre las dos comillas `"` son capturados por el patrón `[^"]\+`, en este caso, la frase `¡Vim es asombroso!`. Este es un patrón común a la hora de capturar una frase encerrada entre un par de delimitadores similares: para capturar una frase entre dos comillas simples, puedes utilizar `/'[^']\+'`.
Los números de teléfono de EE.UU. están formados por un conjunto de tres números, seguidos por otros tres números y finalmente cuatro dígitos. Vamos a ver en detalle el comando:
-`\d\{3\}` contrará un dígito repetido exactamente tres veces
Puedes repetir el último comando de sustitución ya sea con el comando normal `&` o ejecutando `:s`. Si acabas de ejecutar `:s/good/awesome/`, tanto ejecutando `&` o `:s` repetirá la misma sustitución.
También, previamente en este mismo capítulo mencioné que puedes utilizar `//` para repetir el patrón de búsqueda previo. Este truco funciona con el comando de sustitución. Si `/bueno` fue ejecutado recientemente y dejas el primer argumento del patrón de sustitución en blanco, de esta manera `:s//increíble/`, es lo mismo que ejecutar lo siguiente `:s/bueno/increíble/`.
La sintaxis del rango del comando de sustitución es similar a la sintaxis del contador en el comando de la búsqueda (`{n,m}`), con algunas pequeñas diferencias. Aquí tienes algunas variaciones para pasar el rango:
En las siguientes secciones vamos a tratar de ver algunas de las expresiones regualres básicas. El conocimiento de un patrón robusto es esencial para dominar el comando de sustitución.
-`:%s` esto busca en el archivo entero para realizar una acción de sustitución.
-`\d` es el rango predefinido en Vim para los dígito, similar a (`[0-9]`).
-`"\0"` las comillas dobles son literalmente comillas dobles. `\0` es un caracter especial que representa "el patrón de encontrado completo". El patrón de búsqueda aquí es un número de un solo dígito, `\d`. En la línea uno, `\0` tienes el valor "1". En la línea dos, tiene el valor "2". En la línea tres, el valor "3", y así sucesivamente.
-`(\w+) (\w+)` agrupa los patrones. `\w` es uno de los rangos predefinidos de Vim para un caracter d euna palabra (`[0-9A-Za-z_]`). Los paréntesis `( )` rodeando captura un caracter de palabra coincidente en un frupo. Ten en cuenta el espacio entre los dos agrupamientos. `(\w+) (\w+)` captura en dos grupos. en la primera línea, el primer grupo captura el "uno" y el segundo grupo captura el "dos".
-`\2 \1` devuelve el grupo capturado en orden inverso. `\2` contiene la cadena "let" y `\1` la cadena "uno". Al tener `\2 \1` esto devuelve la cadena "let uno".
Recordemos que `\0` representa el patrón encontrado al completo. Puedes romper la cadena encontrada en grupos más pequeños con `( )`. Cada grupo es representado por `\1`, `\2`, `\3`, etc.
Cada `(\d)` encuentra y agrupa cada dígito. En la primera línea, el primer `(\d)` tiene el valor "1", el segundo `(\d)` "2" y el trecer `(\d)` "3". Son almacenados en las variables `\1`, `\2` y `\3`. En la segunda mitad de tu sustitución, el nuevo patrón `\3\2\1` resultará en el valor "321" en la primera línea.
Esto es debido a que ahora solo tienes dos grupos. El primer grupo, capturado por `(\d\d)`, es almacenado en `\1` y tiene el valor "12". El segundo grupo, capturado por `(\d)`, es almacenado dentro de `\2` y tiene el valor "3". `\2\1` entonces, esto devolverá "312".
Hay dos maneras de solucionar esto, Primera, puedes ejecutar el comando de sustitución un par de veces más. Segunda, puedes ejecutar el comando de sustitución añadiendo la opción de sustitución global (`g`) para que realice el comando en todas las coincidencias que encuentre en la línea.
Vim sustituirá todas las "tarta" por "donut" en un único comando. El comando global es una de las muchas opciones que admite el comando de sustitución. Le pasas esas opciones el final del comando de sustitución. Aquí tienes una lista de las opciones más útiles:
Existen más opciones que las que están mostradas en este listado. Para leer sobre estas opciones, llamadas *flags* en Vim, echa un vistazo a la ayuda: `:h s_flags`.
Por cierto, el comando de repetir una sustitución (`&` y `:s`) no mantiene las opciones ejecutadas anteriormente. Al ejecutar `&` esto solo repetirá `:s/tarta/donut/` sin `g`. Pararepetir rápidamente el último comando de sustitución con todas sus opciones, deberás ejecutar `:&&`.
Sin embargo, es difícil discernir qué barras (`/`) son parte del patrón de sustitución y cuales son delimitadores. Puedes cambiar el delimitador por cualquier caracter de un único byte (excepto por letras, números o `"`, `|`, y `\`). Vamos a reemplazarlo por `+`. El comando de sustitución anterior puede ser reescrito como:
Vamos a ver un ejemplo más. Supongamos que estás escribiendo un libro sobre Vim y necesitas convertir en mayúsculas la primera letra de cada palabra de una línea.
-`\<.` está formado por dos partes: `\<` para encontrar el comienzo de cada palabra y `.` para encontrar cualquier caracter. El operador `\<` hace que el siguiente caracter sea el primer caracter de una palabra. Como `.` es el siguiente caracter, esto encontrará el primer caracter de cualquier palabra.
-`\u&` convierte en mayúsculas el símbolo `&`. Recordemos que `&` (o `\0`) representa la coincidencia entera. Encontrará el primer caracter de cualquier palabra.
-`g` la opción o *flag* global. Sin este, este comando solo sustituirá la primera coincidencia encontrada. Y deberemos sustituir cada coincidencia encontrada en esta línea.
Recordemos que puedes utilizar `\zs` y `\ze` para definir el comienzo y el final de una coincidencia. Esta técnica también funciona en la sustitución. Si tienes este texto:
Ya has visto antes la sintaxis de `{3}`. Es del tipo `{n,m}`. En este caso, tienes `{3}` que será exactamente la tercera coincidencia. El único truco nuevo que hemos utilizado aquí es `{-}`. Es una coincidencia no excesiva. Encuentra la coincidencia más corta dada en el patrón. En este caso, `(.{-}Mississippi)` encuentra la mínima cantidad de "Mississippi" precedida por cualquier caracter. Esto contrasta con `(.*Mississippi)` que encuentra la coincidencia más larga del patrón dado.
Si utilizas `(.{-}Mississippi)`, esto encontrará cinco coincidencias: "Uno Mississippi", "dos Mississippi", etc. Si utilizas `(.*Mississippi)`, encontrarás una coincidencia: el último "Mississippi". Para aprender más, echa un vistazo `:h /\{-` y `:h non-greedy`.
Finalmente veamos cómo sustituir frases a través de múltiples archivos. Para esta sección asumiremos que tenemos dos archivos: `food.txt` y `animal.txt`.
Primero, vamos a capturar tanto `food.txt` como `animal.txt` dentro de `:args`. Recordemos de capítulos anteriores que `:args` puede ser utilizado para crear una lista de nombres de archivos. Hay varias formas de realizar esto dentro de Vim:
También puedes ejecutar los comandos anteriors desde fuera de Vim, pasando los archivos como *argumentos* de Vim (por esto es llamado el comando "args"). Desde la terminal, ejecuta:
Para ir al argumento siguiente o previo de la lista, escribe `:next` o `:previous`. Ahora que todos los archivos relevantes están almacenados dentro de la lista de argumentos, puedes realizar una sustitución en múltiples archivos con el comando `:argdo`. Ejecuta:
De manera alternativa, también puedes ejecutar el comando de sustituir en múltiples archivos con macros. Vamos a empezar obteniendo los archivos relevantesen la lista de argumentos. Ejecuta:
-`:args animal.txt food.txt` lista los archivos relevantes dentro de la lista de `:args`.
-`qq` comienza la grabación de la marcho en el registro "q".
-`:%s/dog/chicken/g` sustituye "dog" con "chicken" en todas las líneas del archivo actual.
-`:wnext` escribe (guarda) el archivo y después va al siguiente archivo de la lista `args`. Sería similar a ejecutar `:w` y `:next` al mismo tiempo.
-`q` detiene la grabación de la macro.
-`99@q` ejecuta la macro noventa y nueve veces. Vim detendrá la ejecución de la macro después de encontrar el primer error, así que Vim no ejecutará la macro noventa y nueve veces.
La habilidad para realizar búsquedas correctas es una habilidad necesaria cuando se está editando. Dominar las búsquedas te permite utilizar la flexibilidad de las expresiones regulares para buscar cualquier patrón en un archivo. Tómate tu tiempo para aprender estas técnicas. Incluso haz las búsquedas y sustituciones de este capítulo por tí mismo. Hace tiempo leí un libro sobre expresiones regulares sin realmente realizar ninguna y he olvidado casi por completo todo lo que leí. La lectura activa practicando es la mejor manera de dominar cualquier habilidad.
Una buena manera de mejorar tu habilidad con los patrones de búsqueda es, todo lo que necesites buscar con un patrón (como "hello 123"), en vez de buscarlo de manera literal el término completo (`/hello 123`), intenta realizarlo con un patrón (`/\v(\l+) (\d+)`). Muchos de estos conceptos de expresiones regulares también son aplicables a la programación en general, no solo cuando estás utilizando Vim.
Ahora que has aprendido sobre la búsqueda y sustitución avanzada en Vim, vamos a aprender uno de los comandos más versátiles, el comando global.