Sé usar el comando ls
enumerará todos los directorios. Pero, ¿qué hace el ls *
comando hacer? Lo usé y solo enumera los directorios. ¿La estrella delante de ls
significa qué tan profundo listará los directorios?
Respuesta aceptada:
ls
enumera los archivos y el contenido de los directorios que se pasan como argumentos y, si no se proporciona ningún argumento, enumera el directorio actual. También se le puede pasar una serie de opciones que afectan su comportamiento (ver man ls
para más detalles).
Si ls
se le pasa un argumento llamado *
, buscará un archivo o directorio llamado *
en el directorio actual y listarlo como cualquier otro. ls
no trata el *
personaje de ninguna otra manera que cualquier otra.
Sin embargo, si ls *
es un caparazón línea de comando, que es código en el idioma de un shell de Unix , entonces el shell expandirá ese *
según su globing (también conocido como Generación de nombre de archivo o Expansión de nombre de archivo/ruta ) reglas.
Si bien los diferentes shells admiten diferentes operadores globales, la mayoría de ellos está de acuerdo con el más simple *
. *
como patrón significa cualquier número de caracteres, entonces *
como un glob
se expandirá a la lista de archivos en los directorios actuales que coinciden con ese patrón. Sin embargo, hay una excepción que un punto inicial (.
) el carácter en un nombre de archivo debe coincidir explícitamente, por lo que *
en realidad se expande a la lista de archivos y directorios que no comienzan con .
(en orden léxico).
Por ejemplo, si el directorio actual contiene los archivos llamados .
, ..
, .foo
, -l
y foo bar
, *
será expandido por el shell a dos argumentos para pasar a ls
:-l
y foo bar
, por lo que será como si hubieras escrito:
ls -l "foo bar"
o
'ls' "-l" foo bar
Que son tres formas de ejecutar exactamente el mismo comando. En los 3 casos, el ls
comando (que probablemente se ejecutará desde /bin/ls
de una búsqueda de directorios mencionados en $PATH
) se le pasarán esos 3 argumentos:“ls”, “-l” y “foo bar”.
Por cierto, en este caso, ls
tratará el primero (estrictamente hablando segundo ) uno como opción.
Ahora, como dije, diferentes caparazones tienen diferentes operadores globales. Hace unas décadas, zsh
introdujo el **/
operator¹ que significa hacer coincidir cualquier nivel de subdirectorios, abreviatura de (*/)#
y ***/
que es lo mismo excepto que sigue los enlaces simbólicos mientras desciende por los directorios.
Hace unos años (julio de 2003, ksh93o+
), ksh93
decidió copiar ese comportamiento pero decidió hacerlo opcional, y solo cubrió el **
caso (no ***
). Además, mientras **
solo no era especial en zsh
² (simplemente significa lo mismo que *
como en otras conchas tradicionales desde **
significa cualquier número de caracteres seguido de cualquier número de caracteres), en ksh93, **
significaba lo mismo que **/*
(es decir, cualquier archivo o directorio debajo del actual (excluyendo archivos ocultos)³.
bash
copiado ksh93
unos años más tarde (febrero de 2009, bash 4.0), con la misma sintaxis pero con una desafortunada diferencia:**
de bash era como zsh
's ***
, es decir, estaba siguiendo enlaces simbólicos cuando recurría a subdirectorios, lo que generalmente no es lo que usted quiere que haga y puede tener efectos secundarios desagradables. Se solucionó en parte en bash-4.3 en el sentido de que todavía se seguían los enlaces simbólicos, pero la recursividad se detuvo allí. Se arregló por completo en 5.0.
yash
añadido **
en la versión 2.0 en 2008, habilitado con extended-glob
opción. Su implementación está más cerca de zsh
está en ese **
solo no es especial. En la versión 2.15 (2009), agregó ***
como en zsh
y dos de sus propias extensiones:.**
y .***
para incluir directorios ocultos cuando se repite (en zsh
, el D
calificador global (como en **/*(D)
) tendrá en cuenta los archivos y directorios ocultos, pero si solo desea recorrer los directorios ocultos pero no expandir los archivos ocultos, necesita ((*|.*)/)#*
o **/[^.]*(D)
).
El caparazón de pescado también es compatible con **
. Como la versión anterior de bash
, sigue enlaces simbólicos al descender en el árbol de directorios. Sin embargo, en ese caparazón **/*
no es lo mismo que **
. **
es más una extensión de *
que puede abarcar varios directorios. En fish
, **/*.c
coincidirá con a/b/c.c
pero no a.c
, mientras que a**.c
coincidirá con a.c
y ab/c/d.c
y zsh
's **/.*
por ejemplo, debe escribirse .* **/.*
. Allí, ***
se entiende como **
seguido de *
entonces lo mismo que **
.
tcsh
también agregó un globstar
opción en V6.17.01 (mayo de 2010) y admite tanto **
y ***
a la zsh
.
Entonces en tcsh
, bash
y ksh93
, (cuando la opción correspondiente está habilitada (globstar
)) o fish
, **
expande todos los archivos y directorios debajo del actual, y ***
es lo mismo que **
para fish
, un enlace simbólico que atraviesa **
para tcsh
con globstar
, y lo mismo que *
en bash
y ksh93
(aunque no es imposible que futuras versiones de esos shells también atraviesen enlaces simbólicos).
Arriba, habrá notado la necesidad de asegurarse de que ninguna de las expansiones se interprete como una opción. Para eso, harías:
ls -- *
O:
ls ./*
Hay algunos comandos (no importa para ls
) donde el segundo es preferible ya que incluso con el --
algunos nombres de archivo pueden recibir un tratamiento especial. Es el caso de -
para la mayoría de las utilidades de texto, cd
y pushd
y nombres de archivo que contienen el =
carácter para awk
por ejemplo. Anteponiendo ./
a todos los argumentos se les quita su significado especial (al menos para los casos mencionados anteriormente).
También se debe tener en cuenta que la mayoría de los shells tienen una serie de opciones que afectan el comportamiento global (como si los archivos de puntos se ignoran o no, el orden de clasificación, qué hacer si no hay coincidencias...), consulte también $FIGNORE
parámetro en ksh
Además, en todos los shells excepto en csh
, tcsh
, fish
y zsh
, si el patrón global no coincide con ningún archivo, el patrón se pasa como un argumento no expandido que causa confusión y posiblemente errores. Por ejemplo, si no hay ningún archivo no oculto en el directorio actual
ls *
En realidad llamará a ls
con los dos argumentos ls
y *
. Y como no hay ningún archivo, ninguno llamado *
o verá un mensaje de error de ls (no el shell) como:ls: cannot access *: No such file or directory
, que se sabe que hace que la gente piense que fue ls
eso en realidad estaba expandiendo los globos.
El problema es aún peor en casos como:
rm -- *.[ab]
Si no hay *.a
ni *.b
archivo en el directorio actual, entonces podría terminar eliminando un archivo llamado *.[ab]
por error (csh
, tcsh
y zsh
informaría una no coincidencia error y no llamaría a rm
(y fish
no es compatible con [...]
comodines)).
Si desea pasar un *
literal a ls
, tienes que citar eso *
carácter de alguna manera como en ls *
o ls '*'
o ls "*"
. En shells similares a POSIX, el globbing se puede deshabilitar por completo usando set -o noglob
o set -f
(este último no funciona en zsh
a menos que esté en sh
/ksh
emulación).
.