¿por qué ls -d también enumera los archivos y dónde está documentado?

48
  • al especificar ls --directory a*que solo debe enumerar los directorios que comienzan cona*
  • PERO enumera archivos y directorios que comienzan con a

Preguntas :

  • ¿Dónde puedo encontrar documentación sobre esto, aparte de many infodónde creo que busqué a fondo?
  • funciona esto solo en BASH?
erch
fuente
1
Vea también esta otra pregunta muy similar .
Stéphane Chazelas
55
echo a*también le mostrará una lista de los archivos que comienzan con a(si corresponde). Solo para dejar más claro que no está lshaciendo esto sino la fiesta.
ash108

Respuestas:

89

La sintaxis a*y la *a*implementa el shell, no el lscomando.

Cuando escribes

ls a*

en su shell del sistema, la cáscara se expande a*a una lista de todos los archivos existentes en el directorio actual cuyos nombres comienzan con a. Por ejemplo, podría expandirse a*a la secuencia a1 a2 a3y pasarlos como argumentos a ls. El lscomando en sí mismo nunca ve al *personaje; sólo ve las tres argumentos a1, a2y a3.

Para propósitos de expansión de comodines, "archivos" se refiere a todas las entidades en el directorio actual. Por ejemplo, a1podría ser un archivo normal, a2podría ser un directorio y a3podría ser un enlace simbólico. Todos tienen entradas de directorio, y a la expansión comodín del shell no le importa a qué tipo de entidad se refieren esas entradas.

Prácticamente todos los shells con los que es probable que te encuentres (bash, sh, ksh, zsh, csh, tcsh, ...) implementan comodines. Los detalles pueden variar, pero la sintaxis básica de *coincidencia de cero o más caracteres y la ?coincidencia de cualquier carácter individual es razonablemente consistente.

Para bash en particular, esto se documenta en la sección "Expansión de nombre de archivo" del manual de bash; ejecutar info bashy buscar "Expansión de nombre de archivo", o ver aquí .

El hecho de que esto sea hecho por el shell, y no por comandos individuales, tiene algunas consecuencias interesantes (y a veces sorprendentes). Lo mejor de todo es que el manejo de comodines es consistente para (casi) todos los comandos; Si el shell no hiciera esto, inevitablemente algunos comandos no molestarían, y otros lo harían de maneras sutilmente diferentes que el autor pensó que eran "mejores". (Creo que el shell de comandos de Windows tiene este problema, pero no estoy lo suficientemente familiarizado como para comentar más).

Por otro lado, es difícil escribir un comando para cambiar el nombre de varios archivos. Si tú escribes:

mv *.log *.log.bak

probablemente fallará, ya que *.log.bakse expande en función de los archivos que ya existen en el directorio actual. Hay comandos que hacen este tipo de cosas, pero tienen que usar su propia sintaxis para especificar cómo se van a renombrar los archivos. Algunos comandos (como find) pueden hacer su propia expansión comodín; tienes que citar los argumentos para suprimir la expansión del shell:

find . -name '*.txt' -print

La expansión de comodines del shell se basa completamente en la sintaxis del argumento de la línea de comandos y el conjunto de archivos existentes. No puede verse afectado por el significado del comando. Por ejemplo, si desea mover todos los .logarchivos al directorio principal, puede escribir:

mv *.log ..

Si olvida el ..:

mv *.log

y resulta que hay exactamente dos .logarchivos en el directorio actual, se expandirá a:

mv one.log two.log

que cambiará el nombre one.logy el clobber two.log.

EDITAR : Y después de 52 votos a favor, una aceptación y una insignia de Guru, tal vez debería responder la pregunta en el título.

La opción -du no le dice que solo enumere directorios. Le dice que enumere los directorios como ellos mismos, no sus contenidos. Si le da un nombre a un directorio como argumento para , de manera predeterminada, enumerará el contenido del directorio, ya que generalmente eso es lo que le interesa. La opción le dice que enumere solo el directorio en sí. Esto puede ser particularmente útil cuando se combina con comodines. Si escribe:--directorylsls-d

