pourquoi ls -d liste-t-il également les fichiers et où est-il documenté?

48
  • lors de la spécification ls --directory a*, ne lister que les répertoires commençant para*
  • MAIS il répertorie les fichiers ET les répertoires commençant par a

Questions :

  • Où pourrais-je trouver de la documentation à ce sujet, autre que manet infooù je pense avoir bien regardé?
  • cela fonctionne-t-il uniquement dans BASH?
erch
la source
1
Voir aussi cette autre question très similaire .
Stéphane Chazelas
5
echo a*vous indiquera également les fichiers commençant par a(le cas échéant). Juste pour que ce soit plus clair, ce n'est pas lscela qui se fait, mais la bash
ash108

Réponses:

89

La syntaxe a*et *a*est implémentée par le shell, pas par la lscommande.

Quand vous tapez

ls a*

à l'invite de votre shell, celui-ci se développe a*en une liste de tous les fichiers existants dans le répertoire en cours dont le nom commence par a. Par exemple, il peut être étendu a*à la séquence a1 a2 a3et transmis comme arguments à ls. La lscommande elle-même ne voit jamais le *personnage; il ne voit que les trois arguments a1, a2et a3.

Aux fins d'expansion des caractères génériques, "fichiers" fait référence à toutes les entités du répertoire actuel. Par exemple, il a1peut s'agir d'un fichier normal, d' a2un répertoire et d' a3un lien symbolique. Ils ont tous des entrées de répertoire, et l'extension générique du shell ne tient pas compte du type d'entité auquel ces entrées font référence.

Pratiquement tous les shells rencontrés (bash, sh, ksh, zsh, csh, tcsh, ...) implémentent des caractères génériques. Les détails peuvent varier, mais la syntaxe de base consistant à faire *correspondre zéro ou plusieurs caractères et à faire ?correspondre n'importe quel caractère est raisonnablement cohérente.

Pour bash en particulier, cela est documenté dans la section "Développement du nom de fichier" du manuel bash; lancez info bashet cherchez "extension de nom de fichier", ou voyez ici .

Le fait que cela soit fait par le shell, et non par des commandes individuelles, a des conséquences intéressantes (et parfois surprenantes). La meilleure chose à ce sujet est que la gestion des caractères génériques est cohérente pour (presque) toutes les commandes; Si le shell ne le faisait pas, certaines commandes ne le dérangeraient inévitablement pas, et d'autres le feraient de manières légèrement différentes que l'auteur pensait "meilleures". (Je pense que le shell de commande Windows a ce problème, mais je ne le connais pas suffisamment pour commenter davantage.)

D'autre part, il est difficile d'écrire une commande pour renommer plusieurs fichiers. Si vous écrivez:

mv *.log *.log.bak

il échouera probablement car il *.log.bakest développé en fonction des fichiers déjà présents dans le répertoire en cours. Certaines commandes font ce genre de choses, mais elles doivent utiliser leur propre syntaxe pour spécifier comment les fichiers doivent être renommés. Certaines commandes (telles que find) peuvent créer leur propre développement générique. vous devez citer les arguments pour supprimer l'expansion du shell:

find . -name '*.txt' -print

Le développement des caractères génériques du shell repose entièrement sur la syntaxe de l'argument de ligne de commande et de l'ensemble des fichiers existants. Il ne peut pas être affecté par le sens de la commande. Par exemple, si vous souhaitez déplacer tous les .logfichiers vers le répertoire parent, vous pouvez taper:

mv *.log ..

Si vous oubliez le ..:

mv *.log

et il se trouve qu'il y a exactement deux .logfichiers dans le répertoire courant, il va s'étendre à:

mv one.log two.log

qui va renommer one.loget clobber two.log.

EDIT : Et après 52 votes positifs, un accept et un badge Guru, je devrais peut-être répondre à la question dans le titre.

