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 whichcomando 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-cmdcomandos 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. cshintrodujo alias alrededor de 1978 (aunque cshse lanzó por primera vez en 2BSDmayo de 1979), y también el procesamiento de una .cshrcpara que los usuarios personalicen el shell (cada shell, como se cshlee .cshrcincluso 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 rcarchivo ( .profilees 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 whichscript csh para que los cshusuarios ayudaran a identificar un ejecutable, y es un script apenas diferente que se puede encontrar whichen 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 cshscripts 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 cshbasa en $PATH).
Aquí tienes, whichllegó primero para el shell más popular en ese momento (y cshaú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 cshusuario, ese whichscript 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 sourceingresar otro csharchivo y (aunque eso no sería una buena idea), PATHpodría redefinirse ~/.cshrc.
Ejecutar ese whichcomando 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 typecomando 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 typecomando inicial sufrió un problema similar al whichscript 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/lslugar de solo lo /bin/lsque 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 typenombre incorporado whatis. Y el shell Plan9 (el sucesor de Unix) rc(y sus derivados like akangay 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 cshcaracterísticas (editor de línea, alias ...) en la parte superior del shell Bourne . Agregó su propio whenceincorporado (además de type) que tomó varias opciones ( -vpara proporcionar una typesalida detallada similar, y -pbuscar 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), zshsalió entre 1989 y 1991.
Ash, aunque pretendía ser un reemplazo para el shell Bourne, no tuvo un typebuiltin incorporado hasta mucho más tarde (en NetBSD 1.3 y FreeBSD 2.3), aunque lo tenía hash -v. OSF / 1 /bin/shtenía un typeincorporado que siempre devolvía 0 hasta OSF / 1 v3.x. bashno agregó un whencepero agregó una -popción para typeimprimir la ruta ( type -psería como whence -p) e -ainformar todos los comandos coincidentes. tcshhecho whichincorporado y ha añadido un wherecomando de actuar como bash's type -a. zshlos tiene todos.
El fishshell (2005) tiene un typecomando implementado como una función.
Mientras whichtanto, 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 whereiscomporta como, whichexcepto que solo busca ejecutables $PATH). En OpenBSD y FreeBSD, whichtambién se cambió a uno escrito en C que solo busca comandos $PATH.
Implementaciones
Hay docenas de implementaciones de un whichcomando en varios Unices con diferente sintaxis y comportamiento.
En Linux (además de los incorporados en tcshy zsh) encontramos varias implementaciones. En los sistemas Debian recientes, por ejemplo, es un simple script de shell POSIX que busca comandos en $PATH.
busyboxTambién tiene un whichcomando.
Hay una GNU whichque probablemente sea la más extravagante. Intenta extender lo que el whichscript 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 bashhacerlo). .
zshtiene un par de operadores para expandir a la ruta de los ejecutables: el operador de = expansión de nombre de archivo y el :cmodificador 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/parametersmódulo también crea la tabla hash de comandos como la commandsmatriz asociativa:
$ print -r -- $commands[ls]
/bin/ls
La whatisutilidad (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).
whereistambién se agregó 3BSDal mismo tiempo como whichsi estuviera escrito C, no cshy 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 -vy -V(que solían ser opcionales hasta POSIX.2008). UNIX especifica el typecomando (sin opción). Eso es todo ( where, which, whenceno se especifican en cualquier punto de vista)
Hasta alguna versión, typey command -veran opcionales en la especificación de Linux Standard Base que explica por qué, por ejemplo, algunas versiones antiguas de posh(aunque basadas en las pdkshque tenían ambas) tampoco tenían. command -vtambién se agregó a algunas implementaciones de shell Bourne (como en Solaris).
Estado hoy
El estado actual es ese typey command -vestán en todas partes en todos los shells tipo Bourne (aunque, como lo señaló @jarno, tenga en cuenta la advertencia / error bashcuando no está en modo POSIX o algunos descendientes del shell Almquist a continuación en los comentarios). tcshes el único shell donde querrías usar which(ya que no hay typeallí y whichestá integrado).
En los shells que no sean tcshy zsh, es whichposible 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, ~/.bashrco que no defina $PATHen 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 whereen tcsho zsh, type -aen basho zsh, whence -aen ksh93 y en otros shells, puede usarlo typeen combinación con lo which -aque 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 $PATHcomponentes 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 $PATHhasta que la execvellamada al sistema no regrese con un error . Por ejemplo, si $PATHcontiene /foo:/bary desea ejecutar ls, primero intentarán ejecutar /foo/lso si eso falla /bar/ls. Ahora ejecución de/foo/lspuede 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 lsinformaría /foo/lssi tiene permiso de ejecución /foo/ls, pero la ejecución lspodría ejecutarse /bar/lssi /foo/lsno es un ejecutable válido.
- si
fooes un incorporado o una función o alias, command -v foodevuelve foo. Con algunos shells como ash, pdksho zsh, también puede regresar foosi $PATHincluye la cadena vacía y hay un fooarchivo 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 mountveces está integrada para busybox sh) y, por ejemplo, bashpuede obtener funciones del entorno.
- si
$PATHcontiene 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 cmdpodría no generar una ruta absoluta. Por lo tanto, la ruta que obtienes en el momento en que corres command -vya no será válida después de que estés cden 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 chmodregresará /opt/ast/bin/chmodincluso 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 cshy tcsh, no tienes mucha opción. En tcsh, eso está bien como whichestá incorporado. En csh, ese será el whichcomando 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 whiches si desea conocer la ruta de un comando, ignorando las funciones o funciones potenciales de shell en bash, csh(no tcsh) dasho Bournescripts de shell, es decir, shells que no tienen whence -p(like ksho zsh) , command -ev(like yash), whatis -p( rc, akanga) o un builtin which(like tcsho zsh) en sistemas donde whichestá disponible y no es el cshscript.
Si se cumplen esas condiciones, entonces:
echo=$(which echo)
le daría la trayectoria de la primera echoen $PATH(excepto en casos de esquina), con independencia de que echotambién pasa a ser un / alias / función de línea de orden interna o no.
En otras conchas, preferirías:
- zsh :
echo==echoo 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 echocomando, 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 whichse use el incorporado :
set Echo = "`env which echo`"
cuando necesitas un comando externo
Otro caso en el que es posible que desee utilizar whiches 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 commanden muchos sistemas. Por ejemplo, es raro encontrar un commandcomando en sistemas operativos basados en Linux, mientras que la mayoría de ellos tienen un whichcomando (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 perlque 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 perla invocar un caparazón allí. Al usar which, no tendrías que usar ese truco.
whichsuponen un contexto de shell interactivo. Esta pregunta está etiquetada / portabilidad. Así que interpreto la pregunta en este contexto como "qué usar en lugar dewhichencontrar el primer ejecutable de un nombre dado en el$PATH". La mayoría de las respuestas y razones en contra dewhichtratar 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.).