Corre el riesgo de perder datos al utilizar este comodín de Linux de forma incorrecta
Los comodines de Linux le permiten escribir un único comando que actúa sobre grupos completos de archivos al mismo tiempo. Esto supone un gran ahorro de tiempo, a menos que las cosas salgan mal. Y pueden. Destructivamente.
Para qué sirven los comodines
Los comodines más conocidos son el signo de interrogación ? y el asterisco *. Estos se pueden utilizar para crear patrones de nombres de archivos. El signo de interrogación representa cualquier carácter y el asterisco representa cualquier secuencia de caracteres, incluido el carácter cero.
Sabiendo esto, podemos construir patrones que coincidan con múltiples nombres de archivos. En lugar de escribir todos los nombres de archivos en la línea de comando, escribimos el patrón. El comando actúa sobre todos los archivos que coinciden con el patrón.
Si tenemos una colección de archivos en un directorio como este:
Podemos seleccionar grupos de archivos que coincidan con los patrones que proporcionamos.
ls taf_*
Eso nos da todos los archivos con "taf_" al comienzo de sus nombres.
ls *.sh
ls s*.sh
El primer comando enumera todos los archivos de script de shell en el directorio. El segundo comando enumera sólo los archivos que comienzan con “s” y que también son archivos de script de shell.
Todo parece bastante simple, y con ls lo es. Pero otros comandos pueden hacer uso de este tipo de coincidencia de patrones. Los problemas surgen cuando el shell intenta ayudar haciendo coincidir patrones antes de que el comando tenga la oportunidad.
Usando el asterisco con el comando buscar
La acción de expandir un patrón en una lista de archivos coincidentes se llama globbing.
Comenzó como un comando independiente en la versión 6 de Unix, luego se convirtió en una biblioteca que podía vincularse a otros programas y hoy en día es un shell integrado. La expansión del patrón la realiza el shell y los resultados de la expansión se pasan al comando como parámetros de línea de comando.
Veremos dos ejemplos usando el comando buscar. Uno hace lo que cabría esperar, pero el segundo bien puede sorprenderte.
Para este ejemplo, usaremos un directorio con un solo archivo, llamado readme.txt. Hay dos directorios, src e inc. Contienen una combinación de archivos C, H, MD y TMP.
ls -R
Podemos usar buscar para buscar recursivamente archivos (-tipo f) con nombres que coincidan con nuestro patrón (-nombre *.c), lo que nos da una lista de los archivos C.
find . -type f -name *.c
Podemos añadir la opción -not para invertir la búsqueda, mostrándonos todo aparte de los archivos C.
find . -type f -not -name *.c
Habiendo revisado esta lista, optamos por eliminar todo excepto los archivos C. Podemos hacer esto agregando la opción -delete.
find . -type f -not -name *.c -delete
find .
El segundo comando de búsqueda enumera de forma recursiva todo lo que se encuentra dentro y debajo del directorio actual. Todo lo que queda son nuestros archivos C.
Eso funcionó de la manera que la mayoría de nosotros hubiéramos esperado. Ahora haremos exactamente lo mismo, pero esta vez el archivo en el directorio actual no es un archivo de texto, es un archivo C.
ls -R
Usaremos el mismo comando de búsqueda y las mismas opciones para eliminar todo menos los archivos C. Eso no es lo que queríamos en absoluto.
find . -type f -not -name *.c -delete
find .
Eso eliminó alegremente todos los archivos en el árbol de directorios, excepto el archivo C en el directorio actual.
Restableceremos los archivos una vez más y emitiremos el comando de la forma se supone que debemos usarlo.
Todos los archivos están en su lugar y tenemos un archivo C en el directorio actual, tal como lo teníamos antes.
ls -R
Esta vez, incluiremos el patrón comodín entre comillas simples.
find . -type f -not -name '*.c' -delete
find .
Eso es lo que queríamos. Todo ha desaparecido, excepto nuestros archivos C.
Bien, ¿qué salió mal?
Las comillas simples impiden que el shell expanda el patrón de nombre de archivo. Se pasa al comando o programa tal cual, para que el comando actúe en consecuencia.
En el ejemplo que funcionó, teníamos un archivo readme.txt en el directorio actual. El shell no pudo encontrar una coincidencia con *.c, por lo que pasó *.c a buscar para actuar.
En el ejemplo que eliminó todo menos los archivos C, teníamos un archivo llamado main.c en el directorio actual. El shell hizo coincidir el patrón con ese archivo y pasó el nombre del archivo al comando de búsqueda. Entonces, las instrucciones de Find fueron eliminar todo lo que no se llamara main.c.
Podemos ilustrar esto con un pequeño programa en C que no hace más que mostrar sus parámetros de línea de comando en la ventana de terminal.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
printf("You supplied %d arguments.\n", argc-1);
for (i=1; i<argc; i++)
printf("%-2d) \"%s\"\n", i, argv[i]);
exit (0);
}
Guardé esto como un archivo llamado glob.c y lo compilé con:
gcc -o glob glob.c
La variable argc contiene la cantidad de argumentos que pasamos al programa. Un bucle for recorre la lista de argumentos e imprime cada uno en la ventana del terminal.
El bucle for comienza en el argumento uno, no en cero. Hay un argumento cero. Siempre lleva el nombre del propio binario. Para evitar enturbiar el agua, he evitado imprimirlo. Los únicos argumentos que se imprimen son los que proporcionamos en la línea de comando.
./glob one two 3 ant beetle cockroach
Intentemos eso con *.c como parámetro de línea de comando.
ls *.c
./glob *.c
Sin ningún archivo C en el directorio actual, el shell pasa *.c al comando de búsqueda. El comando de búsqueda actúa entonces sobre el propio patrón comodín. Pero, cuando tenemos un archivo C en el directorio actual, el shell pasa el nombre del archivo C coincidente al programa.
ls *.c
./glob *.c
Nuestro programa recibe el nombre del archivo C como parámetro, y lo mismo ocurre con el comando de búsqueda. En realidad, find estaba haciendo lo que se le dijo que hiciera: eliminar todos los archivos excepto el archivo main.c.
Esta vez, incluiremos el patrón comodín entre comillas simples.
ls *.c
./glob '*.c'
El shell ignora la posibilidad de aplicar su globo al patrón comodín y lo pasa directamente al comando para su posterior procesamiento.
Una solución simple, puedes cotizarme
Como regla general, cite los patrones de comodines que pasa a comandos como buscar. Eso es todo lo que se necesita para evitar este tipo de percance potencialmente desastroso.