Lorsque vous recherchez le chemin d'accès à un exécutable ou que vous vérifiez ce qui se passerait si vous entrez un nom de commande dans un shell Unix, il existe une pléthore d'utilitaires différents (which
, type
, command
, whence
, where
, whereis
, whatis
, hash
, etc.).
On entend souvent ce which
devrait être évité. Pourquoi? Que devrions-nous utiliser à la place ?
Réponse acceptée :
Voici tout ce que vous n'auriez jamais pensé que vous ne voudriez jamais savoir à ce sujet :
Résumé
Pour obtenir le chemin d'accès d'un exécutable dans un script shell de type Bourne (il y a quelques mises en garde ; 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
Le which
command est un héritage brisé du C-Shell et il vaut mieux le laisser seul dans les shells de type Bourne.
Cas d'utilisation
Il existe une distinction entre la recherche de 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 :cette commande se comporte bizarrement, est-ce que j'utilise la bonne ? Que s'est-il exactement passé lorsque j'ai tapé mycmd
? Puis-je regarder plus loin de quoi il s'agit ?
Dans ce cas, vous voulez savoir ce que fait votre shell lorsque vous appelez la commande sans réellement appeler la commande.
Dans les scripts shell, cela a tendance à être assez différent. Dans un script shell, il n'y a aucune raison pour que vous vouliez savoir où ou quelle est une commande 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, afin que vous puissiez en tirer plus 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 à ce chemin).
De manière interactive, vous voudrez peut-être en savoir plus sur tous le my-cmd
commandes disponibles sur le système, dans des scripts, rarement.
La plupart des outils disponibles (comme c'est souvent le cas) ont été conçus pour être utilisés de manière interactive.
Historique
Un peu d'histoire d'abord.
Les premiers shells Unix jusqu'à la fin des années 70 n'avaient ni fonctions ni alias. Uniquement la recherche traditionnelle des exécutables dans $PATH
. csh
introduit des alias vers 1978 (bien que csh
a été sorti pour la première fois dans 2BSD
, en mai 1979), ainsi que le traitement d'un .cshrc
pour les utilisateurs de personnaliser le shell (chaque shell, comme csh
, lit .cshrc
même lorsqu'il n'est pas interactif comme dans les scripts).
Alors que le shell Bourne a été publié pour la première fois dans Unix V7 plus tôt en 1979, la prise en charge des fonctions n'a été ajoutée que beaucoup plus tard (1984 dans SVR2), et de toute façon, il n'a jamais eu de rc
fichier (le .profile
est de configurer votre environnement, pas le shell en soi ).
csh
est devenu beaucoup plus populaire que le shell Bourne car (bien qu'il ait une syntaxe terriblement pire que le shell Bourne), il ajoutait beaucoup de fonctionnalités plus pratiques et agréables pour une utilisation interactive.
Dans 3BSD
(1980), un which
le script csh a été ajouté pour le csh
utilisateurs pour aider à identifier un exécutable, et c'est un script à peine différent que vous pouvez trouver comme which
sur de nombreux Unix commerciaux de nos jours (comme Solaris, HP/UX, AIX ou Tru64).
Ce script lit le ~/.cshrc
de l'utilisateur (comme tous les csh
les scripts le font à moins qu'ils ne soient invoqués avec csh -f
) et recherche le ou les noms de commande fournis dans la liste des alias et dans $path
(le tableau que csh
maintient basé sur $PATH
).
Et voilà :which
est arrivé premier pour le shell le plus populaire à l'époque (et csh
était encore populaire jusqu'au milieu des années 90), ce qui est la principale raison pour laquelle il a été documenté dans des livres et est encore largement utilisé.
Notez que, même pour un csh
utilisateur, ce which
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 plus tard à l'invite ou par exemple par source
un autre csh
file, et (bien que ce ne soit pas une bonne idée), PATH
peut être redéfini dans ~/.cshrc
.
Exécuter ce which
la commande d'un shell Bourne rechercherait toujours les alias définis dans votre ~/.cshrc
, mais si vous n'en avez pas parce que vous n'utilisez pas csh
, cela vous permettrait probablement d'obtenir la bonne réponse.
Une fonctionnalité similaire n'a été ajoutée au shell Bourne qu'en 1984 dans SVR2 avec le type
commande intégrée. Le fait qu'il soit intégré (par opposition à un script externe) signifie qu'il peut vous donne les bonnes informations (dans une certaine mesure) car il a accès aux composants internes du shell.
Le type
initial La commande souffrait d'un problème similaire à celui de which
script en ce sens qu'il ne renvoyait pas d'état de sortie d'échec si la commande n'était pas trouvée. Aussi, pour les exécutables, contrairement à which
, il affiche quelque chose comme ls is /bin/ls
au lieu de simplement /bin/ls
ce qui le rendait moins facile à utiliser dans les scripts.
Le shell Bourne d'Unix version 8 (non publié dans la nature) avait son type
intégré renommé en whatis
et étendu pour signaler également les paramètres et les définitions des fonctions d'impression. Il a également corrigé type
problème de ne pas renvoyer l'échec en cas d'échec de la recherche d'un nom.
rc
, le shell de Plan9 (l'ancien successeur d'Unix) (et ses dérivés comme akanga
et es
) ont whatis
aussi.
Le shell Korn (un sous-ensemble dont le POSIX sh
la définition est basée sur), développé au milieu des années 80 mais pas largement disponible avant 1988, a ajouté de nombreux csh
fonctionnalités (éditeur de ligne, alias…) au-dessus du shell Bourne. Il a ajouté son propre where
intégré (en plus de type
) qui prenait plusieurs options (-v
à fournir avec le type
-like sortie verbeuse, et -p
pour rechercher uniquement les exécutables (pas les alias/fonctions…).
Coïncidence avec l'agitation concernant les problèmes de copyright entre AT&T et Berkeley, quelques logiciels libres Les implémentations de shell sont sorties à la fin des années 80 et au début des années 90. Tout le shell Almquist (ash
, pour remplacer le shell Bourne dans les BSD), l'implémentation du domaine public de ksh
(pdksh
), bash
(sponsorisé par la FSF), zsh
est sorti entre 1989 et 1991.
Ash, bien que censé remplacer le shell Bourne, n'avait pas de type
intégré jusqu'à bien plus tard (dans NetBSD 1.3 et FreeBSD 2.3), bien qu'il ait hash -v
. OSF/1 /bin/sh
avait un type
intégré qui retournait toujours 0 jusqu'à OSF/1 v3.x. bash
n'a pas ajouté de whence
mais ajouté un -p
option pour type
pour imprimer le chemin (type -p
serait comme whence -p
) et -a
signaler tous les commandes correspondantes. tcsh
fait which
intégré et ajouté un where
commande agissant comme bash
's type -a
. zsh
les a tous.
Le fish
shell (2005) a un type
commande implémentée en tant que fonction.
Le which
le script csh a quant à lui été supprimé de NetBSD (car il était intégré dans tcsh et peu utile dans d'autres shells), et la fonctionnalité a été ajoutée à whereis
(lorsqu'il est invoqué en tant que which
, whereis
se comporte comme which
sauf qu'il ne recherche que les exécutables dans $PATH
). Dans OpenBSD et FreeBSD, which
a également été remplacé par un écrit en C qui recherche les commandes dans $PATH
uniquement.
Implémentations
Il existe des dizaines d'implémentations d'un which
commande sur différents 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, c'est un simple script shell POSIX qui recherche les commandes dans $PATH
.
busybox
a aussi un which
commande.
Il existe un GNU
which
qui est probablement le plus extravagant. Il essaie d'étendre ce que le which
csh a fait à d'autres shells :vous pouvez lui dire quels sont vos alias et fonctions afin qu'il puisse vous donner une meilleure réponse (et je crois que certaines distributions Linux définissent des alias globaux autour de cela pour bash
pour le faire).
zsh
a quelques opérateurs pour développer le chemin des exécutables :le =
expansion du nom de fichier opérateur et le :c
modificateur d'expansion de l'historique (ici appliqué à l'expansion des paramètres ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh
, dans le zsh/parameters
le module crée également la table de hachage des commandes en tant que commands
tableau associatif :
$ print -r -- $commands[ls]
/bin/ls
Le whatis
utilitaire (sauf celui dans Unix V8 Bourne shell ou Plan 9 rc
/es
) n'est pas vraiment lié car c'est uniquement pour la documentation (greps la base de données whatis, c'est-à-dire le synopsis de la page de manuel).
whereis
a également été ajouté dans 3BSD
en même temps que which
bien qu'il ait été écrit en C
, pas csh
et est utilisé pour rechercher à la fois l'exécutable, la page de manuel et la source, mais pas en fonction de l'environnement actuel. Encore une fois, cela répond à un besoin différent.
Maintenant, sur le front standard, POSIX spécifie la command -v
et -V
commandes (qui étaient facultatives jusqu'à POSIX.2008). UNIX spécifie le type
commande (pas d'option). C'est tout (where
, which
, whence
ne sont spécifiés dans aucune 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 que basé sur pdksh
qui avait les deux) n'avait ni l'un ni l'autre. command -v
a également été ajouté à certaines implémentations du shell Bourne (comme sur Solaris).
Statut aujourd'hui
Le statut de nos jours est que type
et command -v
sont omniprésents dans tous les shells de type Bourne (bien que, comme l'a noté @jarno, notez la mise en garde/bogue dans bash
lorsqu'il n'est pas en mode POSIX ou certains descendants du shell Almquist ci-dessous dans les commentaires). tcsh
est le seul shell où vous voudriez utiliser which
(car il n'y a pas de type
là 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'y a pas d'alias ou de fonction portant le même nom dans aucun de nos ~/.cshrc
, ~/.bashrc
ou tout fichier de démarrage du shell et vous ne définissez pas $PATH
dans votre ~/.cshrc
. Si vous avez défini un alias ou une fonction pour cela, il peut ou non vous en parler, ou vous dire la mauvaise chose.
Si vous voulez connaître toutes les commandes par un nom donné, il n'y a rien de portable. Vous utiliseriez where
dans tcsh
ou zsh
, type -a
dans bash
ou zsh
, whence -a
dans ksh93 et dans d'autres shells, vous pouvez utiliser type
en combinaison avec which -a
qui peut fonctionner.
Recommandations
Obtenir le chemin d'accès à un exécutable
Maintenant, pour obtenir le chemin d'accès d'un exécutable dans un script, il y a quelques mises en garde :
ls=$(command -v ls)
serait la manière 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 heuristiques pour trouver le chemin. Ils parcourent le$PATH
composants et recherchez le premier fichier non-répertoire pour lequel vous disposez d'une autorisation d'exécution. Cependant, selon le shell, lorsqu'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$PATH
jusqu'auexecve
l'appel système ne renvoie pas d'erreur. Par exemple si$PATH
contient/foo:/bar
et vous voulez exécuterls
, ils essaieront d'abord d'exécuter/foo/ls
ou si cela échoue/bar/ls
. Maintenant exécution de/foo/ls
peut échouer parce que vous n'avez pas l'autorisation d'exécution, mais aussi pour de nombreuses autres raisons, comme ce n'est pas un exécutable valide.command -v ls
rapporterait/foo/ls
si vous avez l'autorisation d'exécution pour/foo/ls
, mais en exécutantls
pourrait en fait exécuter/bar/ls
si/foo/ls
n'est pas un exécutable valide. - si
foo
est une fonction ou un alias intégré,command -v foo
renvoiefoo
. Avec certains obus commeash
,pdksh
ouzsh
, il peut également renvoyerfoo
si$PATH
inclut la chaîne vide et il y a un exécutablefoo
fichier dans le répertoire courant. Dans certaines circonstances, vous devrez peut-être en tenir compte. Gardez à l'esprit par exemple que la liste des commandes intégrées varie avec l'implémentation du shell (par exemple,mount
est parfois intégré pour busyboxsh
), et par exemplebash
peut obtenir des fonctions de l'environnement. - si
$PATH
contient des composants de chemin relatif (généralement.
ou la chaîne vide qui font toutes deux référence au répertoire courant mais peut être n'importe quoi), selon le shell,command -v cmd
peut ne pas générer de chemin absolu. Ainsi, le chemin que vous obtenez au moment où vous exécutez lacommand -v
ne sera plus valide après avoircd
ailleurs. - Anecdotique :avec le shell ksh93, si
/opt/ast/bin
(bien que ce chemin exact puisse varier selon les différents systèmes, je crois) est en vous$PATH
, ksh93 mettra à disposition quelques commandes intégrées supplémentaires (chmod
,cmp
,cat
…), maiscommand -v chmod
renverra/opt/ast/bin/chmod
même si ce chemin n'existe pas.
Déterminer si une commande existe
Pour savoir si une commande donnée existe en 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
, vous n'avez pas trop le choix. Dans tcsh
, c'est bien comme which
est intégré. En csh
, ce sera le système which
commande, qui peut ne pas faire ce que vous voulez dans certains cas.
Trouver des commandes uniquement dans certains shells
Un cas où il peut être judicieux d'utiliser which
est si vous voulez connaître le chemin d'une commande, en ignorant les commandes intégrées ou les fonctions potentielles dans bash
, csh
(pas tcsh
), dash
, ou Bourne
les scripts shell, c'est-à-dire les shells qui n'ont pas whence -p
(comme ksh
ou zsh
), command -ev
(comme yash
), whatis -p
(rc
, akanga
) ou un which
intégré (comme tcsh
ou zsh
) sur les systèmes où which
est disponible et n'est pas le csh
script.
Si ces conditions sont remplies, alors :
echo=$(which echo)
vous donnerait le chemin du premier echo
dans $PATH
(sauf dans les cas extrêmes), que echo
se trouve également être un shell intégré/alias/fonction ou non.
Dans d'autres shells, vous préféreriez :
- zsh :
echo==echo
ouecho=$commands[echo]
ouecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`
(attention aux chemins avec des espaces) - poisson :
set echo (type -fp echo)
Notez que si tout ce que vous voulez faire est de exécuter qui echo
commande, vous n'avez pas besoin d'obtenir son chemin, vous pouvez simplement faire :
env echo this is not echoed by the builtin echo
Par exemple, avec tcsh
, pour empêcher le which
intégré d'être utilisé :
set Echo = "`env which echo`"
Lorsque vous avez besoin d'une commande externe
Un autre cas où vous voudrez peut-être utiliser which
c'est quand vous avez réellement besoin une commande externe. POSIX exige que toutes les commandes intégrées du shell (comme command
) soient également disponibles en tant que commandes externes, mais malheureusement, ce n'est pas le cas pour command
sur de nombreux systèmes. Par exemple, il est rare de trouver une command
commande sur les systèmes d'exploitation basés sur Linux alors que la plupart d'entre eux ont un which
commande (bien que différentes avec des options et des comportements différents).
Les cas où vous voudrez peut-être une commande externe seraient partout où vous exécuteriez une commande sans appeler un shell POSIX.
Le system("some command line")
, popen()
… les fonctions de C ou de divers langages invoquent un shell pour analyser cette ligne de commande, donc system("command -v my-cmd")
travailler en eux. Une exception à cela serait perl
qui optimise le shell s'il ne voit aucun caractère spécial du shell (autre que l'espace). Cela s'applique également à son opérateur 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 :;
ci-dessus force perl
pour y invoquer un shell. En utilisant which
, vous n'auriez pas à utiliser cette astuce.