Bagaimana cara andal menemukan jalur lengkap program di PATH?

12

Saya perlu menemukan jalur program yang diberikan pada PATHmenggunakan skrip shell. Path harus path lengkap aktual dari program, yang dapat diteruskan kemudian ke salah satu exec*fungsi, yang tidak mencari PATHsendiri, misalnya execv.

Ada beberapa program seperti kill, yang tersedia sebagai program aktual dan shell built-in pada saat bersamaan. Jika ini kasusnya, saya perlu path lengkap ke program yang sebenarnya.

Ada beberapa utilitas yang dapat menemukan program pada PATHsebagaimana ditentukan dalam Bagian 2.9.1.1, Pencarian Perintah dan Eksekusi standar POSIX .

Ada which, yang bukan bagian dari standar apa pun. Ini bisa menjadi program reguler pada beberapa sistem, sedangkan beberapa shell memberikannya builtin. Tampaknya tersedia di sebagian besar sistem dan shell, tetapi shell dengan versi builtin, juga hanya mengembalikan nama built-in alih-alih jalur ke executable. Juga tidak dibakukan dengan cara apa pun dan dapat mengembalikan output apa pun dan mengambil opsi yang berbeda.

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

Ada whence, yang merupakan built-in dari beberapa cangkang. Tetapi tidak tersedia pada banyak kerang. Itu juga akan mengembalikan nama built-in bukan path ke program. A -pdapat diteruskan ke mana untuk mengubah perilaku ini.

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

Ada commandbuiltin yang ditentukan oleh POSIX: 2008 . Sayangnya itu juga mencari perintah reguler dan built-in dan akan mengembalikan nama built-in bukan path ke program yang dibayangi oleh built-in dengan nama yang sama. Beberapa shell lama belum mengimplementasikannya.

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
sumber
Saya tidak tahu apakah enabledispesifikasikan dalam POSIX atau tidak, tetapi jika ya, Anda dapat menggunakan enable -n whichuntuk menonaktifkan shell bawaan untuk which.
Muzer
dan adarealpath
Ipor Sircer
@Muzer Pada cangkang yang saya miliki, enablehanya disediakan oleh bashdanzsh
Sebastian Schrader
1
Anda memerlukan metode yang dapat dipercaya untuk shell tertentu yang menjalankan skrip Anda bukan untuk semua shell. Script tidak dieksekusi oleh shell acak tetapi secara khusus oleh shell yang ditentukan pada baris shebang. Yang sedang berkata, dalam bash itu akan menjadi type -p. Baik bash dan dash membiarkan Anda mengatakan commandperintah untuk menjalankan executable yang sebenarnya bahkan jika ada fungsi atau builtin dengan nama yang sama.
AlexP
1
@AlexP commandmelewatkan fungsi (dan alias) tetapi BUKAN bawaan, seperti yang dikatakan Q dengan benar. Dan Anda tidak dapat selalu menggunakan shebang karena tidak ada jalur yang mendapatkan shell yang diberikan, atau bahkan beberapa shell POSIX, di semua sistem.
dave_thompson_085

Jawaban:

11

Cukup cari sendiri.

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

Diuji di bash, dash, ksh, mksh,zsh

Memperbarui

Di atas bagus untuk skrip yang berdiri sendiri namun jika Anda berencana menyematkan ini ke skrip yang lebih besar, Anda mungkin ingin menggunakan sesuatu yang lebih seperti berikut ini.

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
}

Hal ini dimaksudkan agar IFSdipulihkan setelah menemukan pertandingan, juga bertukar exit's dengan return' s

Zachary Brady
sumber
1
Mungkin -x bukan -f?
Jeff Schaller
@JeffSchaller poin bagus, tidak ada alasan untuk memilih file yang tidak dapat dieksekusi.
Zachary Brady
3
Jika Anda mengintegrasikan ini ke dalam skrip shell yang lebih besar (bukan membuatnya menjadi skrip sendiri, seperti yang mungkin dimaksudkan), Anda mungkin ingin mengembalikan nilai lama IFS sesudahnya - jika tidak, ini dapat memiliki banyak efek pada sisa skrip ...
psmears
Mengapa mengekspor IFSvariabel? Tidakkah cukup dengan set ini di shell lokal? Berbicara tentang lokal, apakah local IFSakan portabel? Hal di atas dapat berinteraksi dengan buruk jika sesuatu yang lain menyimpan IFS dengan cara yang sama. Melihat pertanyaan ini di SE , localdapat bekerja untuk sebagian besar shell meskipun tidak POSIX. Memasukkan versi asli ke dalam (…)subkulit mungkin juga berfungsi.
MvG