¿Por qué no utilizar “ cuál ”? ¿Qué usar entonces?

Cuando busque la ruta a un ejecutable o verifique lo que sucedería si ingresa un nombre de comando en un shell de Unix, hay una plétora de utilidades diferentes ( which, type, command, whence, where, whereis, whatis, hash, etc.).

A menudo escuchamos que which debe evitarse. ¿Por qué? ¿Qué deberíamos usar en su lugar?

Comentarios

  • Creo que la mayoría de los argumentos en contra del uso de which están asumiendo un contexto de shell interactivo. Esta pregunta está etiquetada / portabilidad. Así que interpreto la pregunta en este contexto como » qué usar en lugar de which para encontrar el primer ejecutable de un nombre dado en el $PATH «. La mayoría de las respuestas y razones en contra which trata con alias, incorporaciones 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 son ‘ t heredados cuando se ejecuta un script de shell (a menos que lo obtenga con .).
  • @MattBianco, sí, csh (y which sigue siendo una secuencia de comandos csh en la mayoría de Unices) lee ~/.cshrc cuando no es interactivo. Esa ‘ es la razón por la que ‘ notarás que los scripts csh generalmente comienzan con #! /bin/csh -f. which no lo hace porque tiene como objetivo darte los alias, porque ‘ es una herramienta para usuarios (interactivos) de csh. Los usuarios de shells POSIX tienen command -v.
  • @rudimeier, entonces la respuesta sería siempre a menos que su shell sea (t)csh (o no ‘ te importa si no ‘ no te da el resultado correcto), usa type o command -v en su lugar . Vea las respuestas para por qué .
  • @rudimeier, (stat $(which ls) es incorrecto por varias razones (falta --, comillas faltantes), no solo el uso de which). Usted ‘ usaría stat -- "$(command -v ls)". Eso supone que ls es de hecho un comando que se encuentra en el sistema de archivos (no una función incorporada de su shell, o una función de alias). which puede darle la ruta incorrecta (no la ruta que su shell ejecutaría si ingresara ls) o darle un alias como se define en la configuración de algunos otros shells …
  • @rudimeier, nuevamente, hay una serie de condiciones bajo las cuales muchas which implementaciones no le darían ni siquiera el ls que se encontrarían mediante una búsqueda de $PATH (independientemente de lo que ls puede invocar en su caparazón). sh -c 'command -v ls' o zsh -c 'rpm -q --whatprovides =ls' son más propensos a darte la respuesta correcta. El punto aquí es que which es una herencia rota de csh.

Respuesta

Aquí está todo lo que nunca pensó que no 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 El comando es una herencia rota de C-Shell y es mejor dejarlo solo en shells tipo Bourne.

Casos de uso

Allí «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 ¿Qué ocurrió exactamente cuando escribí mycmd? ¿Puedo ver más a fondo qué es?

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 desee saber dónde o qué es un comando si todo lo que desea hacer es ejecutarlo. Por lo general, lo que desea saber es la ruta del ejecutable, por lo que puede obtener más información (como la ruta a otro archivo en relación con ese, o leer información del contenido del archivo ejecutable en esa ruta).

De forma interactiva, 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 usarse de forma interactiva.

Historia

Primero un poco de historia.

Las primeras 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 se lanzó 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 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 archivo rc (el .profile es para configurar su entorno, no el shell per se ).

csh se volvió mucho más popular que el shell Bourne como (aunque tenía una sintaxis terriblemente peor que el Bourne) estaba agregando muchas características más convenientes y agradables para uso interactivo.

En 3BSD (1980), un which script csh se agregó para los 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 usuario» s ~/.cshrc (como todas las secuencias de comandos csh 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 basada en $PATH).

