Aquí está todo lo que nunca pensó que nunca querría saber al respecto:
Resumen
Para obtener el nombre de ruta de un ejecutable en un script de shell similar a Bourne (hay algunas advertencias; ver más abajo):
ls=$(command -v ls)
Para averiguar 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
comando es una herencia rota del C-Shell y es mejor dejarlo solo en los proyectiles tipo Bourne.
Casos de uso
Hay una distinción entre buscar esa información como parte de un script o interactivamente en el indicador de comandos de shell.
En el indicador de comandos 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 mirar más allá de lo que es?
En ese caso, desea saber qué hace su shell cuando invoca el comando sin invocarlo.
En los scripts de shell, tiende a ser bastante diferente. En un script de shell no hay ninguna razón por la que quieras saber dónde o qué es un comando si todo lo que quieres hacer es ejecutarlo. En general, lo que quiere saber es la ruta del archivo ejecutable, para que pueda obtener más información (como la ruta a otro archivo relativo a eso o leer información del contenido del archivo ejecutable en esa ruta).
Interactivamente, es posible que desee conocer todos los 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 depósitos 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
se lanzó por primera vez en 2BSD
mayo de 1979), y también el procesamiento de una .cshrc
para que los usuarios personalicen el shell (cada shell, como se csh
lee .cshrc
incluso cuando no es interactivo como en los scripts).
Si bien el shell Bourne 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 algún rc
archivo ( .profile
es para configurar su entorno, no el shell per se ).
csh
se hizo mucho más popular que el shell Bourne ya que (aunque tenía una sintaxis mucho peor que el shell Bourne) estaba agregando muchas más características convenientes y agradables para uso interactivo.
En 3BSD
(1980), se agregó un which
script csh para que los csh
usuarios ayudaran a identificar un ejecutable, y es un script apenas diferente que se puede encontrar which
en muchos Unices comerciales hoy en día (como Solaris, HP / UX, AIX o Tru64).
Ese script lee al usuario ~/.cshrc
(como lo hacen todos los csh
scripts a menos que se invoque con csh -f
), y busca los nombres de comando proporcionados en la lista de alias y en $path
(la matriz que se csh
basa en $PATH
).
Aquí tienes, which
llegó primero para el shell más popular en ese momento (y csh
aún era popular hasta mediados de los 90), que es la razón principal por la que se documentó en los libros y todavía se usa ampliamente.
Tenga en cuenta que, incluso para un csh
usuario, ese which
script csh no necesariamente le brinda la información correcta. Obtiene los alias definidos ~/.cshrc
, no los que haya definido más adelante en el indicador o, por ejemplo, al source
ingresar otro csh
archivo y (aunque eso no sería una buena idea), PATH
podría redefinirse ~/.cshrc
.
Ejecutar ese which
comando desde un shell Bourne aún buscaría los alias definidos en su ~/.cshrc
, pero si no tiene uno porque no lo usa csh
, probablemente todavía obtendrá 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 shell.
El type
comando inicial sufrió un problema similar al which
script en que no devolvió un estado de salida de falla si no se encontró el comando. Además, para los ejecutables, al contrario which
, genera algo así en ls is /bin/ls
lugar de solo lo /bin/ls
que lo hace menos fácil de usar en los scripts.
El shell Bourne de Unix Versión 8 (no lanzado en la naturaleza) tenía su type
nombre incorporado whatis
. Y el shell Plan9 (el sucesor de Unix) rc
(y sus derivados like akanga
y es
) también lo tienen whatis
.
El shell Korn (un subconjunto en el que se basa la definición POSIX sh), desarrollado a mediados de los años 80 pero no ampliamente disponible antes de 1988, agregó muchas de las csh
características (editor de línea, alias ...) en la parte superior del shell Bourne . Agregó su propio whence
incorporado (además de type
) que tomó varias opciones ( -v
para proporcionar una type
salida detallada similar, y -p
buscar solo ejecutables (no alias / funciones ...)).
Coincidiendo con la agitación con respecto a los problemas de derechos de autor entre AT&T y Berkeley, a finales de los 80 y principios de los 90 surgieron algunas implementaciones de software libre . Todo el shell Almquist (ash, que 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 pretendía ser un reemplazo para el shell Bourne, no tuvo un type
builtin incorporado hasta mucho más tarde (en NetBSD 1.3 y FreeBSD 2.3), aunque lo 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ó una -p
opción para type
imprimir la ruta ( type -p
sería como whence -p
) e -a
informar todos los comandos coincidentes. tcsh
hecho which
incorporado y ha añadido un where
comando de actuar como bash
's type -a
. zsh
los tiene todos.
El fish
shell (2005) tiene un type
comando implementado como una función.
Mientras which
tanto, el script csh se eliminó de NetBSD (ya que estaba incorporado en tcsh y no era de mucha utilidad en otros shells), y la funcionalidad añadida a whereis
(cuando se invoca como which
, se whereis
comporta como, which
excepto que solo busca ejecutables $PATH
). En OpenBSD y FreeBSD, which
también se cambió a uno escrito en C que solo busca comandos $PATH
.
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 una GNU
which
que probablemente sea la más extravagante. Intenta extender lo que el which
script csh hizo a otros shells: puedes decirle cuáles son tus alias y funciones para que pueda darte una mejor respuesta (y creo que algunas distribuciones de Linux establecen algunos alias globales para bash
hacerlo). .
zsh
tiene un par de operadores para expandir a la ruta de los ejecutables: el operador de =
expansión de nombre de archivo y el :c
modificador de expansión de historial (aquí aplicado a la expansión de parámetros ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh
, en el zsh/parameters
módulo también crea la tabla hash de comandos como la commands
matriz asociativa:
$ print -r -- $commands[ls]
/bin/ls
La whatis
utilidad (excepto la de Unix V8 Bourne shell o Plan 9 rc
/ es
) no está realmente relacionada, ya que es solo para documentación (greps, la base de datos whatis, esa es la sinopsis de la página de manual).
whereis
también se agregó 3BSD
al mismo tiempo como which
si estuviera escrito C
, no csh
y se usa para buscar al mismo tiempo, el ejecutable, la página de manual y la fuente, pero no se basa en el entorno actual. Entonces, de nuevo, eso responde a una necesidad diferente.
Ahora, en el frente estándar, POSIX especifica los comandos command -v
y -V
(que solían ser opcionales hasta POSIX.2008). UNIX especifica el type
comando (sin opción). Eso es todo ( where
, which
, whence
no se especifican en cualquier punto de vista)
Hasta alguna versión, type
y command -v
eran opcionales en la especificación de Linux Standard Base que explica por qué, por ejemplo, algunas versiones antiguas de posh
(aunque basadas en las pdksh
que tenían ambas) tampoco tenían. command -v
también se agregó a algunas implementaciones de shell Bourne (como en Solaris).
Estado hoy
El estado actual es ese type
y command -v
están en todas partes en todos los shells tipo Bourne (aunque, como lo señaló @jarno, tenga en cuenta la advertencia / error bash
cuando no está en modo POSIX o algunos descendientes del shell Almquist a continuación en los comentarios). tcsh
es el único shell donde querrías usar which
(ya que no hay type
allí y which
está integrado).
En los shells que no sean tcsh
y zsh
, es which
posible que le indiquen la ruta del ejecutable dado siempre que no haya un alias o función con ese mismo nombre en ninguno de nuestros archivos de inicio de shell ~/.cshrc
, ~/.bashrc
o que no defina $PATH
en su ~/.cshrc
. Si tiene un alias o una función definidos, puede o no informarle al respecto, o decirle algo incorrecto.
Si desea conocer todos los comandos por un nombre de pila, no hay nada portátil. Lo usaría where
en tcsh
o zsh
, type -a
en bash
o zsh
, whence -a
en ksh93 y en otros shells, puede usarlo type
en combinación con lo which -a
que puede funcionar.
Recomendaciones
Obtener el nombre de 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. Todos los
type
, which
, command -v
... todos utilizan la heurística para averiguar la ruta. Recorren los $PATH
componentes y encuentran el primer archivo que no es de directorio para el que tiene 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 que la execve
llamada al sistema no regrese con un error . Por ejemplo, si $PATH
contiene /foo:/bar
y desea ejecutar ls
, 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 /foo/ls
, pero la ejecución ls
podría ejecutarse /bar/ls
si /foo/ls
no es un ejecutable válido.
- si
foo
es un incorporado o una función o alias, command -v foo
devuelve foo
. Con algunos shells como ash
, pdksh
o zsh
, también puede regresar foo
si $PATH
incluye la cadena vacía y hay un foo
archivo ejecutable 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 funciones integradas varía con la implementación del shell (por ejemplo, a mount
veces está integrada para busybox sh
) y, por ejemplo, bash
puede obtener funciones del entorno.
- si
$PATH
contiene componentes de ruta relativos (normalmente .
o la cadena vacía que se refieren al directorio actual pero podrían ser cualquier cosa), dependiendo del shell, command -v cmd
podría no generar una ruta absoluta. Por lo tanto, la ruta que obtienes en el momento en que corres command -v
ya no será válida después de que estés cd
en otro lugar.
- Anecdótica: con la cáscara ksh93, si
/opt/ast/bin
(a pesar de que la ruta exacta puede variar en diferentes sistemas, creo que) está en ti $PATH
, ksh93 hará unas pocas órdenes internas adicionales disponibles ( chmod
, cmp
, cat
...), pero command -v chmod
regresará /opt/ast/bin/chmod
incluso si ese camino doesn' t existe.
Determinar si existe un comando
Para averiguar si un comando dado existe de manera estándar, puede hacer lo siguiente:
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 mucha opción. En tcsh
, eso está bien como which
está incorporado. En csh
, ese será el which
comando del sistema , que puede no hacer lo que desea en algunos casos.
buscar comandos solo en algunos shells
Un caso en el que podría tener sentido usarlo which
es si desea conocer la ruta de un comando, ignorando las funciones o funciones potenciales de shell en bash
, csh
(no tcsh
) dash
o Bourne
scripts de shell, es decir, shells que no tienen whence -p
(like ksh
o zsh
) , command -ev
(like yash
), whatis -p
( rc
, akanga
) o un builtin which
(like tcsh
o zsh
) en sistemas donde which
está disponible y no es el csh
script.
Si se cumplen esas condiciones, entonces:
echo=$(which echo)
le daría la trayectoria de la primera echo
en $PATH
(excepto en casos de esquina), con independencia de que echo
también pasa a ser un / alias / función de línea de orden interna o no.
En otras conchas, preferirías:
- zsh :
echo==echo
o echo=$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 que which
se use el incorporado :
set Echo = "`env which echo`"
cuando necesitas un comando externo
Otro caso en el que es posible que desee utilizar which
es cuando realmente necesita un comando externo. POSIX requiere que todos los componentes integrados de shell (como command
) también estén disponibles como comandos externos, pero desafortunadamente ese no es el caso 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 es posible que desee un comando externo estarían donde ejecute un comando sin invocar un shell POSIX.
El system("some command line")
, popen()
... funciones de C o varias lenguas no invocan una cáscara de analizar esa línea de comandos, por lo que system("command -v my-cmd")
hacen el trabajo en ellos. Una excepción a eso sería la perl
que optimiza el shell si no ve ningún carácter especial del shell (que no sea espacio). Eso también se aplica a su operador 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
La adición de lo :;
anterior obliga perl
a invocar un caparazón allí. Al usar which
, no tendrías que usar ese truco.
which
suponen un contexto de shell interactivo. Esta pregunta está etiquetada / portabilidad. Así que interpreto la pregunta en este contexto como "qué usar en lugar dewhich
encontrar el primer ejecutable de un nombre dado en el$PATH
". La mayoría de las respuestas y razones en contra dewhich
tratar con alias, funciones y funciones, que en la mayoría de los scripts de shell portátiles del mundo real son solo de interés académico. Los alias definidos localmente no se heredan cuando se ejecuta un script de shell (a menos que lo obtenga.
).