GNU/Linux >> Tutoriales Linux >  >> Linux

¿Por qué no usar "cuál"? ¿Qué usar entonces?

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

Relacionado:Debian:¿cómo usar controladores inalámbricos patentados durante la instalación de Debian USB?

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 el execve la llamada al sistema no regresa con un error. Por ejemplo, si $PATH contiene /foo:/bar y quieres 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 para /foo/ls , pero ejecutando ls 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 devuelve foo . Con algunas conchas como ash , pdksh o zsh , también puede devolver foo si $PATH incluye la cadena vacía y hay un ejecutable foo 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 busybox sh ), y por ejemplo bash 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 ejecutas command -v ya no será válido después de cd 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 …), pero command -v chmod devolverá /opt/ast/bin/chmod incluso si ese camino no existe.
Relacionado:Dd vs cat:¿dd sigue siendo relevante en estos días?

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


Linux
  1. Cómo:¿Qué es Git y Github? ¿Cómo lo uso y por qué debería importarme?

  2. ¿Por qué Bash está en todas partes (en la mayoría, si no en todas, las distribuciones de Linux)?

  3. ¿Por qué usamos su - y no solo su?

  4. ¿Qué caracteres debo usar o no usar en los nombres de usuario en Linux?

  5. ¿Por qué no bloquear ICMP?

¿Qué es el Shell en Linux?

¿Qué es una máquina virtual y por qué usarla?

¿Qué son los contenedores multicuenta de Firefox? ¿Por qué y cómo usarlo?

¿Qué es Login Shell en Linux?

¿Qué es la función de la comunidad ONLYOFFICE y por qué debería usarla?

¿Qué es un archivo .sh?