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 whichcommande 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-cmdcommandes 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. cshalias introduits autour de 1978 (mais csha d' abord été publié en 2BSDen mai 1979), ainsi que le traitement d'un .cshrcpour les utilisateurs de personnaliser la coque (chaque coquille, comme cshlit .cshrcmê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 rcfichier ( .profilec’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 whichscript csh a été ajouté pour cshaider les utilisateurs à identifier un exécutable. C’est un script à peine différent que vous pouvez trouver comme whichsur de nombreux Unices commerciaux de nos jours (comme Solaris, HP / UX, AIX ou Tru64).
Ce script lit l'utilisateur ~/.cshrc(comme tous les cshscripts, à 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 cshbase $PATH).
Voilà, whichc'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 cshutilisateur, ce whichscript 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 sourcecréant un autre cshfichier. (Bien que ce ne soit pas une bonne idée), ils PATHpourraient être redéfinis dans ~/.cshrc.
L'exécution de cette whichcommande à 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 typecommande 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 typecommande initiale whichpré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 à whichcela, il génère quelque chose comme ls is /bin/lsau lieu de tout /bin/lsce 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 akangaet es) ont whatisaussi.
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 cshfonctionnalités (éditeur de ligne, alias ...) au-dessus du shell Bourne. . Il a ajouté sa propre fonction whenceintégrée (en plus de type) qui a pris plusieurs options ( -vpour fournir une typesortie verbose semblable à celle-ci, et -pne 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), zshest sorti entre 1989 et 1991.
Ash, bien que censé remplacer le shell Bourne, n’a été typeintégré que bien plus tard (dans NetBSD 1.3 et FreeBSD 2.3), bien qu’il en soit ainsi hash -v. OSF / 1 /bin/shavait une fonction typeintégrée qui renvoyait toujours 0 à OSF / 1 v3.x. bashn'a pas ajouté whencemais ajouté une -poption pour typeimprimer le chemin (ce type -pserait comme whence -p) et -apour signaler toutes les commandes correspondantes. tcshfait whichbuiltin et a ajouté une wherecommande agissant comme bash« s type -a. zsha tous.
Le fishshell (2005) a une typecommande implémentée en tant que fonction.
Le whichscript 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, whereisse comporte comme whichsi elle ne cherchait que les exécutables $PATH). Dans OpenBSD et FreeBSD, whicha é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 whichcommande sur divers Unices avec une syntaxe et un comportement différents.
Sous Linux (à côté de ceux intégrés dans tcshet 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.
busyboxa également une whichcommande.
Il y en a une GNU whichqui est probablement la plus extravagante. Il essaie d’étendre ce que le whichscript 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 bashle faire) .
zsha quelques opérateurs à développer dans le chemin des exécutables: l' opérateur de = développement de nom de fichier et le :cmodificateur 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/parametersmodule fait également la table de hachage de commande comme le commandstableau associatif:
$ print -r -- $commands[ls]
/bin/ls
L' whatisutilitaire (à 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).
whereisa également été ajouté en 3BSDmême temps que whichs'il était écrit C, cshmais 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 -vet -V(qui étaient facultatives jusqu’à POSIX.2008). UNIX spécifie la typecommande (pas d'option). C'est tout ( where, which, whencene sont pas spécifiées dans une norme)
Jusqu'à une certaine version, typeet 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 pdkshdeux) n'en possédaient pas non plus. command -va également été ajouté à certaines implémentations du shell Bourne (comme sur Solaris).
Statut aujourd'hui
Le statut actuel est le même typeet il command -vest omniprésent dans tous les shells de type Bourne (bien que, comme l'a noté @jarno, notez l'avertissement / bogue en dehors du bashmode POSIX ou de certains descendants du shell Almquist ci-dessous dans les commentaires). tcshest le seul shell où vous voudriez utiliser which(car il n’y en a pas typeet whichest intégré).
Dans les shells autres que tcshet zsh, whichpeut 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, ~/.bashrcni aucun fichier de démarrage du shell, et que vous ne définissez pas $PATHdans 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 wheredans tcshou zsh, type -adans bashou zsh, whence -aen ksh93 et dans d' autres coquilles, vous pouvez utiliser typeen combinaison avec ce which -aqui 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 $PATHcomposants 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 $PATHjusqu'à ce que l' execveappel système ne revienne pas avec une erreur. . Par exemple, si $PATHcontient /foo:/baret que vous voulez exécuter ls, ils vont d'abord essayer de s'exécuter /foo/lsou si cela échoue /bar/ls. Maintenant l'exécution de/foo/lspeut é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 lssignalerait /foo/lssi vous avez une autorisation d'exécution pour /foo/ls, mais l'exécution lspeut en réalité s'exécuter /bar/lssi ce /foo/lsn'est pas un exécutable valide.
- Si
fooest une fonction ou un alias intégré, command -v fooretourne foo. Avec certains shells tels que ash, pdkshou zsh, il peut également renvoyer foosi $PATHinclut la chaîne vide et s'il existe un foofichier 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 mountest parfois intégrée à busybox sh) et bashpeut , par exemple, obtenir des fonctions de l’environnement.
- Si
$PATHcontient 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 cmdpeut ne pas générer un chemin absolu. Ainsi, le chemin que vous obtenez au moment où vous courez command -vne sera plus valable après votre passage cdailleurs.
- 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 chmodretournera /opt/ast/bin/chmodmê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 cshet tcsh, tu n'as pas beaucoup de choix. En tcsh, ça va comme whichc'est construit. Dans csh, ce sera la whichcommande 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 whichest si vous voulez connaître le chemin d'une commande, en ignorant shell potentiels builtins ou fonctions bash, csh(non tcsh), dashou des Bournescripts shell, c'est - coquilles qui ne sont pas whence -p(comme kshou zsh) , command -ev(comme yash), whatis -p( rc, akanga) ou un élément intégré which(comme tcshou zsh) sur des systèmes où whichest disponible et n'est pas le cshscript.
Si ces conditions sont remplies, alors:
echo=$(which echo)
vous donnera le chemin du premier echodans $PATH(sauf dans les cas d'angle), que echose trouve être également une commande du shell / alias / fonction ou non.
Dans d'autres coquilles, vous préféreriez:
- zsh :
echo==echoou 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 echocommande, 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 whichde 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 whichavez 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 commandsur de nombreux systèmes. Par exemple, il est rare de trouver une commandcommande sur des systèmes d'exploitation basés sur Linux alors que la plupart d'entre eux ont une whichcommande (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' perloptimiser 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.
whichsupposent 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 dewhichtrouver le premier exécutable d'un nom donné dans le$PATH". La plupart des réponses et des raisons s'opposentwhichau 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.).