Le résultat de ls *, ls ** et ls ***

86

Je sais en utilisant la commande lslistera tous les répertoires. Mais que fait la ls *commande? Je l'ai utilisé et il ne fait que lister les répertoires. L'étoile devant lssignifie-t-elle à quelle profondeur elle peut lister les répertoires?

Andy M
la source

Réponses:

155

lsliste les fichiers et le contenu des répertoires, il est transmis en tant qu'arguments, et si aucun argument n'est fourni, il répertorie le répertoire en cours. On peut également lui transmettre un certain nombre d’options qui affectent son comportement (voir man lspour plus de détails).

Si lsun argument appelé est passé *, il cherchera un fichier ou un répertoire appelé *dans le répertoire actuel et le listera comme n'importe quel autre. lsne traite pas le *personnage autrement que par un autre.

Cependant, s'il ls *s'agit d'une ligne de commande shell , le shell l'étendra *conformément aux règles de globbing du shell correspondant (également appelées génération de noms de fichiers ou extension de noms de fichiers ).

Alors que différents shells prennent en charge différents opérateurs globbing, la plupart d’entre eux s’accordent sur le plus simple *. *en tant que modèle, cela signifie un nombre quelconque de caractères, de sorte *qu'un élément globsera étendu à la liste des fichiers des répertoires actuels qui correspondent à ce modèle. Il existe toutefois une exception, à savoir qu'un caractère point ( .) dans un nom de fichier doit être explicitement mis en correspondance. Il est donc *étendu à la liste des fichiers et des répertoires ne commençant pas par .(dans l'ordre lexicographique).

Par exemple, si le répertoire courant contient les fichiers appelés ., .., .foo, -let foo bar, *sera étendu par l'enveloppe à deux arguments pour passer à ls: -let foo bar, donc ce sera comme si vous aviez tapé:

ls -l "foo bar"

ou

'ls' "-l" foo\ bar

Quelles sont les trois manières d'exécuter exactement la même commande? Dans les 3 cas, les 3 arguments: "ls", "-l" et "foo bar" seront transmis à la lscommande (qui sera probablement exécutée à partir /bin/lsd'une recherche dans les répertoires mentionnés dans $PATH).

Incidemment, dans ce cas, lsle premier (à proprement parler le second ) sera traité comme une option.

Maintenant, comme je l'ai dit, différents obus ont différents opérateurs globbing. Il y a quelques décennies, a été zshintroduit l' **/opérateur¹ qui correspond à n'importe quel niveau de sous-répertoires, abréviation de (*/)#et ***/qui est identique, sauf qu'il suit des liens symboliques lors de la descente des répertoires.

Il y a quelques années (juillet 2003 ksh93o+), a ksh93décidé de copier ce comportement mais a décidé de le rendre facultatif et ne couvrait que le **cas (non ***). De plus, alors que **seul n’était pas spécial zsh(signifie exactement la même chose que *dans d’autres shells traditionnels, car **signifie un nombre quelconque de caractères suivi d’un nombre quelconque de caractères), en ksh93, **signifie la même chose que **/*(ainsi, tout fichier ou répertoire situé en dessous du nom actuel). (excluant les fichiers cachés).

bashcopié ksh93quelques années plus tard (Février 2009, bash 4.0), avec la même syntaxe , mais une différence malheureuse: années bash **était comme zshl » ***, qui est elle suivait les liens symboliques lors récursion dans les sous-répertoires qui ne sont généralement pas ce que vous voulez faire et peut avoir des effets secondaires désagréables. Il a été partiellement corrigé dans bash-4.3 en ce que les liens symboliques étaient toujours suivis, mais la récursivité s’arrêtait là. Il a été entièrement corrigé dans la version 5.0.

yashajouté **dans la version 2.0 en 2008, activé avec l' extended-globoption. Sa mise en œuvre est plus proche de zshcelle dans laquelle **seule n'est pas spéciale. Dans la version 2.15 (2009), il ajoutait ***like in zshet deux de ses propres extensions: .**et .***pour inclure les répertoires cachés lors du récursif (dans zsh, le D qualificatif glob (comme dans **/*(D)) considérera les fichiers et répertoires cachés, mais si vous ne voulez que parcourir mais ne développez pas les fichiers cachés, vous devez ((*|.*)/)#*ou **/[^.]*(D)).

La coquille de poisson prend également en charge **. Comme la version précédente de bash, il suit les liens symboliques lors de la descente de l’arborescence. Dans cette coquille cependant ce **/*n'est pas la même chose que **. **est plutôt une extension *qui peut s’étendre sur plusieurs répertoires. En fish, **/*.ccorrespondra a/b/c.cmais non a.c, tout en a**.ccorrespondront a.cet ab/c/d.cet zshest , **/.*par exemple , doit être écrit .* **/.*. Là, ***est compris comme **suivi par *donc le même que **.

tcsha également ajouté une globstaroption dans la version 6.11.01 (mai 2010) et prend en charge les deux méthodes **et ***à la fois zsh.

