Come posso trovare in modo affidabile il percorso completo di un programma sul PERCORSO?


12

Ho bisogno di trovare il percorso di un determinato programma PATHsull'uso di uno script di shell. Il percorso deve essere il percorso completo effettivo del programma, che può essere passato in seguito a una delle exec*funzioni, che non ricerca PATHse stesso, ad es execv.

Ci sono programmi come kill, che sono disponibili come un vero programma e una shell integrata allo stesso tempo. In questo caso, ho bisogno del percorso completo per il programma effettivo.

Esistono diverse utilità che possono trovare un programma PATHcome specificato nella Sezione 2.9.1.1, Ricerca comandi ed esecuzione dello standard POSIX .

C'è which, che non fa parte di nessuno standard. Può essere un programma regolare su alcuni sistemi, mentre alcune shell forniscono che è incorporato. Sembra essere disponibile sulla maggior parte dei sistemi e delle shell, ma le shell con una versione integrata restituiscono semplicemente il nome del file incorporato anziché il percorso dell'eseguibile. Inoltre, non è standardizzato in alcun modo e può restituire qualsiasi output e prendere diverse opzioni.

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

C'è whence, che è incorporato in alcune shell. Ma non disponibile su molte shell. Restituirà anche il nome del built-in invece del percorso del programma. A -ppuò essere passato a dove modificare questo comportamento.

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

C'è il commandbuiltin specificato da POSIX: 2008 . Sfortunatamente cerca anche comandi e built-in regolari e restituirà il nome del built-in invece del percorso del programma ombreggiato da un built-in con lo stesso nome. Alcune vecchie shell non l'hanno ancora implementato.

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

Non riesco a capire se enableè specificato in POSIX o meno, ma se lo è, è possibile utilizzare enable -n whichper disabilitare la shell integrata which.
Muzer,

e c'èrealpath
Ipor Sircer il

@Muzer Sulle conchiglie che ho a disposizione, enableè fornito solo da bashezsh
Sebastian Schrader,

1
È necessario un metodo affidabile per la shell specifica che esegue lo script non per tutte le shell. Gli script non vengono eseguiti da una shell casuale ma specificamente dalla shell specificata nella riga shebang. Detto questo, in bash sarebbe type -p. Sia bash che dash consentono di pronunciare il commandcomando per eseguire un eseguibile effettivo anche se esiste una funzione o un builtin con lo stesso nome.
AlexP,

1
@AlexP commandsalta le funzioni (e gli alias) ma NON i builtin, come dice correttamente la Q. E non puoi sempre usare uno shebang perché non esiste un percorso che ottenga una determinata shell, o persino una shell POSIX, su tutti i sistemi.
dave_thompson_085,

Risposte:


11

Cerca da solo.

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

Testato in bash, dash, ksh, mksh,zsh

Aggiornare

Quanto sopra è utile per uno script autonomo, tuttavia se stai pensando di incorporarlo in uno script più grande, potresti voler usare qualcosa di più simile al seguente.

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
}

Questo è così che IFSviene ripristinato dopo aver trovato la partita, anche scambiato exitcon return"s"


1
Forse -x invece di -f?
Jeff Schaller

@JeffSchaller buon punto, nessun motivo per scegliere i file non eseguibili.
Zachary Brady,

3
Se lo integri in uno script di shell più grande (invece di trasformarlo in uno script a sé stante, come presumibilmente inteso), potresti voler ripristinare il vecchio valore di IFS in seguito, altrimenti ciò potrebbe avere molti effetti su il resto della sceneggiatura ...
psmears,

Perché esportare la IFSvariabile? Non è sufficiente avere questo set nella shell locale? Parlando di locale, sarebbe local IFSportatile? Quanto sopra può interagire male se qualcos'altro sta salvando IFS allo stesso modo. Guardando questa domanda su SE , localpuò funzionare per la maggior parte delle shell nonostante non sia POSIX. Anche mettere la versione originale in una (…)subshell potrebbe funzionare.
MvG,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.