ls -l a*

lsle dará una lista larga de cada archivo cuyo nombre comienza con a, y de los contenidos de cada directorio cuyo nombre comienza con a. Si solo desea una lista de los archivos y directorios, una línea para cada uno, puede usar:

ls -ld a*

que es equivalente a:

ls -l -d a*

Recuerde nuevamente que el lscomando nunca ve al *personaje.

En cuanto a dónde está documentado esto, man lsle mostrará la documentación para el lscomando en casi cualquier sistema similar a Unix. En la mayoría de los sistemas basados ​​en Linux, el lscomando es parte del paquete GNU coreutils; si tiene el infocomando, ya sea info lso info coreutils lsdebería darle una documentación más completa y definitiva. Otros sistemas, como MacOS, pueden usar diferentes versiones del lscomando y pueden no tener el infocomando; para esos sistemas, use man ls. Y ls --helpmostrará un mensaje de uso relativamente corto (117 líneas en mi sistema) si está utilizando la implementación GNU coreutils.

Y sí, incluso los expertos necesitan consultar la documentación de vez en cuando. Ver también esta broma clásica .

Keith Thompson
fuente
8
@Thomas: se a*expande a una lista de todos los archivos en el directorio actual cuyos nombres comienzan con a.
Keith Thompson,
2
@Thomas: O en cualquier directorio que especifique:ls subdir/a*
Keith Thompson,
1
Para ver realmente el comando ejecutado después de la expansión del shell, echose. Entonces, si quieres saber qué sucede cuando lo haces ls a*, primero ejecutaecho ls a*
Carlos Campderrós
1
o simplemente echo a*... el caparazón se expande globalmente de todos modos
Inútil
2
@sendmoreinfo: Eso depende del shell y la configuración. En csh y tcsh, un error en la expansión global es un error. En bash, shopt -s failglobprovoca el mismo comportamiento. Establecer nullglobpero no failglobhace que, por ejemplo, se *nosuchfile*expanda a una cadena vacía.
Keith Thompson,
27

Ver la respuesta de Keith Thompson; pero para explicar por qué ls --directory a*muestra archivos y directorios: la --directoryopción no suprime los archivos que no son de directorio. En cambio, enumera los directorios como tales, mientras que de lo contrario enumeraría su contenido. Ejemplo:

$ mkdir foo
$ touch foo/bar
$ ls foo
bar
$ ls --directory foo
foo
chirlu
fuente
3
ese ejemplo es más convincente con la -Fopción
Glenn Jackman,
6

Para ser muy explícito, está documentado en la página del manual ls (1) :

-d, --directorio de entradas de directorio de lista en lugar de contenidos, y no desreferencia los enlaces simbólicos

para ser justos, "las entradas en lugar de los contenidos" probablemente podrían ser más explicativas:

Si FILE es un directorio, muestre la entrada del directorio en lugar de enumerar el contenido de ese directorio. Si ARCHIVO es un enlace simbólico, muestre la entrada del enlace en lugar del archivo señalado por el enlace.

msw
fuente
Para ser pedante, mientras que la opción -d puede explicarse (si es tersamente) en la página de manual, la expansión de argumentos y el comodín no están documentados en la página de manual de ls ya que lo hace el shell. Si desactiva la expansión de nombre de archivo en su shell, "ls a *" solo le mostrará un archivo que literalmente se llama 'a *'.
Johnny
Para ser pedante, aunque otros encuestados respondieron a la expansión de shell, ninguno de ellos aborda la escasa explicación y cómo podría ser mal interpretada. Si desea que repita lo que ya se dijo bien, sea más directo.
msw
4

Globbing

Como se ha explicado, la expansión de *(y expansiones similares) se llama globbing en la jerga de Unix, y generalmente es una característica del procesador de comandos (conocido como "shell" en lenguaje Unix). Por lo tanto, el engorde puede usarse en muchos otros lugares también; escriba man 7 globen el shell de una distribución clásica de Linux (o vea esto ) para obtener más información sobre globbing.

