web-dev-qa-db-fra.com

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

Je sais que l'utilisation de la commande ls listera tous les répertoires. Mais qu'est-ce que le ls * commande faire? Je l'ai utilisé et il répertorie simplement les répertoires. L'étoile devant ls signifie-t-elle la profondeur de la liste des répertoires?

86
Andy M

ls répertorie les fichiers et le contenu des répertoires auxquels il est transmis en tant qu'arguments, et si aucun argument n'est donné, il répertorie le répertoire actuel. Il peut également être transmis un certain nombre d'options qui affectent son comportement (voir man ls Pour plus de détails).

Si ls reçoit un argument appelé *, Il recherchera un fichier ou un répertoire appelé * Dans le répertoire courant et le listera comme n'importe quel autre. ls ne traite pas le caractère * Autrement que par tout autre.

Cependant, si ls * Est une ligne de commande Shell, le Shell étendra ce * En fonction du globbing du Shell correspondant = (également appelé règles Génération de nom de fichier ou Expansion de nom de fichier ).

Alors que différents shells prennent en charge différents opérateurs de globbing, la plupart d'entre eux s'accordent sur le plus simple *. * En tant que modèle signifie n'importe quel nombre de caractères, donc * En tant que glob s'étendra à la liste des fichiers dans les répertoires actuels qui correspondent à ce modèle. Il existe cependant une exception: le caractère point (.) Dans un nom de fichier doit être mis en correspondance explicitement, donc * Se développe en fait à la liste des fichiers et répertoires ne commençant pas par . (par ordre lexicographique).

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

ls -l "foo bar"

ou

'ls' "-l" foo\ bar

Ce sont trois façons d'exécuter exactement la même commande. Dans les 3 cas, la commande ls (qui sera probablement exécutée à partir de /bin/ls À partir d'une recherche de répertoires mentionnés dans $PATH) Passera ces 3 arguments: "ls" , "-l" et "foo bar".

Par ailleurs, dans ce cas, ls traitera la première (à proprement parler second ) comme option.

Maintenant, comme je l'ai dit, différents shells ont différents opérateurs de globbing. Il y a quelques décennies, zsh a introduit l'opérateur **/¹ qui signifie correspondre à n'importe quel niveau de sous-répertoires, abréviation de (*/)# Et ***/ Qui est le même sauf que il suit les liens symboliques tout en descendant les répertoires.

Il y a quelques années (juillet 2003, ksh93o+), ksh93 A décidé de copier ce comportement mais a décidé de le rendre facultatif, et ne couvrait que le cas ** (Pas ***). De plus, alors que ** Seul n'était pas spécial dans zsh ² (signifiait simplement la même chose que * Comme dans d'autres coquilles traditionnelles puisque ** Signifie n'importe quel nombre de caractères suivi de n'importe quel nombre de caractères), dans ksh93, ** signifiait la même chose que **/* (donc tout fichier ou répertoire sous le courant (à l'exception des fichiers cachés).

bash a copié ksh93 Quelques années plus tard (février 2009, bash 4.0), avec la même syntaxe mais une différence malheureuse: ** De bash était comme zsh ' s ***, c'est-à-dire qu'il suivait des liens symboliques lors de la récursivité dans des sous-répertoires, ce qui n'est généralement pas ce que vous voulez qu'il fasse et peut avoir des effets secondaires désagréables. Il a été partiellement corrigé dans bash-4.3 en ce sens que les liens symboliques étaient toujours suivis, mais la récursivité s'est arrêtée là. Il a été entièrement corrigé dans 5.0.

yash a ajouté ** Dans la version 2.0 en 2008, activé avec l'option extended-glob. Son implémentation est plus proche de celle de zsh dans la mesure où ** À lui seul n'est pas spécial. Dans la version 2.15 (2009), il a ajouté *** Comme dans zsh et deux de ses propres extensions: .** Et .*** Pour inclure des répertoires cachés lors de la récursivité ( dans zsh, le D glob qualifier (comme dans **/*(D)) considérera les fichiers et répertoires cachés, mais si vous vous voulez seulement parcourir les répertoires cachés mais pas développer les fichiers cachés, vous avez besoin de ((*|.*)/)#* ou **/[^.]*(D)).

Shell 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 de répertoires. Dans ce shell, cependant, **/* N'est pas identique à **. ** Est plus une extension de * Qui peut s'étendre sur plusieurs répertoires. Dans fish, **/*.c Correspondra à a/b/c.c Mais pas à a.c, Tandis que a**.c Correspondra à a.c Et ab/c/d.c Et zsh, par exemple, **/.* Doivent être écrits .* **/.*. Là, *** Est compris comme ** Suivi de * Donc comme **.

tcsh a également ajouté une option globstar dans la version V6.17.01 (mai 2010) et prend en charge ** Et *** À la zsh.