L' option -dou ne dit pas de ne lister que les répertoires. Il lui dit de lister les répertoires comme eux-mêmes, pas leur contenu. Si vous donnez un nom de répertoire comme argument , par défaut, le contenu du répertoire sera répertorié , car c'est généralement ce qui vous intéresse. L' option lui dit de ne répertorier que le répertoire lui-même. Cela peut être particulièrement utile lorsqu'il est combiné avec des caractères génériques. Si vous tapez:--directorylsls-d

ls -l a*

lsvous donnera une longue liste de chaque fichier dont le nom commence par a, et du contenu de chaque répertoire dont le nom commence par a. Si vous voulez juste une liste des fichiers et des répertoires, une ligne pour chacun, vous pouvez utiliser:

ls -ld a*

qui équivaut à:

ls -l -d a*

Rappelez-vous à nouveau que la lscommande ne voit jamais le *personnage.

Quant à savoir où cela est documenté, man lsvous montrera la documentation de la lscommande sur n'importe quel système de type Unix. Sur la plupart des systèmes basés sur Linux, la lscommande fait partie du paquet GNU coreutils; si vous avez la infocommande, vous devriez info lsou info coreutils lsvous fournir une documentation plus complète et plus définitive. D'autres systèmes, tels que MacOS, peuvent utiliser différentes versions de la lscommande et ne pas en disposer info; pour ces systèmes, utilisez man ls. Et ls --helpaffichera un message d'utilisation relativement court (117 lignes sur mon système) si vous utilisez la mise en œuvre GNU coreutils.

Et oui, même les experts doivent consulter la documentation de temps en temps. Voir aussi cette blague classique .

Keith Thompson
la source
8
@Thomas: a*étend la liste de tous les fichiers du répertoire actuel dont le nom commence par a.
Keith Thompson
2
@Thomas: Ou dans le répertoire que vous spécifiez:ls subdir/a*
Keith Thompson
1
Pour voir vraiment la commande exécutée après le développement du shell, echoil faut. Donc, si vous voulez savoir ce qui se passe quand vous le faites ls a*, exécutez d'abordecho ls a*
Carlos Campderrós
1
ou juste echo a*... la coquille ne fait que glob l'expansion
Inutile
2
@sendmoreinfo: Cela dépend du shell et des paramètres. Dans csh et tcsh, une expansion globale défaillante est une erreur. En bash, shopt -s failglobprovoque le même comportement. Définir nullglobmais ne failglobprovoque pas , par exemple, *nosuchfile*pour développer une chaîne vide.
Keith Thompson
27

Voir la réponse de Keith Thompson; mais pour expliquer pourquoi ls --directory a*affiche les fichiers et les répertoires: L' --directoryoption ne supprime pas les fichiers autres que des répertoires. Au lieu de cela, il répertorie les répertoires en tant que tels, alors qu'il listerait autrement leur contenu. Exemple:

$ mkdir foo
$ touch foo/bar
$ ls foo
bar
$ ls --directory foo
foo
chirlu
la source
3
cet exemple est plus convaincant avec l' -Foption
Glenn Jackman
6

Pour être très explicite, il est documenté dans la page de manuel ls (1) :

-d, --directory liste les entrées du répertoire au lieu du contenu, et ne déréférence pas les liens symboliques

pour être juste, "entrées au lieu de contenu" pourrait probablement être plus explicatif:

Si FILE est un répertoire, affichez l'entrée de répertoire elle-même au lieu de répertorier le contenu de ce répertoire. Si FILE est un lien symbolique, affichez l'entrée de lien elle-même au lieu du fichier pointé par le lien.

msw
la source
Pour être pédant, bien que l'option -d puisse être expliquée (si elle est concise) dans la page de manuel, le développement d'arguments et les caractères génériques ne sont pas décrits dans la page de manuel de ls car ils sont effectués par le shell. Si vous désactivez l'expansion du nom de fichier dans votre shell, "ls a *" ne vous montrera qu'un fichier qui s'appelle littéralement "a *".
Johnny
Pour être pédant, bien que d’autres répondants aient répondu à l’expansion du shell, aucun d’entre eux n’a répondu à l’explication limitée et à la façon dont elle pourrait être mal interprétée. Si vous souhaitez que je répète ce qui a déjà été bien dit, soyez plus direct, s'il vous plaît.
Mardi
4