Aquí tienes: which llegó primero para el shell más popular en ese momento (y csh siguió siendo popular hasta mediados de 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 El script csh no necesariamente le da la información correcta. Obtiene los alias definidos en ~/.cshrc, no los que puede haber definido más adelante en el indicador o, por ejemplo, source en otro csh archivo y (aunque eso no sería una buena idea), PATH podría redefinirse en ~/.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 usa csh, probablemente obtendrá la respuesta correcta.

No se agregó una funcionalidad similar a el shell Bourne hasta 1984 en SVR2 con el comando interno type. 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 comando type inicial sufrió un problema similar al de la secuencia de comandos which en el sentido de que no devolvió un estado de salida de falla si no se encontró el comando. Además, para los ejecutables, a diferencia de which, genera algo como ls is /bin/ls en lugar de solo /bin/ls que lo hacía menos fácil de usar en scripts.

Unix Versión 8″ s (no lanzado en la naturaleza) Bourne shell lo tenía «s type incorporado renombrado a whatis. Y el shell Plan9 (el futuro sucesor de Unix) rc (y su derivados como akanga y es) también tienen whatis.

El caparazón de Korn (un subconjunto del cual el POSIX sh se basa en la definición), desarrollado a mediados de los 80 pero no 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 el resultado detallado type, y -p para buscar solo ejecutables (no alias / funciones …)) .

Coincidiendo con la confusión con respecto a los problemas de derechos de autor entre AT & T y Berkeley, surgieron algunas implementaciones de shell de software libre a finales de los 80 y principios de los 90.Todo el shell Almquist (ash, para reemplazar el shell Bourne en BSD), la implementación de dominio público de ksh (pdksh), bash (patrocinado por la FSF), zsh se publicó entre 1989 y 1991.

Ash, aunque estaba destinado a ser un reemplazo del shell Bourne, no tenía un type incorporado 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 devolvió 0 hasta OSF / 1 v3.x. bash no agregó un whence pero agregó un -p opción a type para imprimir la ruta (type -p sería como whence -p) y -a para informar todos los comandos coincidentes. tcsh hizo which incorporado y agregó un where comando que actúa como bash» s type -a. zsh los tiene todos.

El fish shell (2005) tiene un comando type 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 de mucha utilidad 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 únicamente .

Implementaciones

Hay docenas de implementaciones de un which co mmand en varios Unices con diferente sintaxis y comportamiento.

En Linux (además de los integrados en tcsh y zsh ) encontramos varias implementaciones. En sistemas Debian recientes, por ejemplo, es «un script de shell POSIX simple 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 script which csh hizo a otros shells: puede decirle cuáles son sus alias y funciones para que pueda dar tienes una mejor respuesta (y creo que algunas distribuciones de Linux establecen algunos alias globales para que bash lo haga).

zsh tiene un par de operadores para expandir a la ruta de los ejecutables: el operador = expansión de nombre de archivo 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 también hace que la tabla hash de comandos sea la commands matriz asociativa:

$ print -r -- $commands[ls] /bin/ls 

La utilidad whatis (excepto la de 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, que es la sinopsis de la página de manual»).

whereis también añadido 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 de 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 command -v y -V comandos (que solían ser opcionales hasta POSIX.2008). UNIX especifica el comando type (sin opción). Eso es todo (where, which, whence no se especifican en ningún estándar) .

Hasta algunas versiones, 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 basadas en pdksh que tenían ambas) no tenían ninguna. command -v también se agregó a algunas implementaciones de shell Bourne (como en Solaris).

Estado actual