Donc, dans tcsh, bashet ksh93, (lorsque l’option correspondante est activée ( globstar)) ou fish, **développe tous les fichiers et répertoires situés sous le répertoire actuel, et ***est identique à **pour fish, un lien symbolique traversant **pour tcshavec globstar, et identique à *dans bashet ksh93(bien que Il n’est pas impossible que les futures versions de ces shells traversent également des liens symboliques).

Ci-dessus, vous avez remarqué la nécessité de vous assurer qu'aucune des extensions ne soit interprétée comme une option. Pour cela, vous feriez:

ls -- *

Ou:

ls ./*

Il existe certaines commandes (peu importe ls) dans lesquelles la seconde est préférable, car même avec --certains noms de fichiers, ceux-ci peuvent être traités de manière spécifique. Il est le cas de -la plupart des services publics de texte, cdet , pushdet les noms de fichiers contenant le =caractère pour awkpar exemple. La présélection ./de tous les arguments supprime leur signification particulière (au moins pour les cas mentionnés ci-dessus).

Il convient également de noter que la plupart des shells ont un certain nombre d’options qui affectent le comportement de globalisation (par exemple, si les fichiers de points sont ignorés ou non, l’ordre de tri, ce qu’il faut faire s’il n’ya pas de correspondance ...), voir aussi le $FIGNOREparamètre dansksh

En outre, dans chaque coquille , mais csh, tcsh, fishet zsh, si le modèle englobement ne correspond à aucun fichier, le modèle est passé comme un argument non dilatée qui provoque la confusion et peut - être des bugs. Par exemple, s'il n'y a pas de fichier non caché dans le répertoire en cours

ls *

Appellera en fait lsavec les deux arguments lset *. Et comme il n'y a pas de fichier du tout, donc pas appelé *non plus, vous verrez un message d'erreur de la part de ls (pas le shell) comme suit:, ls: cannot access *: No such file or directoryqui a été reconnu pour faire croire aux gens que c'était en lstrain d'agrandir les globs.

Le problème est encore pire dans les cas suivants:

rm -- *.[ab]

S'il n'y a pas *.ani *.bfichier dans le répertoire courant, alors vous pourriez finir par la suppression d' un fichier appelé *.[ab]par erreur ( csh, tcshet zshinscrira un pas de match erreur et ne serait pas appeler rm(et fishne supporte pas les [...]jokers)).

Si vous voulez passer un littéral *à ls, vous devez citer ce *caractère d'une manière quelconque dans ls \*ou ls '*'ou ls "*". Dans les shells de type POSIX, le globbing peut être désactivé en utilisant set -o noglobou set -f(ce dernier ne fonctionnant zshque dans sh/ kshémulation).


¹ Alors qu’il (*/)#a toujours été soutenu, il a d'abord été raccourci comme ..../dans zsh-2.0 (et potentiellement auparavant), puis ****/dans 2.1 avant d’obtenir sa forme définitive **/dans 2.2 (début 1992).