A principios de Unix, en globrealidad fue implementado por un programa separado llamado /etc/glob(consulte la página 10 de este antiguo manual de UNIX para obtener documentación al respecto). Hoy en día, es una rutina de código suministrada por bibliotecas de códigos, y es comúnmente utilizada por shells. Fuente : Wikipedia .

Directorios

En cuanto a por qué ls -denumera archivos y directorios ...

La lspágina del manual explica por qué sucede esto, pero con una explicación muy concisa. Así que aquí hay un intento de una explicación ampliada:

Por defecto lsenumera el contenido de los directorios cuando se les da su nombre, y hará lo mismo para los enlaces simbólicos a los directorios. La -dopción significa, "cuando se dan los nombres de los directorios" (que también se pueden dar implícitamente por globbing) , solo muestra los _name_s de los directorios, pero no su contenido. De manera similar, cuando se le proporcione (explícita o implícitamente ) nombre (s) de enlaces simbólicos a directorios , muestre el nombre del archivo del enlace simbólico, no el contenido del directorio al que hace referencia.

La -dopción no tiene nada que ver, por lo que puedo decir, con qué elementos están listados; esto se puede hacer (fuente: aquí ) con find, así: find . -maxdepth 1 -type d. No estoy seguro de si hay una buena manera de hacerlo solo con GNU ls. Aquí hay algunos ejemplos de cómo usar el findcomando.

En resumen: de forma predeterminada, lsmuestra el contenido de los directorios cuando se les asigna su nombre de ruta. -dcambia este comportamiento, mostrando solo el nombre del directorio en sí, como se haría para un archivo estándar.

Abbafei
fuente
3

Documentación no dice que las listas únicas entradas de directorio Bur más bien cuando lsrecibe el nombre de directorio que enumera la entrada del directorio en lugar de su contenido. La mejor manera de entender es con el ejemplo:

> {ice} ~ :10:47 % ls -l / 
total 97
drwxr-xr-x   2 root root  4096 May  3 00:27 bin
drwxr-xr-x   4 root root  1024 May  3 14:17 boot
drwxr-xr-x   2 root root  4096 Apr 29 13:44 cdrom
drwxr-xr-x  18 root root  4420 May  9 09:58 dev
...
lrwxrwxrwx   1 root root    33 May  3 14:16 vmlinuz -> boot/vmlinuz-3.9.0-030900-generic
lrwxrwxrwx   1 root root    29 May  3 11:07 vmlinuz.old -> boot/vmlinuz-3.8.0-19-generic
> {ice} ~ :10:47 % ls -ld /
drwxr-xr-x 25 root root 4096 May  3 14:16 /
defhlt
fuente
2

La documentación es parte del shell . En particular, el shell realiza varias expansiones y sustituciones en un orden particular antes de ejecutar un comando.

La secuencia de expansiones de palabras para un shell compatible con Bourne es

  1. Expansión Tilde
  2. Expansión de Parámetros
  3. Sustitución de comando
  4. Expansión Aritmética
  5. División de campo
  6. Expansión de nombre de ruta
  7. Retiro de cotización

Entonces, el lscomando ni siquiera ve los *caracteres, los argumentos ya están expandidos con todos los archivos que coinciden con el a*patrón global. Esto es diferente a lo que ocurre en Windows, donde cada comando que necesita el nombre de archivo tiene que implementar esta característica.

Jens
fuente
1

La palabra clave que necesita ( man bash) es "Expansión del nombre de ruta".

Hauke ​​Laging
fuente
3
más como "Expansión de nombre de ruta" o "Generación de nombre de archivo". Relacionado con la "coincidencia de patrones", pero no es lo mismo.
Stéphane Chazelas
@StephaneChazelas De hecho, pero la "coincidencia de patrones" es una subsección de "expansión de nombre de ruta" en la página del manual, mientras que "Generación de nombre de archivo" no aparece en absoluto (solo en el documento de información).
Hauke ​​Laging