GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi ne pas utiliser « qui » ? Quoi utiliser alors ?

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…).

En relation :Debian – Comment utiliser les pilotes sans fil propriétaires lors de l'installation de Debian USB ?

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'au execve l'appel système ne renvoie pas d'erreur. Par exemple si $PATH contient /foo:/bar et vous voulez exécuter ls , 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écutant ls 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 renvoie foo . Avec certains obus comme ash , pdksh ou zsh , il peut également renvoyer foo si $PATH inclut la chaîne vide et il y a un exécutable foo 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 busybox sh ), et par exemple bash 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 la command -v ne sera plus valide après avoir cd 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 …), mais command -v chmod renverra /opt/ast/bin/chmod même si ce chemin n'existe pas.
En relation:Dd vs chat - est-ce que dd est toujours pertinent de nos jours ?

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 ou echo=$commands[echo] ou echo=${${:-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.


Linux
  1. Pourquoi personne n'utilise le True Bourne Shell comme /bin/sh ?

  2. Pourquoi eval devrait-il être évité dans Bash et que dois-je utiliser à la place ?

  3. qu'est-ce que le démon dbus et pourquoi vlc en a besoin

  4. Pourquoi utilisons-nous su - et pas seulement su ?

  5. Quels caractères dois-je utiliser ou ne pas utiliser dans les noms d'utilisateur sous Linux ?

Qu'est-ce que le Shell sous Linux ?

Qu'est-ce qu'une machine virtuelle et pourquoi l'utiliser ?

Qu'est-ce que les conteneurs multi-comptes Firefox ? Pourquoi et comment l'utiliser ?

Qu'est-ce que le shell de connexion sous Linux ?

Qu'est-ce que la fonctionnalité de la communauté ONLYOFFICE et pourquoi devriez-vous l'utiliser ?

Qu'est-ce qu'un fichier .sh ?