Voici tout ce que vous n'auriez jamais pensé ne jamais vouloir savoir:
Sommaire
Pour obtenir le nom de fichier d’un exécutable dans un script shell de type Bourne (il existe quelques réserves; voir ci-dessous):
ls=$(command -v ls)
Pour savoir si une commande donnée existe:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
À l'invite d'un shell interactif de type Bourne:
type ls
La which
commande est un héritage brisé du C-Shell et il vaut mieux la laisser seule dans des coquilles à la Bourne.
Cas d'utilisation
Il y a une différence entre rechercher ces informations dans le cadre d'un script ou de manière interactive à l'invite du shell.
À l’invite du shell, le cas d’utilisation typique est le suivant: cette commande se comporte étrangement, est-ce que j’utilise la bonne? Qu'est-ce qui s'est passé exactement quand j'ai tapé mycmd
? Puis-je regarder plus loin ce que c'est?
Dans ce cas, vous voulez savoir ce que fait votre shell lorsque vous appelez la commande sans l'invoquer.
Dans les scripts shell, cela a tendance à être très différent. Dans un script shell, il n'y a aucune raison pour laquelle vous voulez savoir où et quelle commande est si tout ce que vous voulez faire est de l'exécuter. Généralement, ce que vous voulez savoir, c'est le chemin de l'exécutable. Vous pouvez ainsi en extraire davantage d'informations (comme le chemin d'accès à un autre fichier par rapport à celui-ci ou lire des informations à partir du contenu du fichier exécutable situé sur ce chemin).
Vous voudrez peut-être connaître, de manière interactive, toutes les my-cmd
commandes disponibles sur le système, rarement dans les scripts.
La plupart des outils disponibles (comme c'est souvent le cas) ont été conçus pour être utilisés de manière interactive.
Histoire
Un peu d'histoire d'abord.
Les premiers shell Unix jusqu’à la fin des années 70 n’avaient aucune fonction ni pseudonyme. Seule la recherche traditionnelle des exécutables dans $PATH
. csh
alias introduits autour de 1978 (mais csh
a d' abord été publié en 2BSD
en mai 1979), ainsi que le traitement d'un .cshrc
pour les utilisateurs de personnaliser la coque (chaque coquille, comme csh
lit .cshrc
même lorsqu'ils ne sont pas interactifs comme dans les scripts).
Bien que le shell Bourne ait été publié pour la première fois dans Unix V7 plus tôt en 1979, le support des fonctions n’a été ajouté que beaucoup plus tard (1984 en SVR2), et de toute façon, il n’a jamais eu de rc
fichier ( .profile
c’est pour configurer votre environnement, pas le shell en tant que tel ).
csh
est devenu beaucoup plus populaire que le shell Bourne car (même si sa syntaxe était bien pire que celle du shell Bourne), il ajoutait de nombreuses fonctionnalités plus pratiques et plus agréables pour une utilisation interactive.
En 3BSD
(1980), un which
script csh a été ajouté pour csh
aider les utilisateurs à identifier un exécutable. C’est un script à peine différent que vous pouvez trouver comme which
sur de nombreux Unices commerciaux de nos jours (comme Solaris, HP / UX, AIX ou Tru64).
Ce script lit l'utilisateur ~/.cshrc
(comme tous les csh
scripts, à moins d'être appelé avec csh -f
), et recherche le ou les noms de commande fournis dans la liste des alias et dans $path
(le tableau sur lequel on se csh
base $PATH
).
Voilà, which
c'est d'abord arrivé avec la coquille la plus populaire à l'époque (et elle l' csh
était toujours jusqu'au milieu des années 90), ce qui est la raison principale pour laquelle elle a été documentée dans des livres et est toujours largement utilisée.
Notez que, même pour un csh
utilisateur, ce which
script csh ne vous donne pas nécessairement les bonnes informations. Il obtient les alias définis dans ~/.cshrc
, pas ceux que vous avez définis ultérieurement à l'invite ou par exemple en source
créant un autre csh
fichier. (Bien que ce ne soit pas une bonne idée), ils PATH
pourraient être redéfinis dans ~/.cshrc
.
L'exécution de cette which
commande à partir d'un shell Bourne continuerait à rechercher les alias définis dans votre ~/.cshrc
, mais si vous n'en avez pas, car vous n'utiliseriez pas csh
, vous obtiendrez probablement toujours la bonne réponse.
Une fonctionnalité similaire n'a été ajoutée au shell Bourne qu'en 1984 dans SVR2 avec la type
commande intégrée. Le fait qu'il soit intégré (par opposition à un script externe) signifie qu'il peut vous donner les bonnes informations (dans une certaine mesure) car il a accès aux éléments internes du shell.
La type
commande initiale which
présentait un problème similaire à celui du script car elle ne renvoyait pas d'état de sortie en cas d'échec si la commande était introuvable. En outre, pour les exécutables, contrairement à which
cela, il génère quelque chose comme ls is /bin/ls
au lieu de tout /bin/ls
ce qui le rend moins facile à utiliser dans les scripts.
Le shell Bourne de la version 8 de Unix (non publié à l’état sauvage) a type
été renommé en whatis
. Et le shell Plan9 (l'ancien successeur d'Unix) rc
(et ses dérivés comme akanga
et es
) ont whatis
aussi.
Le shell Korn (un sous-ensemble sur lequel la définition de POSIX sh est basée), développé au milieu des années 80 mais pas encore largement disponible avant 1988, a ajouté de nombreuses csh
fonctionnalités (éditeur de ligne, alias ...) au-dessus du shell Bourne. . Il a ajouté sa propre fonction whence
intégrée (en plus de type
) qui a pris plusieurs options ( -v
pour fournir une type
sortie verbose semblable à celle-ci, et -p
ne rechercher que les exécutables (pas les alias / fonctions ...)).
Par coïncidence avec la tourmente liée aux problèmes de droits d'auteur entre AT & T et Berkeley, quelques implémentations de shell de logiciels libres sont apparues à la fin des années 80 et au début des années 90. L’ensemble du shell Almquist (cendres, remplaçant du shell Bourne dans les BSD), l’implémentation dans le domaine public de ksh (pdksh) bash
(parrainé par la FSF), zsh
est sorti entre 1989 et 1991.
Ash, bien que censé remplacer le shell Bourne, n’a été type
intégré que bien plus tard (dans NetBSD 1.3 et FreeBSD 2.3), bien qu’il en soit ainsi hash -v
. OSF / 1 /bin/sh
avait une fonction type
intégrée qui renvoyait toujours 0 à OSF / 1 v3.x. bash
n'a pas ajouté whence
mais ajouté une -p
option pour type
imprimer le chemin (ce type -p
serait comme whence -p
) et -a
pour signaler toutes les commandes correspondantes. tcsh
fait which
builtin et a ajouté une where
commande agissant comme bash
« s type -a
. zsh
a tous.
Le fish
shell (2005) a une type
commande implémentée en tant que fonction.
Le which
script csh a quant à lui été supprimé de NetBSD (car il était intégré à tcsh et peu utilisé dans d’autres shells), et la fonctionnalité ajoutée à whereis
(lorsqu’elle est appelée which
, whereis
se comporte comme which
si elle ne cherchait que les exécutables $PATH
). Dans OpenBSD et FreeBSD, which
a également été remplacé par un script écrit en C qui ne recherche que les commandes $PATH
.
Implémentations
Il existe des dizaines d'implémentations d'une which
commande sur divers Unices avec une syntaxe et un comportement différents.
Sous Linux (à côté de ceux intégrés dans tcsh
et zsh
), nous trouvons plusieurs implémentations. Sur les systèmes Debian récents, par exemple, il s’agit d’un simple script shell POSIX qui cherche des commandes dans $PATH
.
busybox
a également une which
commande.
Il y en a une GNU
which
qui est probablement la plus extravagante. Il essaie d’étendre ce que le which
script csh a fait à d’autres shells: vous pouvez lui indiquer quels sont vos alias et fonctions afin de vous donner une meilleure réponse (et je pense que certaines distributions Linux ont défini des alias mondiaux autour de cela pour bash
le faire) .
zsh
a quelques opérateurs à développer dans le chemin des exécutables: l' opérateur de =
développement de nom de fichier et le :c
modificateur de développement d'historique (appliqué ici à la dilatation de paramètre ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh
, dans le zsh/parameters
module fait également la table de hachage de commande comme le commands
tableau associatif:
$ print -r -- $commands[ls]
/bin/ls
L' whatis
utilitaire (à l'exception de celui de Unix V8 Bourne shell ou de Plan 9 rc
/ es
) n'est pas vraiment lié car il est destiné uniquement à la documentation (insère la base de données whatis, c'est-à-dire le synopsis de la page de manuel).
whereis
a également été ajouté en 3BSD
même temps que which
s'il était écrit C
, csh
mais qu'il est utilisé pour rechercher en même temps l'exécutable, la page de manuel et le source, mais non sur l'environnement actuel. Encore une fois, cela répond à un besoin différent.
Maintenant, sur le plan standard, POSIX spécifie les commandes command -v
et -V
(qui étaient facultatives jusqu’à POSIX.2008). UNIX spécifie la type
commande (pas d'option). C'est tout ( where
, which
, whence
ne sont pas spécifiées dans une norme)
Jusqu'à une certaine version, type
et command -v
étaient facultatifs dans la spécification Linux Standard Base, ce qui explique pourquoi, par exemple, certaines anciennes versions de posh
(bien qu'elles soient basées sur les pdksh
deux) n'en possédaient pas non plus. command -v
a également été ajouté à certaines implémentations du shell Bourne (comme sur Solaris).
Statut aujourd'hui
Le statut actuel est le même type
et il command -v
est omniprésent dans tous les shells de type Bourne (bien que, comme l'a noté @jarno, notez l'avertissement / bogue en dehors du bash
mode POSIX ou de certains descendants du shell Almquist ci-dessous dans les commentaires). tcsh
est le seul shell où vous voudriez utiliser which
(car il n’y en a pas type
et which
est intégré).
Dans les shells autres que tcsh
et zsh
, which
peut vous indiquer le chemin de l’exécutable donné tant qu’il n’ya ni alias ni fonction du même nom dans aucun de nos fichiers ~/.cshrc
, ~/.bashrc
ni aucun fichier de démarrage du shell, et que vous ne définissez pas $PATH
dans votre ~/.cshrc
. Si vous avez défini un alias ou une fonction, il peut vous en parler ou ne pas vous indiquer la mauvaise chose.
Si vous voulez connaître toutes les commandes portant un nom donné, rien n’est portable. Vous utiliseriez where
dans tcsh
ou zsh
, type -a
dans bash
ou zsh
, whence -a
en ksh93 et dans d' autres coquilles, vous pouvez utiliser type
en combinaison avec ce which -a
qui peut fonctionner.
Recommandations
Obtenir le chemin d'accès à un exécutable
Maintenant, pour obtenir le chemin d’un exécutable dans un script, quelques mises en garde:
ls=$(command -v ls)
serait le moyen standard de le faire.
Il y a cependant quelques problèmes:
- Il n'est pas possible de connaître le chemin de l'exécutable sans l'exécuter. Tous les
type
, which
, command -v
... tous utilisent des technologies heuristiques pour trouver le chemin. Ils parcourent les $PATH
composants en boucle et recherchent le premier fichier non répertoire pour lequel vous disposez d'une autorisation d'exécution. Cependant, selon le shell, quand il s'agit d'exécuter la commande, beaucoup d'entre eux (Bourne, AT & T ksh, zsh, ash ...) les exécuteront simplement dans l'ordre de $PATH
jusqu'à ce que l' execve
appel système ne revienne pas avec une erreur. . Par exemple, si $PATH
contient /foo:/bar
et que vous voulez exécuter ls
, ils vont d'abord essayer de s'exécuter /foo/ls
ou si cela échoue /bar/ls
. Maintenant l'exécution de/foo/ls
peut échouer car vous n’avez pas le droit d’exécution, mais aussi pour de nombreuses autres raisons, comme l’exécutable n’est pas valide. command -v ls
signalerait /foo/ls
si vous avez une autorisation d'exécution pour /foo/ls
, mais l'exécution ls
peut en réalité s'exécuter /bar/ls
si ce /foo/ls
n'est pas un exécutable valide.
- Si
foo
est une fonction ou un alias intégré, command -v foo
retourne foo
. Avec certains shells tels que ash
, pdksh
ou zsh
, il peut également renvoyer foo
si $PATH
inclut la chaîne vide et s'il existe un foo
fichier exécutable dans le répertoire en cours. Dans certaines circonstances, vous devrez peut-être en tenir compte. Gardez à l’esprit, par exemple, que la liste des commandes intégrées varie en fonction de l’implémentation du shell (par exemple, elle mount
est parfois intégrée à busybox sh
) et bash
peut , par exemple, obtenir des fonctions de l’environnement.
- Si
$PATH
contient des composants de chemin relatif (généralement .
ou la chaîne vide qui font tous les deux référence au répertoire en cours mais peut être n'importe quoi), selon le shell, command -v cmd
peut ne pas générer un chemin absolu. Ainsi, le chemin que vous obtenez au moment où vous courez command -v
ne sera plus valable après votre passage cd
ailleurs.
- Anecdotique: avec le shell ksh93, si
/opt/ast/bin
(bien que cette voie exacte peut varier en fonction des différents systèmes , je crois) est en vous $PATH
, ksh93 mettra à la disposition quelques builtins supplémentaires ( chmod
, cmp
, cat
...), mais command -v chmod
retournera /opt/ast/bin/chmod
même si ce chemin n » t existent.
Déterminer si une commande existe
Pour savoir si une commande donnée existe de manière standard, vous pouvez faire:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Où on pourrait vouloir utiliser which
(t)csh
En csh
et tcsh
, tu n'as pas beaucoup de choix. En tcsh
, ça va comme which
c'est construit. Dans csh
, ce sera la which
commande système , qui peut ne pas faire ce que vous voulez dans quelques cas.
trouver des commandes uniquement dans certains coquillages
Un cas où il peut être judicieux d'utiliser which
est si vous voulez connaître le chemin d'une commande, en ignorant shell potentiels builtins ou fonctions bash
, csh
(non tcsh
), dash
ou des Bourne
scripts shell, c'est - coquilles qui ne sont pas whence -p
(comme ksh
ou zsh
) , command -ev
(comme yash
), whatis -p
( rc
, akanga
) ou un élément intégré which
(comme tcsh
ou zsh
) sur des systèmes où which
est disponible et n'est pas le csh
script.
Si ces conditions sont remplies, alors:
echo=$(which echo)
vous donnera le chemin du premier echo
dans $PATH
(sauf dans les cas d'angle), que echo
se trouve être également une commande du shell / alias / fonction ou non.
Dans d'autres coquilles, vous préféreriez:
- zsh :
echo==echo
ou echo=$commands[echo]
ouecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`
(méfiez-vous des chemins avec des espaces)
- poissons :
set echo (type -fp echo)
Notez que si tout ce que vous voulez faire est d’ exécuter cette echo
commande, vous n’avez pas à obtenir son chemin, vous pouvez simplement faire:
env echo this is not echoed by the builtin echo
Par exemple, avec tcsh
, pour empêcher l’utilisation which
de la commande intégrée :
set Echo = "`env which echo`"
quand vous avez besoin d'une commande externe
Vous pouvez également utiliser un autre cas lorsque vous which
avez réellement besoin d' une commande externe. POSIX exige que toutes les fonctions intégrées dans le shell (comme command
) soient également disponibles en tant que commandes externes, mais malheureusement, ce n'est pas le cas command
sur de nombreux systèmes. Par exemple, il est rare de trouver une command
commande sur des systèmes d'exploitation basés sur Linux alors que la plupart d'entre eux ont une which
commande (bien que différentes avec des options et des comportements différents).
Les cas dans lesquels vous souhaiterez peut-être une commande externe seraient ceux où vous exécuteriez une commande sans appeler un shell POSIX.
Les fonctions system("some command line")
, popen()
... de C ou de divers langages invoquent un shell pour analyser cette ligne de commande; system("command -v my-cmd")
travaillez-y donc. Une exception à cela serait d' perl
optimiser le shell s'il ne voit aucun caractère spécial du shell (autre que l'espace). Cela s'applique également à son opérateur de backtick:
$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0
$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs
L'ajout de ce qui :;
précède force perl
à y invoquer un shell. En utilisant which
, vous n'auriez pas à utiliser cette astuce.
which
supposent un contexte de shell interactif. Cette question est étiquetée / portabilité. J'interprète donc la question dans ce contexte comme "quoi utiliser au lieu dewhich
trouver le premier exécutable d'un nom donné dans le$PATH
". La plupart des réponses et des raisons s'opposentwhich
au traitement des alias, des fonctions intégrées et des fonctions, qui dans la plupart des scripts shell portables du monde réel n'ont qu'un intérêt théorique. Les alias définis localement ne sont pas hérités lors de l'exécution d'un script shell (sauf si vous le sourcez.
).