Al buscar la ruta a un ejecutable o verificar qué sucedería si ingresa un nombre de comando en un shell de Unix, hay una gran cantidad de utilidades diferentes (which
, type
, command
, whence
, where
, whereis
, whatis
, hash
, etc.).
A menudo escuchamos que which
debería ser evitado. ¿Por qué? ¿Qué deberíamos usar en su lugar?
Respuesta aceptada:
Aquí está todo lo que nunca pensaste que nunca querrías saber al respecto:
Resumen
Para obtener el nombre de ruta de un ejecutable en un script de shell similar a Bourne (hay algunas advertencias; consulte a continuación):
ls=$(command -v ls)
Para saber si existe un comando dado:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
En el indicador de un shell interactivo similar a Bourne:
type ls
El which
command es una herencia rota del C-Shell y es mejor dejarlo solo en shells tipo Bourne.
Casos de uso
Hay una distinción entre buscar esa información como parte de un script o de forma interactiva en el indicador de shell.
En el indicador de shell, el caso de uso típico es:este comando se comporta de manera extraña, ¿estoy usando el correcto? ¿Qué sucedió exactamente cuando escribí mycmd
? ? ¿Puedo ver más de qué se trata?
En ese caso, desea saber qué hace su shell cuando invoca el comando sin invocar realmente el comando.
En los scripts de shell, tiende a ser bastante diferente. En un script de shell no hay ninguna razón por la que quiera saber dónde o qué comando es si todo lo que quiere hacer es ejecutarlo. En general, lo que desea saber es la ruta del ejecutable, para que pueda obtener más información (como la ruta a otro archivo relacionado con eso, o leer información del contenido del archivo ejecutable en esa ruta).
De forma interactiva, es posible que desee conocer todas el my-cmd
comandos disponibles en el sistema, en scripts, rara vez.
La mayoría de las herramientas disponibles (como suele ser el caso) han sido diseñadas para ser utilizadas de forma interactiva.
Historia
Un poco de historia primero.
Los primeros shells de Unix hasta finales de los 70 no tenían funciones ni alias. Solo la búsqueda tradicional de ejecutables en $PATH
. csh
introdujo alias alrededor de 1978 (aunque csh
fue lanzado por primera vez en 2BSD
, en mayo de 1979), y también el procesamiento de un .cshrc
para que los usuarios personalicen el shell (cada shell, como csh
, lee .cshrc
incluso cuando no es interactivo como en los scripts).
Si bien Bourne Shell se lanzó por primera vez en Unix V7 a principios de 1979, el soporte de funciones solo se agregó mucho más tarde (1984 en SVR2) y, de todos modos, nunca tuvo algo de rc
. archivo (el .profile
es configurar su entorno, no el shell per se ).
csh
se volvió mucho más popular que el shell Bourne ya que (aunque tenía una sintaxis terriblemente peor que el shell Bourne) estaba agregando muchas características más convenientes y agradables para el uso interactivo.
En 3BSD
(1980), un which
Se agregó el script csh para csh
usuarios para ayudar a identificar un ejecutable, y es un script apenas diferente que puede encontrar como which
en muchos Unices comerciales hoy en día (como Solaris, HP/UX, AIX o Tru64).
Ese script lee el ~/.cshrc
del usuario (como todos los csh
los scripts funcionan a menos que se invoquen con csh -f
) y busca los nombres de comando proporcionados en la lista de alias y en $path
(la matriz que csh
mantiene basado en $PATH
).
Aquí tienes:which
llegó primero para el shell más popular en ese momento (y csh
siguió siendo popular hasta mediados de los 90), que es la razón principal por la que se documentó en libros y todavía se usa ampliamente.
Tenga en cuenta que, incluso para un csh
usuario, ese which
csh script no necesariamente le brinda la información correcta. Obtiene los alias definidos en ~/.cshrc
, no los que haya definido más tarde en el indicador o, por ejemplo, mediante source
ing otro csh
archivo, y (aunque eso no sería una buena idea), PATH
podría redefinirse en ~/.cshrc
.
Ejecutando ese which
el comando de un shell Bourne aún buscaría los alias definidos en su ~/.cshrc
, pero si no tienes uno porque no usas csh
, eso probablemente le daría la respuesta correcta.
No se agregó una funcionalidad similar al shell Bourne hasta 1984 en SVR2 con el type
comando incorporado. El hecho de que esté integrado (a diferencia de un script externo) significa que puede brindarle la información correcta (hasta cierto punto) ya que tiene acceso a las partes internas del caparazón.
El type
inicial comando sufrió un problema similar al which
secuencia de comandos en el sentido de que no devolvía un estado de salida fallido si no se encontraba el comando. Además, para ejecutables, al contrario de which
, genera algo como ls is /bin/ls
en lugar de solo /bin/ls
lo que hizo que fuera menos fácil de usar en scripts.
El shell Bourne de Unix versión 8 (no publicado en la naturaleza) tenía su type
incorporado renombrado a whatis
y ampliado para informar también sobre parámetros y definiciones de funciones de impresión. También corrigió type
Problema de no devolver el error cuando no se puede encontrar un nombre.
rc
, el caparazón de Plan9 (el futuro sucesor de Unix) (y sus derivados como akanga
y es
) tiene whatis
también.
El shell Korn (un subconjunto del cual el POSIX sh
se basa la definición), desarrollado a mediados de los 80 pero no disponible antes de 1988, agregó muchos de los csh
características (editor de línea, alias...) en la parte superior de la shell Bourne. Agregó su propio whence
incorporado (además de type
) que tomó varias opciones (-v
proporcionar con el type
-como salida detallada, y -p
para buscar solo ejecutables (no alias/funciones...)).
Coincidentemente con la agitación con respecto a los problemas de derechos de autor entre AT&T y Berkeley, algunos software libre Las implementaciones de shell surgieron a finales de los 80 y principios de los 90. Todo el caparazón de Almquist (ash
, para ser el reemplazo del shell Bourne en BSD), la implementación de dominio público de ksh
(pdksh
), bash
(patrocinado por la FSF), zsh
salió entre 1989 y 1991.
Ash, aunque estaba destinado a ser un reemplazo del shell Bourne, no tenía un type
integrado hasta mucho más tarde (en NetBSD 1.3 y FreeBSD 2.3), aunque tenía hash -v
. OSF/1 /bin/sh
tenía un type
incorporado que siempre devolvía 0 hasta OSF/1 v3.x. bash
no agregó un whence
pero agregó un -p
opción para type
para imprimir la ruta (type -p
sería como whence -p
) y -a
para reportar todos los comandos coincidentes. tcsh
hizo which
incorporado y agregó un where
comando actuando como bash
's type -a
. zsh
los tiene todos.
El fish
shell (2005) tiene un type
comando implementado como una función.
El which
Mientras tanto, el script csh se eliminó de NetBSD (ya que estaba integrado en tcsh y no era muy útil en otros shells), y la funcionalidad se agregó a whereis
(cuando se invoca como which
, whereis
se comporta como which
excepto que solo busca ejecutables en $PATH
). En OpenBSD y FreeBSD, which
también se cambió a uno escrito en C que busca comandos en $PATH
solo.
Implementaciones
Hay docenas de implementaciones de un which
Comando en varios Unices con diferente sintaxis y comportamiento.
En Linux (además de los incorporados en tcsh
y zsh
) encontramos varias implementaciones. En los sistemas Debian recientes, por ejemplo, es un simple script de shell POSIX que busca comandos en $PATH
.
busybox
también tiene un which
comando.
Hay un GNU
which
que es probablemente el más extravagante. Intenta extender lo que el which
csh script hizo con otros shells:puede decirle cuáles son sus alias y funciones para que pueda darle una mejor respuesta (y creo que algunas distribuciones de Linux establecen algunos alias globales en torno a eso para bash
hacer eso).
zsh
tiene un par de operadores para expandir a la ruta de los ejecutables:el =
expansión de nombre de archivo operador y el :c
modificador de expansión de historial (aquí aplicado a expansión de parámetros ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh
, en el zsh/parameters
El módulo también crea la tabla hash de comandos como commands
matriz asociativa:
$ print -r -- $commands[ls]
/bin/ls
El whatis
utilidad (excepto la que está en Unix V8 Bourne shell o Plan 9 rc
/es
) no está realmente relacionado ya que es solo para documentación (greps la base de datos whatis, esa es la sinopsis de la página man).
whereis
también se agregó en 3BSD
al mismo tiempo que which
aunque estaba escrito en C
, no csh
y se utiliza para buscar al mismo tiempo, el ejecutable, la página del manual y la fuente, pero no se basa en el entorno actual. De nuevo, eso responde a una necesidad diferente.
Ahora, en el frente estándar, POSIX especifica el command -v
y -V
comandos (que solían ser opcionales hasta POSIX.2008). UNIX especifica el type
Comando (sin opción). Eso es todo (where
, which
, whence
no están especificados en ninguna norma).
Hasta alguna versión, type
y command -v
eran opcionales en la especificación de la base estándar de Linux, lo que explica por qué, por ejemplo, algunas versiones antiguas de posh
(aunque basado en pdksh
que tenía ambos) no tenía ninguno. command -v
también se agregó a algunas implementaciones de shell Bourne (como en Solaris).
Estado hoy
El estado actual es que type
y command -v
son omnipresentes en todos los shells tipo Bourne (aunque, como señaló @jarno, tenga en cuenta la advertencia/error en bash
cuando no está en modo POSIX o algunos descendientes del shell Almquist a continuación en los comentarios). tcsh
es el único shell en el que querrías usar which
(ya que no hay type
allí y which
está integrado).
En los shells que no sean tcsh
y zsh
, which
puede decirle la ruta del ejecutable dado, siempre que no haya un alias o función con ese mismo nombre en cualquiera de nuestros ~/.cshrc
, ~/.bashrc
o cualquier archivo de inicio de shell y no define $PATH
en su ~/.cshrc
. Si tiene un alias o una función definida para él, puede o no informarle al respecto, o decirle algo incorrecto.
Si desea conocer todos los comandos por un nombre dado, no hay nada portátil. Usarías where
en tcsh
o zsh
, type -a
en bash
o zsh
, whence -a
en ksh93 y en otros shells, puede usar type
en combinación con which -a
que puede funcionar.
Recomendaciones
Obtención de la ruta a un ejecutable
Ahora, para obtener el nombre de ruta de un ejecutable en un script, hay algunas advertencias:
ls=$(command -v ls)
sería la forma estándar de hacerlo.
Sin embargo, hay algunos problemas:
- No es posible conocer la ruta del ejecutable sin ejecutarlo. Todo el
type
,which
,command -v
… todos usan heurísticas para encontrar el camino. Recorren el$PATH
componentes y busque el primer archivo que no sea de directorio para el que tenga permiso de ejecución. Sin embargo, dependiendo del shell, cuando se trata de ejecutar el comando, muchos de ellos (Bourne, AT&T ksh, zsh, ash…) simplemente los ejecutarán en el orden de$PATH
hasta elexecve
la llamada al sistema no regresa con un error. Por ejemplo, si$PATH
contiene/foo:/bar
y quieres ejecutarls
, primero intentarán ejecutar/foo/ls
o si eso falla/bar/ls
. Ahora ejecución de/foo/ls
puede fallar porque no tiene permiso de ejecución, pero también por muchas otras razones, como que no es un ejecutable válido.command -v ls
informaría/foo/ls
si tiene permiso de ejecución para/foo/ls
, pero ejecutandols
en realidad podría ejecutar/bar/ls
si/foo/ls
no es un ejecutable válido. - si
foo
es una función integrada o un alias,command -v foo
devuelvefoo
. Con algunas conchas comoash
,pdksh
ozsh
, también puede devolverfoo
si$PATH
incluye la cadena vacía y hay un ejecutablefoo
archivo en el directorio actual. Hay algunas circunstancias en las que es posible que deba tener eso en cuenta. Tenga en cuenta, por ejemplo, que la lista de elementos integrados varía según la implementación del shell (por ejemplo,mount
a veces está integrado para busyboxsh
), y por ejemplobash
puede obtener funciones del entorno. - si
$PATH
contiene componentes de rutas relativas (típicamente.
o la cadena vacía que se refiere al directorio actual pero podría ser cualquier cosa), según el shell,command -v cmd
podría no generar una ruta absoluta. Entonces, la ruta que obtienes en el momento en que ejecutascommand -v
ya no será válido después decd
en otro lugar. - Anecdótico:con el shell ksh93, si
/opt/ast/bin
(aunque esa ruta exacta puede variar en diferentes sistemas, creo) está en usted$PATH
, ksh93 pondrá a disposición algunos componentes adicionales (chmod
,cmp
,cat
…), perocommand -v chmod
devolverá/opt/ast/bin/chmod
incluso si ese camino no existe.
Determinar si existe un comando
Para averiguar si un comando dado existe de forma estándar, puede hacer:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Donde uno podría querer usar which
(t)csh
En csh
y tcsh
, no tienes muchas opciones. En tcsh
, eso está bien como which
está incorporado. En csh
, ese será el sistema which
comando, que puede no hacer lo que usted quiere en algunos casos.
Encontrar comandos solo en algunos shells
Un caso en el que podría tener sentido usar which
es si desea conocer la ruta de un comando, ignorando los posibles componentes o funciones de shell en bash
, csh
(no tcsh
), dash
o Bourne
shell scripts, es decir, shells que no tienen whence -p
(como ksh
o zsh
), command -ev
(como yash
), whatis -p
(rc
, akanga
) o un which
incorporado (como tcsh
o zsh
) en sistemas donde which
está disponible y no es el csh
guión.
Si se cumplen esas condiciones, entonces:
echo=$(which echo)
te daría la ruta del primer echo
en $PATH
(excepto en casos de esquina), independientemente de si echo
también pasa a ser un shell incorporado/alias/función o no.
En otros shells, preferirías:
- zsh :
echo==echo
oecho=$commands[echo]
oecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- Yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`
(cuidado con los caminos con espacios) - peces :
set echo (type -fp echo)
Tenga en cuenta que si todo lo que quiere hacer es ejecutar ese echo
comando, no tiene que obtener su ruta, simplemente puede hacer:
env echo this is not echoed by the builtin echo
Por ejemplo, con tcsh
, para evitar el which
incorporado de ser usado:
set Echo = "`env which echo`"
Cuando necesite un comando externo
Otro caso en el que puede querer usar which
es cuando realmente necesitas un comando externo. POSIX requiere que todas las funciones internas de shell (como command
) también estarán disponibles como comandos externos, pero desafortunadamente, ese no es el caso para command
en muchos sistemas. Por ejemplo, es raro encontrar un command
comando en sistemas operativos basados en Linux, mientras que la mayoría de ellos tienen un which
comando (aunque diferentes con diferentes opciones y comportamientos).
Los casos en los que puede querer un comando externo serían donde ejecutaría un comando sin invocar un shell POSIX.
El system("some command line")
, popen()
… las funciones de C o varios lenguajes invocan un shell para analizar esa línea de comando, entonces system("command -v my-cmd")
trabajar en ellos. Una excepción a eso sería perl
que optimiza el shell si no ve ningún carácter especial de shell (aparte del espacio). Eso también se aplica a su operador de acento grave:
$ 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
La adición de ese :;
arriba obliga a perl
para invocar un caparazón allí. Usando which
, no tendrías que usar ese truco.