find, wildcards y comillas
Hoy un compañero estaba buscando un fichero con find -name
y el wildcard asterisco. Lo ha hecho sin comillas. Le he comentado que las comillas son necesarias. Sin comillas, bash expande el wildcard y manda el resultado a find. Con comillas, lo envía como literal, sin expandir, y es find quien lo interpreta.
Después he querido probarlo un ejemplo, y he encontrado este enlace. He intentado repetir algo similar en mi ordenador.
Primer caso: búsqueda de ficheros .txt
si no hay ninguno en el primer nivel del directorio buscado:
[root@localhost j]# find . -name *.txt > /tmp/sin_comillas.txt
[root@localhost j]# find . -name "*.txt" > /tmp/con_comillas.txt
[root@localhost j]# cmp /tmp/sin_comillas.txt /tmp/con_comillas.txt
[root@localhost j]#
El comando cmp no devuelve nada. La salida es la misma. Veamos cualquiera de los dos ficheros:
[root@localhost j]# cat /tmp/con_comillas.txt | more
./.PyCharm2016.3/config/disabled_plugins.txt
./.PyCharm2016.3/config/jdbc-drivers/xerial-sqlite-license.txt
./.PyCharm2016.3/config/disabled_update.txt
[...]
Segundo caso: con un fichero patata.txt
. Volvemos a probar:
[root@localhost j]# touch patata.txt
[root@localhost j]# find . -name "*.txt" > /tmp/con_comillas.txt
[root@localhost j]# find . -name *.txt > /tmp/sin_comillas.txt
[root@localhost j]# cmp /tmp/sin_comillas.txt /tmp/con_comillas.txt
/tmp/sin_comillas.txt /tmp/con_comillas.txt differ: byte 3, line 1
Ahora find sin comillas sólo ha encontrado un fichero, patata.txt
. ¿Por qué?
Bash ha expandido esto:
[root@localhost j]# find . -name *.txt > /tmp/sin_comillas.txt
a esto:
[root@localhost j]# find . -name patata.txt > /tmp/sin_comillas.txt
¿Por qué se obtiene el mismo resultado en el primer caso? La respuesta la encontramos en la opción de Bash nullglob
(allow_null_glob_expansion
en bash 1). En este enlace se explica con detalle:
In bash Version 1, the option allow_null_glob_expansion converts nonmatching wildcard patterns into the null string. Otherwise, the wildcard is left as is without expansion. Here’s an example with echo (Section 27.5), which simply shows the arguments that it gets from the shell. In the directory where I’m running this example, there are no names starting with a, but there are two starting with s. In the first case below, allow_null_glob_expansion isn’t set, so the shell passes the unmatched a* to echo. After setting allow_null_glob_expansion, the shell removes the unmatched a* before it passes the results to echo:
bash$ echo a* s*
a* sedscr subdir
bash$ allow_null_glob_expansion=1
bash$ echo a* s*
sedscr subdir
bash Version 2 leaves nonmatching wildcard patterns as they are unless you’ve set the shell’s nullglob option (shopt -s nullglob). The nullglob option does the same thing that allow_null_glob_expansion=1 does in bash version 1
Al no encontrar ningún match para *.txt
en el directorio actual, bash envía el parámetro tal cual, sin expansión. En esta situación, la ejecución de find es igual con comillas que sin ellas. Sin embargo, debemos evitar omitirlas por la peligrosidad de encontrarnos con el segundo caso.
Para terminar, podemos consultar el estado de la opción nullglob ejecutando shopt
sin parámetros:
[root@localhost j]# shopt | grep nullglob
nullglob off