Ainsi, dans tcsh, bash et ksh93, (Lorsque l'option correspondante est activée (globstar)) ou fish, ** Développe tous les fichiers et répertoires en dessous de l'actuel, et *** Est identique à ** Pour fish, un lien symbolique traversant ** Pour tcsh avec globstar, et la même chose que * Dans bash et ksh93 (Bien qu'il ne soit pas impossible que les futures versions de ces shells soient également traverser les liens symboliques).

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

ls -- *

Ou:

ls ./*

Il y a certaines commandes (cela n'a pas d'importance pour ls) où la seconde est préférable car même avec le -- Certains noms de fichiers peuvent être traités spécialement. C'est le cas de - Pour la plupart des utilitaires de texte, cd et pushd et les noms de fichiers qui contiennent le caractère = Pour awk par exemple. Ajouter ./ À 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 globbing (comme si les fichiers dot sont ignorés ou non, l'ordre de tri, que faire s'il n'y a pas de correspondance ...), voir aussi le $FIGNORE Paramètre dans ksh

De plus, dans chaque shell, mais csh, tcsh, fish et zsh, si le globbing pattern ne correspond à aucun fichier, le modèle est transmis sous forme d'argument non développé, ce qui provoque de la confusion et éventuellement des bogues. Par exemple, s'il n'y a pas de fichier non caché dans le répertoire courant

ls *

Appellera en fait ls avec les deux arguments ls et *. Et comme il n'y a pas de fichier du tout, donc aucun ne s'appelle * Non plus, vous verrez un message d'erreur de ls (pas le Shell) comme: ls: cannot access *: No such file or directory, Qui est connu pour faire croire aux gens que c'est ls qui a réellement étendu les globes.

Le problème est encore pire dans des cas comme:

rm -- *.[ab]

S'il n'y a pas de fichier *.a Ni *.b Dans le répertoire en cours, vous risquez de supprimer un fichier appelé *.[ab] Par erreur (csh, tcsh et zsh signaleraient une erreur no match et n'appelleraient pas rm (et fish ne fait pas ') t prend en charge les caractères génériques [...])).

Si vous voulez passer un littéral * À ls, vous devez citer ce caractère * D'une manière ou d'une autre comme dans ls \* Ou ls '*' ou ls "*". Dans les shells de type POSIX, la globalisation peut être complètement désactivée en utilisant set -o noglob Ou set -f (Ce dernier ne fonctionne pas dans zsh sauf dans sh/ksh émulation).


¹ Bien que (*/)# Ait toujours été pris en charge, il a d'abord été raccourci en tant que ..../ En zsh-2.0 (et éventuellement avant), puis ****/ En 2.1 avant d'obtenir sa forme définitive **/ En 2.2 (début 1992)

² L'option globstarshort , a depuis été ajoutée (en 2015) pour permettre d'utiliser ** Et *** Au lieu de **/* et ***/* respectivement

156
Stéphane Chazelas

La commande ls par défaut est ls .: Liste toutes les entrées du répertoire actuel.

La commande ls * signifie 'exécuter ls sur l'extension de * Modèle de coque '

Le * 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 .. Cela ira d'un niveau en profondeur.

L'interprétation du double ou du triple * les modèles dépendent du shell réellement utilisé.

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

36
Dennis Kaarsemaker

Vous pouvez démystifier l'ensemble du processus en tapant echo au lieu de ls d'abord, pour voir à quoi la commande se développe:

$ echo *
Applications Downloads Documents tmp.html

Donc dans ce cas, ls * se développe en 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 utilisez bash comme shell - la plupart des gens le sont, et différents shells ont un comportement différent. Si vous utilisez ash ou csh ou ksh ou zsh, vous pouvez vous attendre à ce que les choses fonctionnent différemment. C'est le point d'avoir différents obus.

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

$ echo D*
Downloads Documents

Et fait intéressant, une barre oblique de fin fait implicitement partie de tout nom de répertoire. Donc */ ne produira que les répertoires (et les liens symboliques vers les répertoires):

$ echo */
Applications/ Downloads/ Documents/

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

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

Étant donné que le répertoire Downloads ne contient aucun sous-répertoire, il ne se retrouve pas dans la sortie. Ceci est très utile pour examiner uniquement les fichiers que vous souhaitez. J'utilise des commandes comme celle-ci tout le temps:

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

S'il en existe, tous les wp-config.php fichiers qui existent au niveau de base de tout utilisateur public_html répertoire. Ou peut-être pour être plus complet:

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

Cela trouvera tout wp-config.php fichiers dans n'importe quel utilisateur public_html répertoires ou l'un de leurs sous-répertoires, mais il fonctionnera plus efficacement que simplement find /home/ -name wp-config.php car il n'examinera rien mais le public_html répertoires pour chacun des utilisateurs.

12
tylerl

Dans certains shells, y compris bash 4.x avec l'option globstar activée, ** effectuera un glob récursif, des répertoires correspondants décroissants. Des astérisques supplémentaires ne modifient pas davantage cette opération.

10

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 des répertoires (comme le sera '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 bien plus encore). "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 contenant des tirets, etc.).

Le remplacement des caractères génériques du shell ne fonctionnera pas avec un simple astérisque '*' sur les fichiers dot. Pour répertorier uniquement les fichiers dot, utilisez:

ls .??*

0
Razzlephrazz