¿Cómo encuentro de manera confiable la ruta completa de un programa en la RUTA?

12

Necesito encontrar la ruta de un programa dado PATHusando un script de shell. La ruta debe ser la ruta completa real del programa, que se puede pasar más tarde a una de las exec*funciones, que no busca en PATHsí misma, por ejemplo execv.

Hay programas como kill, que están disponibles como un programa real y un shell incorporado al mismo tiempo. Si este es el caso, necesito la ruta completa al programa real.

Hay varias utilidades que pueden encontrar un programa en el PATHespecificado en la Sección 2.9.1.1, Búsqueda de comandos y ejecución del estándar POSIX .

La hay which, que no forma parte de ningún estándar. Puede ser un programa regular en algunos sistemas, mientras que algunos shells lo proporcionan. Parece estar disponible en la mayoría de los sistemas y shells, pero los shells con una versión incorporada, también solo devuelven el nombre del incorporado en lugar de la ruta al ejecutable. Además, no está estandarizado de ninguna manera y puede devolver cualquier salida y tomar diferentes opciones.

bash# which kill
/usr/bin/kill
dash# which kill
/usr/bin/kill
fish# which kill
/usr/bin/kill
mksh# which kill
/usr/bin/kill
tcsh# which kill
kill: shell built-in command.
zsh# which kill
kill: shell built-in command

Hay whence, que es una de las conchas incorporadas. Pero no está disponible en muchos proyectiles. También devolverá el nombre del incorporado en lugar de la ruta al programa. Se -ppuede pasar A de dónde cambiar este comportamiento.

bash# whence kill
bash: whence: command not found
dash# whence kill
dash: 1: whence: not found
fish# whence kill
fish: Unknown command 'whence'
mksh# whence kill
kill
mksh# whence -p kill
/usr/bin/kill
tcsh# whence kill
whence: Command not found.
zsh# whence kill
kill
zsh# whence -p kill
/usr/bin/kill

Existe la commandconstrucción especificada por POSIX: 2008 . Desafortunadamente, también busca comandos regulares e incorporados y devolverá el nombre del incorporado en lugar de la ruta al programa sombreado por un incorporado del mismo nombre. Algunas conchas viejas aún no lo han implementado.

bash# command -v kill
kill
dash# command -v kill
kill
fish# command -v kill
/usr/bin/kill
mksh# command -v kill
kill
tcsh# command -v kill
command: Command not found.
zsh# command -v kill
kill
Sebastian Schrader
fuente
No puedo entender si enablese especifica en POSIX o no, pero si lo es, podría usar enable -n whichpara deshabilitar el shell incorporado which.
Muzer
y hayrealpath
Ipor Sircer
@Muzer En los depósitos que tengo a mi disposición, enablesolo es proporcionado por bashyzsh
Sebastian Schrader
1
Necesita un método real para el shell específico que ejecuta su script, no para todos los shells. Los scripts no son ejecutados por un shell aleatorio sino específicamente por el shell especificado en la línea shebang. Dicho esto, en bash eso sería type -p. Tanto bash como dash le permiten decir commandcomando para ejecutar un ejecutable real, incluso si hay una función o incorporado con el mismo nombre.
AlexP
1
@AlexP commandomite funciones (y alias) pero NO integra, como dice Q correctamente. Y no siempre puede usar un shebang porque no hay una ruta que obtenga un shell determinado, o incluso un shell POSIX, en todos los sistemas.
dave_thompson_085

Respuestas:

11

Solo búscalo tú mismo.

export IFS=":"
[ -z "${1}" ] && exit 1
for dir in $PATH
do if [ -x "${dir}/${1}" ]
   then echo "${dir}/${1}"
        exit 0
   fi
done
echo ${1} not found
exit 1

Probado en bash, dash, ksh, mksh,zsh

Actualizar

Lo anterior es bueno para una secuencia de comandos independiente, sin embargo, si planea incorporar esto en una secuencia de comandos más grande, es posible que desee usar algo más como lo siguiente.

function find_path() {
   IFS_SAVE="${IFS}"
   export IFS=":"
   [ -z "${1}" ] && exit 1
   for dir in $PATH
   do if [ -x "${dir}/${1}" ]
      then echo "${dir}/${1}"
           export IFS="${IFS_SAVE}"
           return 0
      fi
   done
   export IFS="${IFS_SAVE}"
   echo ${1} not found
   return 1
}

Esto es para que IFSse restaure después de encontrar la coincidencia, también se intercambia exit's por return'

Zachary Brady
fuente
1
¿Quizás -x en lugar de -f?
Jeff Schaller
@JeffSchaller buen punto, no hay razón para elegir archivos no ejecutables.
Zachary Brady
3
Si integra esto en un script de shell más grande (en lugar de convertirlo en un script por derecho propio, como se supone), es posible que desee restaurar el valor anterior de IFS más adelante; de ​​lo contrario, eso podría tener muchos efectos en el resto del guión ...
psmears
¿Por qué exportar la IFSvariable? ¿No es suficiente tener esto configurado en el shell local? Hablando de local, ¿ local IFSsería portátil? Lo anterior puede interactuar mal si algo más está guardando IFS de la misma manera. Mirando esta pregunta en SE , localpuede funcionar para la mayoría de los shells a pesar de no ser POSIX. Poner la versión original en una (…)subshell también podría funcionar.
MvG