Me gustaría preguntar:
¿Por qué se echo {1,2,3}
expande a 1 2 3, que es un comportamiento esperado, mientras que echo [[:digit:]]
regresa [[:digit:]]
mientras esperaba que imprimiera todos los dígitos de 0
a 9
?
Me gustaría preguntar:
¿Por qué se echo {1,2,3}
expande a 1 2 3, que es un comportamiento esperado, mientras que echo [[:digit:]]
regresa [[:digit:]]
mientras esperaba que imprimiera todos los dígitos de 0
a 9
?
Respuestas:
Porque son dos cosas diferentes. El {1,2,3}
es un ejemplo de expansión de llaves . El armazón{1,2,3}
expande la construcción , incluso antes de verla. Puede ver qué sucede si usa :echo
set -x
$ set -x
$ echo {1,2,3}
+ echo 1 2 3
1 2 3
Como puede ver, el comando echo {1,2,3}
se expande para:
echo 1 2 3
Sin embargo, [[:digit:]]
es una clase de caracteres POSIX . Cuando se lo da echo
, el shell también lo procesa primero, pero esta vez se está procesando como un globo de shell . funciona de la misma manera que si ejecuta, echo *
que imprimirá todos los archivos en el directorio actual. Pero [[:digit:]]
es un globo de shell que coincidirá con cualquier dígito. Ahora, en bash, si un globo de shell no coincide con nada, se expandirá a sí mismo:
$ echo /this*matches*no*files
+ echo '/this*matches*no*files'
/this*matches*no*files
Si el globo coincide con algo, se imprimirá:
$ echo /e*c
+ echo /etc
/etc
En ambos casos, echo
solo imprime lo que el shell le dice que imprima, pero en el segundo caso, ya que el globo coincide con algo ( /etc
) se le dice que imprima ese algo.
Entonces, dado que no tiene ningún archivo o directorio cuyo nombre consista exactamente en un dígito (que es lo [[:digit:]]
que coincidiría), el globo se expande a sí mismo y obtiene:
$ echo [[:digit:]]
[[:digit:]]
Ahora, intente crear un archivo llamado 5
y ejecute el mismo comando:
$ echo [[:digit:]]
5
Y si hay más de un archivo coincidente:
$ touch 1 5
$ echo [[:digit:]]
1 5
Esto está (más o menos) documentado en man bash
la explicación de las nullglob
opciones que desactiva este comportamiento:
nullglob
If set, bash allows patterns which match no files (see
Pathname Expansion above) to expand to a null string,
rather than themselves.
Si configura esta opción:
$ rm 1 5
$ shopt -s nullglob
$ echo [[:digit:]] ## prints nothing
$
shopt -s failglob
para obtener un comportamiento más útil similar al de los proyectiles modernos como zsh
o fish
.
failglob
. nullglob
puede causar problemas inesperados, por ejemplo, al pegar una URL que tiene un ?
.
nullglob
para demostrar que el patrón está siendo interpretado como un globo por el caparazón.
{1,2,3}
es una expansión de llaves , se expande a las palabras enumeradas sin tener en cuenta su significado.
[...]
es un grupo de caracteres, utilizado en la expansión de nombre de archivo (o comodín, o glob) de manera similar al asterisco *
y al signo de interrogación ?
. Coincide con cualquier carácter individual incluido en la lista, o con caracteres que son miembros de grupos con nombre, como [:digit:]
si se enumeran. El comportamiento predeterminado de la mayoría de los shells es dejar el comodín como está si no hay archivos que lo coincidan.
(Tenga en cuenta que realmente no puede convertir un comodín / patrón en el conjunto de cadenas que coincidiría. El asterisco puede coincidir con cualquier cadena de cualquier longitud, por lo que expandir cualquier patrón que lo contenga produciría una lista infinita de cadenas).
Entonces:
$ bash -c 'echo [[:digit:]]' # bash leaves it as-is
[[:digit:]]
$ zsh -c 'echo [[:digit:]]' # zsh by default complains if no match
zsh:1: no matches found: [[:digit:]]
$ touch 1 3 d i g t
$ bash -c 'echo [[:digit:]]' # now there are two matches
1 3 # note that d, i, g and t do NOT match
Pero aún:
$ bash -c 'echo {1,2,3}'
1 2 3
Ambos están expandidos por el shell , no importa si el comando que está ejecutando es ls
, o echo
o rm
. También tenga en cuenta que si se cita cualquiera de esos, no se expandirán:
$ bash -c 'echo "[[:digit:]]"' # even though matching files still exist
[[:digit:]]
$ bash -c 'echo "{1,2,3}"'
{1,2,3}
[[:digit:]]
antes de pasarlo echo
, por lo que echo
nunca ve [[:digit:]]
, solo ve 1 3
. Puede ver esto en acción ejecutando set -x
lo que imprimirá los comandos reales que se están ejecutando (ejecute set +x
para apagarlo nuevamente).
echo
no busca archivos, el shell sí, antes de ejecutar el echo
.
{1,2,3}
(y, por ejemplo, {1..3}
son expansiones de llaves . El shell las interpreta antes de la ejecución del comando.
[[:digit:]]
es un token de coincidencia de patrones , pero no lo está utilizando en una ubicación con ningún archivo que coincida con ese patrón. Si usa una coincidencia de patrón que no tiene coincidencias, se expande a sí misma:
$ echo [[:digit:]]; touch 3; echo [[:digit:]]
[[:digit:]]
3