Làm thế nào để tôi đáng tin cậy tìm thấy đường dẫn đầy đủ của một chương trình trên PATH?


12

Tôi cần tìm đường dẫn của một chương trình đã cho PATHbằng cách sử dụng tập lệnh shell. Đường dẫn phải là đường dẫn đầy đủ thực tế của chương trình, có thể được chuyển sau này đến một trong các exec*hàm, không tìm kiếm PATHchính nó, ví dụ execv.

Có những chương trình như kill, có sẵn như là một chương trình thực tế và một vỏ được tích hợp cùng một lúc. Nếu đây là trường hợp, tôi cần đường dẫn đầy đủ đến chương trình thực tế.

Có một số tiện ích có thể tìm thấy một chương trình trên PATHnhư được chỉ định trong Mục 2.9.1.1, Tìm kiếm lệnh và Thực thi tiêu chuẩn POSIX .

which, đó không phải là một phần của bất kỳ tiêu chuẩn. Nó có thể là một chương trình thông thường trên một số hệ thống, trong khi một số shell cung cấp cho nó là một chương trình dựng sẵn. Nó dường như có sẵn trên hầu hết các hệ thống và hệ vỏ, nhưng các shell với phiên bản dựng sẵn, cũng chỉ trả về tên của phần dựng sẵn thay vì đường dẫn đến tệp thực thi. Ngoài ra, nó không được tiêu chuẩn hóa theo bất kỳ cách nào và có thể trả về bất kỳ đầu ra nào và thực hiện các tùy chọn khác nhau.

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

whence, đó là một tích hợp của một vài vỏ. Nhưng không có sẵn trên nhiều vỏ. Nó cũng sẽ trả về tên của phần dựng sẵn thay vì đường dẫn đến chương trình. A -pcó thể được thông qua để thay đổi hành vi này.

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

commandnội dung được chỉ định bởi POSIX: 2008 . Thật không may, nó cũng tìm kiếm các lệnh thông thường và tích hợp sẵn và sẽ trả về tên của phần dựng sẵn thay vì đường dẫn đến chương trình được tạo bóng bởi một phần tử cùng tên. Một số vỏ cũ chưa thực hiện được.

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

Tôi không thể biết liệu enablecó được chỉ định trong POSIX hay không, nhưng nếu có, bạn có thể sử dụng enable -n whichđể vô hiệu hóa trình vỏ được tích hợp sẵn which.
Muzer

và córealpath
Ipor Sircer

@Muzer Trên vỏ tôi có theo ý của tôi, enablechỉ được cung cấp bởi bashzsh
Sebastian Schrader

1
Bạn cần một phương thức khả thi cho trình bao cụ thể đang chạy tập lệnh của bạn không phải cho tất cả các trình bao. Các tập lệnh không được thực thi bởi trình bao ngẫu nhiên mà cụ thể bằng trình bao được chỉ định trong dòng shebang. Điều đó đang được nói, trong bash đó sẽ được type -p. Cả bash và dash đều cho phép bạn nói commandlệnh để chạy một tệp thực thi thực tế ngay cả khi có một hàm hoặc hàm dựng sẵn có cùng tên.
AlexP

1
@AlexP commandbỏ qua các hàm (và bí danh) nhưng KHÔNG được dựng, như Q nói chính xác. Và bạn không thể luôn sử dụng shebang bởi vì không có đường dẫn nào nhận được bất kỳ shell nào, hoặc thậm chí một số vỏ POSIX, trên tất cả các hệ thống.
dave_thndry_085

Câu trả lời:


11

Chỉ cần tìm kiếm nó cho mình.

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

Thử nghiệm trên bash, dash, ksh, mksh,zsh

Cập nhật

Ở trên là tốt cho một tập lệnh độc lập tuy nhiên nếu bạn dự định nhúng tập lệnh này vào tập lệnh lớn hơn, bạn có thể muốn sử dụng một cái gì đó giống như sau.

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
}

Điều này là để IFSđược khôi phục sau khi tìm thấy kết quả khớp, cũng được hoán đổi exitvới return's


1
Có lẽ -x thay vì -f?
Jeff Schaller

@JeffSchaller điểm tốt, không có lý do để chọn ra các tệp không thể thực thi.
Zachary Brady

3
Nếu bạn tích hợp tập lệnh này vào tập lệnh shell lớn hơn (trái với việc biến nó thành tập lệnh theo cách riêng của nó, như dự định), bạn có thể muốn khôi phục giá trị cũ của IFS sau đó - nếu không điều đó có thể có nhiều hiệu ứng trên phần còn lại của kịch bản ...
psmears

Tại sao phải xuất IFSbiến? Nó không đủ để có bộ này trong vỏ cục bộ? Nói về địa phương, sẽ local IFSđược xách tay? Ở trên có thể tương tác kém nếu một cái gì đó khác đang lưu IFS theo cùng một cách. Nhìn vào câu hỏi này trên SE , localcó thể hoạt động cho hầu hết các shell mặc dù không phải là POSIX. Đặt phiên bản gốc trong một (…)subshell cũng có thể hoạt động.
MvG
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.