Réponses:
$ touch ./-c $ 'a \ n12 \ tb' foo $ du -hs * 0 a 12 b 0 foo 0 au total
Comme vous pouvez le voir, le -c
fichier a été pris en option du
et n'est pas signalé (et vous voyez la total
ligne à cause de du -c
). De plus, le fichier appelé a\n12\tb
nous fait penser qu'il existe des fichiers appelés a
et b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
C'est mieux. Au moins, ce temps -c
n'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 -c
d'être pris comme option et l'absence d' ./
avant b
dans la sortie indique qu'il n'y a pas de b
fichier 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 cmd
ne 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.txt
dans le répertoire courant (définit la variable awk a
au b.txt
lieu de lui dire de traiter le fichier).
awk -f file.awk ./*
N'a pas le problème car ./a
n'est pas un nom de variable awk valide, donc ./a=b.txt
n'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 cat
de 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 foo
sont faux car il suppose que les noms de fichiers ne contiennent pas de caractères de nouvelle ligne ( wc -l
compte les caractères de nouvelle ligne, ceux produits par grep
pour 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
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:
\r
déplace le curseur au début de la ligne, \b
déplace le curseur vers l'arrière, \e[C
vers l'avant (dans la plupart des terminaux) ...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 x
mais y
est manquant.
Certains outils comme GNU
ls 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 du
ne 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 ls
que 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 sed
implémentations conformes POSIX mais notez que certaines anciennes sed
implé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 -A
avec 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 y
jamais 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 du
signale l'utilisation du disque, il ne signale pas d'autres liens vers un fichier déjà répertorié (toutes les du
implémentations ne se comportent pas comme cela cependant lorsque les liens matériels sont répertoriés sur la ligne de commande).
[a-z0-9.+-]
.
/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 ...)
"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 root
bien 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.
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 echo
pour voir à quoi le shell les étendrait.
$ echo *
$ echo ./*
Ces 2 commandes listeront tous les fichiers de votre répertoire actuel.
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 echo
commandes 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!
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.