Quelle est la différence entre «du -sh *» et «du -sh ./*»?

26

Quelle est la différence entre du -sh *et du -sh ./*?

Remarque: Ce qui m'intéresse, ce sont les pièces *et ./*.

Biswanath
la source
2
le résultat ? on vous montrera ./ devant le nom du fichier
Kiwy

Réponses:

69
$ touch ./-c $ 'a \ n12 \ tb' foo
$ du -hs *
0 a
12 b
0 foo
0 au total

Comme vous pouvez le voir, le -cfichier a été pris en option duet n'est pas signalé (et vous voyez la totalligne à cause de du -c). De plus, le fichier appelé a\n12\tbnous fait penser qu'il existe des fichiers appelés aet b.

$ du -hs -- *
0       a
12      b
0       -c
0       foo

C'est mieux. Au moins, ce temps -cn'est pas pris en option.

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

C'est même mieux. Le ./préfixe empêche -cd'être pris comme option et l'absence d' ./avant bdans la sortie indique qu'il n'y a pas de bfichier dedans, mais il y a un fichier avec un caractère de nouvelle ligne (mais voir ci-dessous 1 pour plus de digressions à ce sujet).

Il est recommandé d'utiliser le ./préfixe lorsque cela est possible, et dans le cas contraire et pour les données arbitraires, vous devez toujours utiliser:

cmd -- "$var"

ou:

cmd -- $patterns

Si cmdne prend pas en charge --pour marquer la fin des options, vous devez le signaler comme un bug à son auteur (sauf quand c'est par choix et documenté comme pour echo).

Il y a des cas où ./*ça --ne résout pas les problèmes . Par exemple:

awk -f file.awk -- *

échoue s'il y a un fichier appelé a=b.txtdans le répertoire courant (définit la variable awk aau b.txtlieu de lui dire de traiter le fichier).

awk -f file.awk ./*

N'a pas le problème car ./an'est pas un nom de variable awk valide, donc ./a=b.txtn'est pas considéré comme une affectation de variable.

cat -- * | wc -l

échoue s'il y a un fichier appelé -dans le répertoire courant, car cela indique catde lire à partir de son stdin ( -est spécial pour la plupart des utilitaires de traitement de texte et pour cd/ pushd).

cat ./* | wc -l

est OK car ./-n'est pas spécial pour cat.

Des choses comme:

grep -l -- foo *.txt | wc -l

compter le nombre de fichiers qui contiennent foosont faux car il suppose que les noms de fichiers ne contiennent pas de caractères de nouvelle ligne ( wc -lcompte les caractères de nouvelle ligne, ceux produits par greppour chaque fichier et ceux dans les noms de fichiers eux-mêmes). Vous devez utiliser à la place:

grep -l foo ./*.txt | grep -c /

(compter le nombre de /caractères est plus fiable car il ne peut y en avoir qu'un par nom de fichier).

Pour récursif grep, l'astuce équivalente est d'utiliser:

grep -rl foo .//. | grep -c //

./* peut cependant avoir des effets secondaires indésirables.

cat ./*

ajoute deux caractères supplémentaires par fichier, ce qui vous permettrait d'atteindre la limite de la taille maximale des arguments + environnement plus tôt. Et parfois, vous ne voulez pas que cela ./soit signalé dans la sortie. Comme:

grep foo ./*

Produirait:

./a.txt: foobar

au lieu de:

a.txt: foobar

Digressions supplémentaires

1 . Je sens que je dois développer cela ici, après la discussion dans les commentaires.

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

Ci-dessus, le fait de ./marquer le début de chaque fichier signifie que nous pouvons clairement identifier où chaque nom de fichier commence (à ./) et où il se termine (à la nouvelle ligne avant la prochaine ./ou la fin de la sortie).

Cela signifie que la sortie de du ./*, contrairement à celle de du -- *) peut être analysée de manière fiable, mais pas si facilement dans un script.

Lorsque la sortie est envoyée à un terminal, il existe de nombreuses autres façons dont un nom de fichier peut vous tromper:

  • Les caractères de contrôle, les séquences d'échappement peuvent affecter la façon dont les choses sont affichées. Par exemple, \rdéplace le curseur au début de la ligne, \bdéplace le curseur vers l'arrière, \e[Cvers l'avant (dans la plupart des terminaux) ...
  • de nombreux caractères sont invisibles sur un terminal en commençant par le plus évident: le caractère espace.
  • Certains caractères Unicode sont identiques à la barre oblique de la plupart des polices

    $ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
    /    

    (voir comment ça se passe dans votre navigateur).

Un exemple:

$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*
0       ./x
0       ./x
0       ./x
0       .∕x
0       ./x
0       ./x

Beaucoup de xmais yest manquant.

Certains outils comme GNUls remplaceraient les caractères non imprimables par un point d'interrogation (notez que (U + 2215) est imprimable cependant) lorsque la sortie est envoyée à un terminal. GNU dune le fait pas.

Il y a moyen de les faire se révéler:

$ ls
x  x   x?0?.∕x  y  y?0?.?[Cx  y?x
$ LC_ALL=C ls
x  x?0?.???x  x   y  y?x  y?0?.?[Cx

Voyez à quel point nous nous sommes tournés ???après avoir dit lsque notre jeu de caractères était ASCII.

$ du -hs ./* | LC_ALL=C sed -n l
0\t./x$
0\t./x $
0\t./x$
0\t.\342\210\225x$
0\t./y\r0\t.\033[Cx$
0\t./y\bx$

$marque la fin de la ligne, afin que nous puissions repérer le "x"vs "x ", tous les caractères non imprimables et les caractères non ASCII sont représentés par une séquence de barre oblique inverse (la barre oblique inverse elle-même serait représentée avec deux barres obliques inverses), ce qui signifie qu'elle n'est pas ambiguë. C'était GNU sed, il devrait être le même dans toutes les sedimplémentations conformes POSIX mais notez que certaines anciennes sedimplémentations ne sont pas aussi utiles.

$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$

(pas standard mais assez courant, également cat -Aavec certaines implémentations). Celui-ci est utile et utilise une représentation différente mais est ambigu ( "^I"et <TAB>est affiché le même par exemple).

$ du -hs ./* | od -vtc
0000000   0  \t   .   /   x  \n   0  \t   .   /   x      \n   0  \t   .
0000020   /   x  \n   0  \t   . 342 210 225   x  \n   0  \t   .   /   y
0000040  \r   0  \t   . 033   [   C   x  \n   0  \t   .   /   y  \b   x
0000060  \n
0000061

Celui-ci est standard et sans ambiguïté (et cohérent d'une implémentation à l'autre) mais pas aussi facile à lire.

Vous remarquerez que yjamais n'apparaît au-dessus. C'est un problème complètement sans rapport avec du -hs *qui n'a rien à voir avec les noms de fichiers, mais il convient de le noter: parce qu'il dusignale l'utilisation du disque, il ne signale pas d'autres liens vers un fichier déjà répertorié (toutes les duimplémentations ne se comportent pas comme cela cependant lorsque les liens matériels sont répertoriés sur la ligne de commande).

Stéphane Chazelas
la source
+1, agréable et complet (pour autant que je sache ^^). J'aime particulièrement l'avantage "grep -c /". A noter également: l'avantage de "./*" sur "*" apparaît dans l'une des (nombreuses) bonnes réponses de la FAQ Unix (probablement sur faqs.org. Iirc, c'est dans la question des fichiers rm-ing commençant par une "-").
Olivier Dulac
… Et ce n'est pas une mauvaise pratique d'avoir des fichiers avec des nouvelles lignes et des tabulations dans leurs noms? Je sais que j'essaie de limiter les noms à [a-z0-9.+-].
Blacklight Shining
5
@BlacklightShining, c'est très mauvais de voler des voitures, mais c'est mauvais de laisser votre voiture déverrouillée (ignorer les nouvelles lignes), surtout quand c'est une voiture chère (script s'exécutant en tant qu'utilisateur privilégié, sur un serveur avec des données sensibles ...) ou lorsque vous le garer dans une zone accidentée ( /tmp) ou dans une zone avec beaucoup de voitures chères ( $HOME) et c'est encore pire d'aller sur un site de questions / réponses et de dire que c'est toujours bien de ne pas verrouiller votre voiture sans spécifier dans quelles conditions (dans un garage verrouillé, script vous avez écrit exécuté par vous-même uniquement sur une machine non connectée à un réseau ou à un stockage amovible ...)
Stéphane Chazelas
1
@BlacklightShining, les sauts de ligne sont inhabituels mais les espaces intégrés sont très courants de nos jours, en particulier pour les fichiers créés via l'interface graphique.
alexis
2
@BlacklightShining, oui bien que celui-ci (comme "b "ou "a\bb") tromperait un utilisateur sur un terminal mais pas un script analysant la sortie de du ./*. Je devrais probablement ajouter une note à ce sujet. Fera demain. Notez que plus tôt, je voulais dire privilégié au sens général, non root(bien que cela s'applique d'autant plus rootbien sûr). les sauts de ligne sont autorisés, les ignorer est un bug. les bogues ont l'habitude d'être exploités. Vous devez mesurer le risque au cas par cas. De bonnes pratiques de codage peuvent éviter les problèmes dans de nombreux cas. Certes, sur SE, nous devons sensibiliser.
Stéphane Chazelas
6

Il n'y a aucune différence entre a *et ./*en termes de fichiers listés. La seule différence serait avec le 2ème formulaire, chaque fichier aurait une barre oblique ./préfixée devant eux, ce qui signifie généralement le répertoire actuel.

N'oubliez pas que le .répertoire est une notation abrégée pour le répertoire actuel.

$ ls -la | head -4
total 28864
drwx------. 104 saml saml    12288 Jan 23 20:04 .
drwxr-xr-x.   4 root root     4096 Jul  8  2013 ..
-rw-rw-r--.   1 saml saml      972 Oct  6 20:26 abcdefg

Vous pouvez vous convaincre que ces 2 listes sont essentiellement la même chose en utilisant echopour voir à quoi le shell les étendrait.

$ echo *
$ echo ./*

Ces 2 commandes listeront tous les fichiers de votre répertoire actuel.

Exemples

Nous pouvons faire de fausses données comme ceci:

$ touch file{1..5}
$ ll
total 0
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file1
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file2
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file3
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file4
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file5

Maintenant, lorsque nous utilisons les echocommandes ci-dessus, nous voyons la sortie suivante:

$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5

Cette différence peut sembler inutile mais il y a des situations où vous voulez garantir aux divers outils de ligne de commande Unix que vous leur passez des noms de fichiers via la ligne de commande, et rien de plus!

Alors pourquoi utiliser ./*?

Comme le souligne la réponse de @ Stephane , en raison de la nature des caractères autorisés lors de la dénomination de fichiers et de répertoires sous Unix, des noms de fichiers dangereux peuvent être construits qui ont des effets secondaires inattendus lorsqu'ils sont transmis à diverses commandes Unix sur la ligne de commande.

Si souvent, l'utilisation de ./sera utilisée pour garantir que les noms de fichiers étendus sont considérés comme des noms de fichiers lorsqu'ils sont passés comme arguments aux différentes commandes Unix.

slm
la source