Stéphane Chazelas
la source
22
Vraiment super réponse!
Andrea Corbellini
7
Un autre bel exemple est find -name *. Surtout avec des modèles plus complexes, s'il y a exactement une correspondance dans le répertoire actuel, les gens ne se rendront souvent pas compte qu'ils ne transmettent pas l'astérisque à find.
jeudi
Je continue à donner +1, je suis vraiment heureux de voir Stéphane Chazelas actif ici sur unix.stackexchange.com ! Excellentes contributions, Stéphane, comme toujours!
Dimitre Radoulov
1
Dans le poisson, *.[ab]essayerait de supprimer tout ce qui se termine par .[ab]. [n'est pas spécial dans le poisson.
Konrad Borowski
35

La commande par lsdéfaut est ls .: Liste toutes les entrées du répertoire en cours .

La commande ls *signifie 'run ls sur le développement du *motif shell'

Le *modèle est traité par le shell et s'étend à toutes les entrées du répertoire en cours, à l'exception de celles commençant par un .. Il ira un niveau de profondeur.

L’interprétation des *motifs doubles ou triples dépend de la coque utilisée.

*est un caractère générique qui correspond à 0 ou plusieurs caractères. Certains coquillages modernes se retrouveront dans des sous-répertoires en voyant le **motif.

Dennis Kaarsemaker
la source
17
Sauf que supplémentaire * faire ajouter quelque chose, voir l'autre réponse expliquant la globstar. Il faut également préciser que cela n’a rien à voir avec les astérisques, il n’a jamais dépassé aucun de ces astérisques. Exécuter echo ls *pour voir ce qui serait exécuté lorsque vous écrivez ls *.
mardi
12

Vous pouvez démystifier l'ensemble du processus en tapant à la echoplace de la lspremière, pour voir en quoi la commande se développe:

$ echo *
Applications Downloads Documents tmp.html

Donc, dans ce cas, ls *étend àls Applications Downloads Documents tmp.html

$ echo **
Applications Downloads Documents tmp.html

$ echo ***
Applications Downloads Documents tmp.html

Donc pas de changement. Cela suppose que vous l'utilisiez bashcomme coquille - la plupart des gens le sont, et différentes coquilles ont un comportement différent. Si vous utilisez ashou cshou kshou zsh, vous pouvez vous attendre à ce que les choses fonctionnent différemment. C'est le point d'avoir différentes coquilles.

Essayons donc quelque chose de différent (toujours avec bash) afin que nous ayons une idée de ce que l’ *opérateur globbing ( ) peut faire pour nous. Par exemple, nous pouvons filtrer par partie du nom:

$ echo D*
Downloads Documents

Et curieusement, une barre oblique de fin fait implicitement partie de tout nom de répertoire. Ainsi */, seuls les répertoires (et les liens symboliques vers les répertoires) seront générés:

$ echo */
Applications/ Downloads/ Documents/

Et nous pouvons filtrer à plusieurs niveaux en plaçant des barres obliques au milieu:

$ echo D*/*/
Documents/Work/ /Documents/unfinished/

Comme le Downloadsrépertoire ne contient aucun sous-répertoire, il ne se retrouve pas dans la sortie. Ceci est très utile pour simplement examiner les fichiers que vous voulez. J'utilise des commandes comme ça tout le temps:

$ ls -l /home/*/public_html/wp-config.php

Cela répertorie, le cas échéant, tous les wp-config.phpfichiers existant au niveau de base du public_htmlrépertoire de tout utilisateur . Ou peut-être pour être plus complet:

$ find /home/*/public_html/ -name wp-config.php

Ceci trouvera des wp-config.phpfichiers dans de tout utilisateur public_htmlrépertoires ou l' un de leurs sous - répertoires, mais il fonctionnera plus efficacement que juste find /home/ -name wp-config.phpparce qu'il n'examinera rien , mais les public_htmlrépertoires pour chacun des utilisateurs.

tylerl
la source
1
"Cela suppose que vous utilisez bashvotre shell" ← et que le globstar n'est pas activé. shopt -s globstaret essayez à nouveau ...
njsg
Un autre moyen de démystifier est de set -xcommencer à imprimer la commande "réelle" exécutée à chaque fois (désactiver avec set +x).
ShreevatsaR
10

Dans certains shells, y compris bash 4.x avec l' globstaroption activée, **effectuera un glob récurrent, des répertoires décroissants correspondants. Les astérisques supplémentaires ne modifient plus cette opération.

Ignacio Vazquez-Abrams
la source
1
Comme je l'ai dit dans ma réponse cependant, méfiez-vous que contrairement à ksh93et zsh, bashtraverse les liens symboliques dans la récursion, ce qui est généralement indésirable.
Stéphane Chazelas
Cela a maintenant été corrigé à la bash 4.3
Stéphane Chazelas
1

Si vous voulez "plonger profondément", utilisez l'option ls -R (récursive), ou utilisez find, comme ceci:

find . -ls

"find" plongera au bas de l'arborescence (comme le fera 'ls -R'), et a beaucoup plus d'options, comme lister les répertoires (-type d), les fichiers uniquement (-type f) ou afficher les fichiers ayant d'autres caractéristiques (aucun utilisateur dans / etc / passwd, autorisations spécifiques, et beaucoup plus). "find" est également un peu plus sûr dans les scripts (en raison de règles de globalisation incohérentes entre les shells, ainsi que des échappements spéciaux pour les fichiers comportant des tirets, etc.).

La mise en forme générique de shell ne fonctionnera pas uniquement avec un astérisque '*' sur les fichiers de points. Pour lister uniquement les fichiers de points, utilisez:

ls .??*

Razzlephrazz
la source
-1

Extra * n'ajoute pas de niveau de profondeur. Mais si vous essayez

 ls */*/*

- vous obtiendrez la liste des sous-dossiers des sous-dossiers dans les dossiers du dossier actuel ...

VB9-UANIC
la source
Faux. Selon le shell, extra *ajoutera un niveau de profondeur.
jeudi
Alors, quelle coque ajoutera un niveau de profondeur aux étoiles supplémentaires?
VB9-UANIC
Nombreuses. Y compris GNU bashavec l’option globstar, le korn shell et zsh. Et peut-être d'autres, je suppose. unix.stackexchange.com/a/62665/14831
njsg
-1

Ma prise principale est que echo ** / *. Ext sera généralement ...

Peut ou peut ne pas: lister un fichier .ext dans le répertoire courant

Peut ou peut ne pas: inclure les fichiers. *. Ext

Généralement, je préférerais que l'expression globale soit '** /' et que cela puisse donner une chaîne nulle (pas de sous-répertoire). Voici comment je convertis les expressions globales en entrée de programme. Bien sûr, je m'assure que des commentaires l'expliquent dans des exemples d'utilisation.

anthony
la source