El estado actual es que type y command -v son ubicuos en todos los shells tipo Bourne (aunque, como lo 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 le gustaría usar which (ya que «no hay type allí y which está incorporado).

En los shells que no sean tcsh y zsh, which puede indicarle la ruta del ejecutable dado siempre que no haya un alias o función con el 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 que le informe o no, o le diga algo incorrecto.

Si quiere saber sobre todos los comandos con 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

Obtener el nombre de la ruta de un ejecutable

Ahora, para obtener el nombre de la ruta de un ejecutable en un script, hay algunas advertencias:

ls=$(command -v ls) 

sería la forma estándar de hacerlo.

Sin embargo, existen algunos problemas:

  • No es posible conocer la ruta del ejecutable sin ejecutarlo. type, which, command -v … todos usan heurísticas para averiguar la ruta . Recorren los componentes $PATH y encuentran el primer archivo que no es de directorio para el que tiene permiso de ejecución. Sin embargo, depe Al encontrar en el 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 llamada al sistema execve no regrese con un error. Por ejemplo, si $PATH contiene /foo:/bar y quieres ejecutar ls, primero intentarán para ejecutar /foo/ls o si eso falla /bar/ls. Ahora la ejecución de /foo/ls puede fallar porque no tiene permiso de ejecución, pero también por muchas otras razones, como si no fuera un ejecutable válido. command -v ls informaría /foo/ls si tiene permiso de ejecución para /foo/ls, pero ejecuta ls podría ejecutar /bar/ls si /foo/ls no es un ejecutable válido.
  • si foo es una función o un alias incorporado, command -v foo devuelve foo. Con algunos shells como ash, pdksh o zsh, también puede devolver foo si $PATH incluye la cadena vacía y hay un archivo foo 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 elementos integrados varía con la implementación del shell (por ejemplo, mount a veces está integrado para busybox sh) y, por ejemplo, bash puede obtener funciones del entorno.
  • if $PATH contiene componentes de ruta relativa (normalmente . o la cadena vacía que se refieren al directorio actual pero podrían ser cualquier cosa), según el shell, command -v cmd no genere una ruta absoluta. Por lo tanto, la ruta que obtiene en el momento en que ejecuta ya no será válido después de cd en otro lugar.
  • Anecdótico: con el shell ksh93, si /opt/ast/bin (aunque creo que esa ruta exacta puede variar en diferentes sistemas) está en ti $PATH, ksh93 pondrá a disposición algunas incorporaciones adicionales (chmod, cmp, cat …), pero command -v chmod devolverá /opt/ast/bin/chmod incluso si esa ruta no existe.

Determinación de si existe un comando

Para averiguar si un comando determinado existe de manera 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, ya que which está integrado. En csh, ese será el comando del sistema which, que puede no hacer lo que usted desea en algunos casos.

Buscar 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 el potencial funciones o incorporaciones de shell en bash, csh (no tcsh), dash, o Bourne scripts de shell, es decir, shells que «no tienen whence -p (como ksh o zsh), command -ev (como yash ), whatis -p (rc, akanga) o una which (como tcsh o zsh) en sistemas donde which es disponible y no es el csh script.

Si se cumplen esas condiciones, entonces:

echo=$(which echo) 

le daría la ruta del primer echo en $PATH (excepto en los casos de esquina), independientemente de si echo también es un shell función incorporada / alias / o no.

En otros shells, preferiría:

  • 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 las rutas con espacios)
  • pez : 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, solo puede hacer:

env echo this is not echoed by the builtin echo 

Por ejemplo, con tcsh, para evitar que se utilice el which incorporado:

set Echo = "`env which echo`" 

Cuando necesita un comando externo

Otro caso en el que puede querer usar which es cuando realmente necesita un comando externo. POSIX requiere que todos los comandos internos de shell (como command) también estén disponibles como comandos externos, pero desafortunadamente, ese «no es el caso para command en muchos sistemas. Por ejemplo, es raro encontrar un comando command 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 podría 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 comandos, por lo que system("command -v my-cmd") trabaja en ellos. Una excepción sería perl que optimiza el shell si no ve ningún carácter especial del shell (que no sea el espacio). Eso también se aplica a su operador de comillas invertidas:

$ 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 :; anterior obliga a perl a invocar un shell allí. Al usar which, no tendría que usar ese truco.