Globbing

Comme cela a été expliqué, le développement de *(et les développements similaires) s'appelle globbing dans le jargon Unix et est généralement une fonctionnalité du processeur de commande (appelé le "shell" dans le langage Unix). Donc globbing peut être utilisé dans beaucoup d'autres endroits aussi; tapez man 7 globsur le shell d'une distribution Linux classique (ou voyez ceci ) pour en savoir plus sur globbing.

Au début, Unix globétait en réalité implémenté par un programme séparé appelé /etc/glob(reportez-vous à la page 10 de cet ancien manuel UNIX pour la documentation correspondante). De nos jours, il s'agit d'une routine de code fournie par les bibliothèques de codes et couramment utilisée par les shells. Source : Wikipedia .

Répertoires

Pourquoi les ls -dlistes de fichiers ainsi que les répertoires ...

La lspage de manuel explique pourquoi cela se produit, mais avec une explication très concise. Donc, voici une tentative d'explication élargie:

Par défaut, lsrépertorie le contenu des répertoires quand on leur donne leur nom, et fera la même chose pour les liens symboliques vers les répertoires. L' -doption signifie "lorsque le ou les noms de répertoire (s) sont donnés" (qui peut également être donné implicitement par globbing) , affiche uniquement les _name_s du / des répertoire (s), mais pas leur contenu. De même, lorsque le nom (ou les liens) des liens symboliques vers les répertoires sont fournis (explicitement ou implicitement ) , affichez le nom du fichier du lien symbolique, pas le contenu du répertoire auquel il fait référence.

L' -doption n'a rien à voir, autant que je sache, avec quels éléments sont listés; cela peut être fait (source: ici ) avec find, comme suit: find . -maxdepth 1 -type d. Je ne suis pas sûr s'il existe un bon moyen de le faire uniquement avec GNU ls. Voici quelques exemples d'utilisation de la findcommande.

Donc, pour résumer: Par défaut, lsaffiche le contenu des répertoires lorsque leur nom de chemin est donné. -dmodifie ce comportement en affichant uniquement le nom du répertoire lui-même, comme ce serait le cas pour un fichier standard.

Abbafei
la source
3

La documentation n'indique pas qu'elle répertorie uniquement les entrées de répertoire, mais plutôt quand elle lsreçoit un nom de répertoire, elle répertorie l'entrée dir au lieu de son contenu. Le meilleur moyen de comprendre est par exemple:

> {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
la source
2

La documentation fait partie du shell . En particulier, le shell effectue diverses expansions et substitutions dans un ordre particulier avant d'exécuter une commande.

La séquence d’ expansion de mots pour un shell compatible Bourne est

  1. Expansion Tilde
  2. Paramètre Expansion
  3. Substitution de commande
  4. Expansion arithmétique
  5. Division de champ
  6. Expansion du chemin
  7. Enlèvement de devis

Donc, la lscommande ne voit même pas les *caractères, les arguments sont déjà développés avec tous les fichiers correspondant au a*motif glob. Ceci est différent de Windows, où chaque commande nécessitant une suppression de nom de fichier doit implémenter cette fonctionnalité elle-même.

Jens
la source
1

Le mot clé dont vous avez besoin ( man bash) est "Expansion du nom de chemin".

Hauke ​​Laging
la source
3
plus comme "Expansion du nom de chemin" ou "Génération de nom de fichier". Relatif à "pattern matching", mais pas le même.
Stéphane Chazelas
@StephaneChazelas En effet, mais "correspondance de motif" est une sous-section de "développement du nom de chemin" dans la page de manuel, alors que "Génération de noms de fichiers" n'y figure pas du tout (uniquement dans le document d'information).
Hauke ​​Laging