Comentarios

  • @Joe, which es un script csh en muchos Unices comerciales. La razón es histórica, que ‘ es la razón por la que di el historial, para que la gente entienda de dónde viene, por qué la gente se acostumbró a usarlo y por qué en realidad hay ‘ s no hay razón para que deba usarlo. Y sí, algunas personas usan (t) csh. No todo el mundo usa Linux todavía
  • Después de leer esta publicación, he encontrado mucho contexto para la respuesta, pero no la respuesta en sí.¿Dónde en esta publicación dice realmente por qué no usar which, a diferencia de las cosas que podría estar intentando usar which que hacer, el historial de which, las implementaciones de which, otros comandos para realizar tareas relacionadas o las razones para usar which? ¿Por qué los otros comandos son mejores ? ¿Qué hacen de manera diferente a which? ¿Cómo evitan sus trampas? Esta respuesta en realidad gasta más palabras en los problemas con las alternativas que en los problemas con which.
  • command se describe por POSIX.
  • @St é phaneChazelas Si creo un nuevo archivo con touch /usr/bin/mytestfile y luego ejecuto command -v mytestfile, dará la ruta (mientras que which mytestfile no).
  • @jarno, oh sí, tú ‘ derecho. bash se decidirá por un archivo no ejecutable si ‘ t encontrar uno ejecutable, por lo que ‘ s » OK » (aunque en la práctica uno preferiría command -v / type devuelve un error) ya que ‘ es el comando que intentaría ejecutar cuando ejecuta mytestfile, pero el comportamiento dash tiene errores, como si hubiera ‘ un cmd antes de uno ejecutable, command -v devuelve el no ejecutable mientras que la ejecución de cmd ejecutaría el ejecutable (el uno también tiene hash). FreeBSD sh (también basado en ash) tiene el mismo error. zsh, yash, ksh, mksh, bash como sh están bien.

Responder

Las razones por las que uno puede No quiero usar which ya se han explicado, pero aquí hay algunos ejemplos en algunos sistemas en los que which realmente falla.

En shells de tipo Bourne, «estamos comparando la salida de which con la salida de type (type siendo un intérprete de órdenes incorporado, está destinado a ser la verdad básica, ya que es el intérprete de órdenes diciéndonos cómo invocaría un comando).

Muchos casos son casos de esquina , pero tenga en cuenta que which / type se utilizan a menudo en casos de esquina (para encontrar la respuesta a un comportamiento inesperado como: ¿por qué diablos ese comando se comporta así, a cuál estoy llamando? ).

La mayoría de los sistemas, la mayoría de shells tipo Bourne: funciones

El caso más obvio es para las funciones:

$ type ls ls is a function ls () { [ -t 1 ] && set -- -F "$@"; command ls "$@" } $ which ls /bin/ls 

La razón es que which solo informa sobre ejecutables y, a veces, sobre alias (aunque no siempre los de su shell), no funciones.

El GNU cuya página de manual tiene un ejemplo roto (ya que se olvidaron de citar $@) sobre cómo usarlo para informar funciones también, pero al igual que para alias, debido a que no implementa un analizador de sintaxis de shell, es fácil de engañar:

$ which() { (alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@";} $ f() { echo $"\n}\ng ()\n{ echo bar;\n}\n" >> ~/foo; } $ type f f is a function f () { echo " } g () { echo bar; } " >> ~/foo } $ type g bash: type: g: not found $ which f f () { echo " } $ which g g () { echo bar; } 

La mayoría de los sistemas, la mayoría de los shells tipo Bourne: incorporados

Otro caso obvio son las incorporaciones o palabras clave, ya que which siendo un comando externo no tiene forma de saber qué incorporaciones tiene tu shell (y algunas shells como zsh, bash o ksh pueden cargar archivos integrados de forma dinámica):

$ type echo . time echo is a shell builtin . is a shell builtin time is a shell keyword $ which echo . time /bin/echo which: no . in (/bin:/usr/bin) /usr/bin/time 

(eso no «se aplica a zsh donde which está integrado)

Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5. 1 y muchos otros:

$ csh % which ls ls: aliased to ls -F % unalias ls % which ls ls: aliased to ls -F % ksh $ which ls ls: aliased to ls -F $ type ls ls is a tracked alias for /usr/bin/ls 

Eso se debe a que en la mayoría de los Unices comerciales, which (como en la implementación original en 3BSD) es un csh script que dice ~/.cshrc. Los alias que informará son los definidos allí independientemente de los alias que haya definido actualmente e independientemente del shell que esté utilizando realmente.

En HP / UX o Tru64:

% echo "setenv PATH /bin:/usr/bin" >> ~/.cshrc % setenv PATH ~/bin:/bin:/usr/bin % ln -s /bin/ls ~/bin/ % which ls /bin/ls 

(las versiones de Solaris y AIX han solucionado ese problema guardando $path antes de leer el ~/.cshrc y restaurarlo antes de buscar los comandos)

$ type "a b" a b is /home/stephane/bin/a b $ which "a b" no a in /usr/sbin /usr/bin no b in /usr/sbin /usr/bin 

O:

$ d="$HOME/my bin" $ mkdir "$d"; PATH=$PATH:$d $ ln -s /bin/ls "$d/myls" $ type myls myls is /home/stephane/my bin/myls $ which myls no myls in /usr/sbin /usr/bin /home/stephane/my bin 

(por supuesto, al ser un script csh, no puede «esperar que funcione con argumentos que contengan espacios …)

CentOS 6.4, bash

$ type which which is aliased to `alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde" $ alias foo=": "|test|"" $ which foo alias foo=": "|test|"" /usr/bin/test $ alias $"foo=\nalias bar=" $ unalias bar -bash: unalias: bar: not found $ which bar alias bar=" 

En ese sistema, hay un alias definido en todo el sistema que envuelve el comando GNU which.

La salida falsa se debe a que which lee la salida de bash «s alias pero no sabe cómo analizarlo correctamente y usa heurísticas (un alias por línea, busca el primer comando encontrado después de |, ;, & …)

Lo peor en CentOS es que zsh tiene un comando interno which perfectamente correcto, pero CentOS logró romperlo reemplazándolo con un alias que no funciona para GNU which.

Debian 7.0, ksh93:

(aunque se aplica a la mayoría de los sistemas con muchos shells)

$ unset PATH $ which which /usr/local/bin/which $ type which which is a tracked alias for /bin/which 

En Debian, /bin/which es una secuencia de comandos /bin/sh. En mi caso, sh es dash pero «es lo mismo cuando» es bash.

Un PATH no establecido no es para deshabilitar la búsqueda de PATH, sino que significa usar el sistema «s RUTA predeterminada que desafortunadamente en Debian, nadie está de acuerdo (dash y bash tienen /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, zsh tiene /bin:/usr/bin:/usr/ucb:/usr/local/bin, ksh93 tiene /bin:/usr/bin, mksh tiene /usr/bin:/bin ($(getconf PATH)), execvp() (como en env) tiene :/bin:/usr/bin (sí, primero busca en el directorio actual)) .

Por eso which se equivoca anteriormente, ya que «está usando dash» la PATH que es diferente de ksh93 «s

No t mejor con GNU which que informa:

which: no which in ((null)) 

(curiosamente, de hecho hay un /usr/local/bin/which en mi sistema, que en realidad es una secuencia de comandos akanga que viene con akanga (una rc derivado de shell donde el PATH predeterminado es /usr/ucb:/usr/bin:/bin:.))

bash, cualquier sistema:

El a quien Chris se refiere en su respuesta :

$ PATH=$HOME/bin:/bin $ ls /dev/null /dev/null $ cp /bin/ls bin $ type ls ls is hashed (/bin/ls) $ command -v ls /bin/ls $ which ls /home/chazelas/bin/ls 

También después de llamar a hash manualmente:

$ type -a which which is /usr/local/bin/which which is /usr/bin/which which is /bin/which $ hash -p /bin/which which $ which which /usr/local/bin/which $ type which which is hashed (/bin/which) 

Ahora un caso donde which y algunas veces type fallan:

$ mkdir a b $ echo "#!/bin/echo" > a/foo $ echo "#!/" > b/foo $ chmod +x a/foo b/foo $ PATH=b:a:$PATH $ which foo b/foo $ type foo foo is b/foo 

Ahora, con algunos shells:

$ foo bash: ./b/foo: /: bad interpreter: Permission denied 

Con otros:

$ foo a/foo 

Ni which ni type puede saber de antemano que b/foo no puede b e ejecutado. Algunas shells como bash, ksh o yash, al invocar foo de hecho intentará ejecutar b/foo e informar un error, mientras que otros (como zsh, ash, csh, Bourne, tcsh) se ejecutarán a/foo tras la falla de la llamada del sistema execve() en b/foo.

Comentarios

  • mksh en realidad usa algo diferente para el $PATH predeterminado: primero , se usa la constante de tiempo de compilación del sistema operativo _PATH_DEFPATH (más comúnmente en los BSD), luego, se usa confstr(_CS_PATH, …) (POSIX), y si ambos no existen o fallan, se usa /bin:/usr/bin:/sbin:/usr/sbin.
  • En su primer ejemplo, incluso si ls es una función que está usando g ls de PATH. Y which está bien para decirte cuál se usa /usr/bin/ls o /usr/local/bin/ls. No ‘ t ver » ¿Por qué no usar cuál » ….
  • @rudimeier, que which ls me dará /bin/ls independientemente de si el ls la función llama a /bin/ls o /opt/gnu/bin/ls o dir o nada en absoluto. IOW, which (aquello que implementaciones, IMMV) está dando algo irrelevante
  • @St é phaneChazelas. No no no. Ya que mi ls es una función. que mi ls función llama a ls desde PATH. Ahora which me dice dónde está el archivo. Solo ve un caso de uso único: » ¿Qué haría mi shell con este comando? » Para este caso de uso which es incorrecto, correcto.Pero hay otros casos de uso en los que (GNU) which es exactamente lo correcto.
  • @rudimeter, depende de which implementación. Algunos te dirán que ‘ es un alias (si tienes un alias configurado o si hay un ~/.cshrc en tu casa que tiene tal alias), algunos le darán una ruta pero la incorrecta en algunas condiciones. sh -c 'command -v ls', aunque no es perfecto, es más probable que le dé la respuesta correcta a ese requisito diferente (y también es estándar).

Answer

Una cosa que (según mi rápido vistazo) parece que Stephane no mencionó es que which tiene ni idea de la tabla hash de ruta de su shell. Esto tiene el efecto de que puede devolver un resultado que no es representativo de lo que realmente se ejecuta, lo que lo hace ineficaz en la depuración.

Respuesta

Por lo general, me estremezco cuando se recomienda esta pregunta a usuarios desprevenidos porque atacar sin fundamento which no es útil para nadie.

Si which funciona bien y proporciona la respuesta correcta a alguna tarea, siguiendo el modelo de Unix: haz una cosa, hazlo bien , ¿por qué debería which ser prohibido?

La pregunta debería ser, entonces, ¿cuál funciona bien y hace bien un trabajo específico?

Por un lado, la utilidad externa en / bin / que en Debian es un script de shell cuyo objetivo es solo listar los ejecutables del nombre dado en la ruta. Creo que which cumple correctamente su objetivo previsto. No carga ningún alias, ninguna función, nada del shell, solo enumera los primeros (o todos) los ejecutables del nombre de pila en la RUTA. El que significa de haber encontrado un archivo con el mismo nombre dado es algo que el usuario debe averiguar por ella (él) self.

Sí, otras which implementaciones pueden (y normalmente tienen) tener problemas particulares.

Respuesta

A menudo escuchamos lo que debe evitarse. ¿Por qué? ¿Qué deberíamos usar en su lugar?

Nunca escuché eso. Proporcione ejemplos específicos. Me preocuparía por su distribución de Linux y los paquetes de software instalados, ya que es de donde viene which!

SLES 11.4 x86-64

en tcsh versión 6.18.01:

> which which which: shell built-in command. 

en bash versión 3.2-147:

> which which /usr/bin/which > which -v GNU which v2.19, Copyright (C) 1999 - 2008 Carlo Wood. GNU which comes with ABSOLUTELY NO WARRANTY; This program is free software; your freedom to use, change and distribute this program is protected by the GPL. 

which es parte de util-linux un paquete estándar distribuido por la Organización del Kernel de Linux para su uso como parte del sistema operativo Linux. También proporciona estos otros archivos

/bin/dmesg /bin/findmnt /bin/logger /bin/lsblk /bin/more /bin/mount /bin/umount /sbin/adjtimex /sbin/agetty /sbin/blkid /sbin/blockdev /sbin/cfdisk /sbin/chcpu /sbin/ctrlaltdel /sbin/elvtune /sbin/fdisk /sbin/findfs /sbin/fsck /sbin/fsck.cramfs /sbin/fsck.minix /sbin/fsfreeze /sbin/fstrim /sbin/hwclock /sbin/losetup /sbin/mkfs /sbin/mkfs.bfs /sbin/mkfs.cramfs /sbin/mkfs.minix /sbin/mkswap /sbin/nologin /sbin/pivot_root /sbin/raw /sbin/sfdisk /sbin/swaplabel /sbin/swapoff /sbin/swapon /sbin/switch_root /sbin/wipefs /usr/bin/cal /usr/bin/chrp-addnote /usr/bin/chrt /usr/bin/col /usr/bin/colcrt /usr/bin/colrm /usr/bin/column /usr/bin/cytune /usr/bin/ddate /usr/bin/fallocate /usr/bin/flock /usr/bin/getopt /usr/bin/hexdump /usr/bin/i386 /usr/bin/ionice /usr/bin/ipcmk /usr/bin/ipcrm /usr/bin/ipcs /usr/bin/isosize /usr/bin/line /usr/bin/linux32 /usr/bin/linux64 /usr/bin/look /usr/bin/lscpu /usr/bin/mcookie /usr/bin/mesg /usr/bin/mkzimage_cmdline /usr/bin/namei /usr/bin/rename /usr/bin/renice /usr/bin/rev /usr/bin/script /usr/bin/scriptreplay /usr/bin/setarch /usr/bin/setsid /usr/bin/setterm /usr/bin/tailf /usr/bin/taskset /usr/bin/time /usr/bin/ul /usr/bin/uname26 /usr/bin/unshare /usr/bin/uuidgen /usr/bin/wall /usr/bin/whereis /usr/bin/which /usr/bin/write /usr/bin/x86_64 /usr/sbin/addpart /usr/sbin/delpart /usr/sbin/fdformat /usr/sbin/flushb /usr/sbin/freeramdisk /usr/sbin/klogconsole /usr/sbin/ldattach /usr/sbin/partx /usr/sbin/rcraw /usr/sbin/readprofile /usr/sbin/rtcwake /usr/sbin/setctsid /usr/sbin/tunelp 

my util-linux es la versión 2.19. Las notas de la versión se pueden encontrar fácilmente desde la versión 2.13 con fecha (28 de agosto de 2007). No estoy seguro de cuál era el objetivo o el objetivo de esto, ciertamente no fue respondido en ese largo mensaje votado 331 veces.

Comentarios

  • Observe cómo La pregunta no menciona a qué Unix se refiere. Linux es solo uno de algunos.
  • Como muestra su which -v, ese ‘ s GNU que (el extravagante uno mencionado en la otra respuesta y de ninguna manera es específico de Linux), no util-linux, que AFAIK nunca incluyó una utilidad which. util-linux 2.19 es de 2011, GNU que 2.19 es de 2008.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *