Nếu các kịch bản shell bắt đầu bằng #!/bin/bash
, chúng sẽ luôn chạy với bash
từ /bin
. Tuy nhiên, nếu họ bắt đầu với #!/usr/bin/env bash
, họ sẽ tìm kiếm bash
trong$PATH
và sau đó bắt đầu với cái đầu tiên họ có thể tìm thấy.
Tại sao điều này sẽ hữu ích? Giả sử bạn muốn chạy bash
các tập lệnh, yêu cầu bash 4.x hoặc mới hơn, nhưng hệ thống của bạn chỉ cóbash
cài đặt 3.x và hiện tại bản phân phối của bạn không cung cấp phiên bản mới hơn hoặc bạn không phải là quản trị viên và không thể thay đổi những gì được cài đặt trên hệ thống đó .
Tất nhiên, bạn có thể tải xuống mã nguồn bash và xây dựng bash của riêng bạn từ đầu, đặt nó vào ~/bin
ví dụ. Và bạn cũng có thể sửa đổi $PATH
biến của mình trong .bash_profile
tệp để đưa ~/bin
vào làm mục nhập đầu tiên ( PATH=$HOME/bin:$PATH
như ~
sẽ không mở rộng trong $PATH
). Nếu bây giờ bạn gọi bash
, vỏ đầu tiên sẽ tìm nó $PATH
theo thứ tự, vì vậy nó bắt đầu với ~/bin
, nơi nó sẽ tìm thấy của bạn bash
. Điều tương tự cũng xảy ra nếu các tập lệnh tìm kiếm để bash
sử dụng #!/usr/bin/env bash
, vì vậy các tập lệnh này sẽ hoạt động trên hệ thống của bạn bằng cách sử dụng bản bash
dựng tùy chỉnh của bạn .
Một nhược điểm là, điều này có thể dẫn đến hành vi không mong muốn, ví dụ: cùng một tập lệnh trên cùng một máy có thể chạy với các trình thông dịch khác nhau cho các môi trường khác nhau hoặc người dùng có đường dẫn tìm kiếm khác nhau, gây ra tất cả các loại đau đầu.
Nhược điểm lớn nhất env
là một số hệ thống sẽ chỉ cho phép một đối số, vì vậy bạn không thể thực hiện điều này #!/usr/bin/env <interpreter> <arg>
, vì các hệ thống sẽ xem <interpreter> <arg>
như một đối số (chúng sẽ coi nó như thể biểu thức được trích dẫn) và do đó env
sẽ tìm kiếm một trình thông dịch có tên <interpreter> <arg>
. Lưu ý rằng đây không phải là vấn đề của env
chính lệnh, nó luôn cho phép nhiều tham số được truyền qua nhưng với trình phân tích cú pháp shebang của hệ thống phân tích cú pháp dòng này trước khi gọi env
. Trong khi đó, điều này đã được sửa trên hầu hết các hệ thống nhưng nếu tập lệnh của bạn muốn siêu di động, bạn không thể tin rằng điều này đã được sửa trên hệ thống mà bạn sẽ chạy.
Nó thậm chí có thể có ý nghĩa bảo mật, ví dụ nếu sudo
không được cấu hình để làm sạch môi trường hoặc $PATH
bị loại trừ khỏi việc dọn dẹp. Hãy để tôi chứng minh điều này:
Thường /bin
là một nơi được bảo vệ tốt, chỉ root
có thể thay đổi bất cứ điều gì ở đó. Tuy nhiên, thư mục nhà của bạn không phải là bất kỳ chương trình nào bạn chạy đều có thể thay đổi nó. Điều đó có nghĩa là mã độc có thể đặt giả bash
vào một thư mục ẩn, sửa đổi thư mục của bạn .bash_profile
để đưa thư mục đó vào $PATH
, vì vậy tất cả các tập lệnh sử dụng #!/usr/bin/env bash
sẽ chạy với giả mạo đó bash
. Nếu sudo
giữ $PATH
, bạn đang gặp rắc rối lớn.
Ví dụ, hãy xem xét một công cụ tạo một tệp ~/.evil/bash
có nội dung sau:
#!/bin/bash
if [ $EUID -eq 0 ]; then
echo "All your base are belong to us..."
# We are root - do whatever you want to do
fi
/bin/bash "$@"
Hãy tạo một kịch bản đơn giản sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Bằng chứng về khái niệm (trên một hệ thống sudo
lưu giữ $PATH
):
$ ./sample.sh
Hello World
$ sudo ./sample.sh
Hello World
$ export PATH="$HOME/.evil:$PATH"
$ ./sample.sh
Hello World
$ sudo ./sample.sh
All your base are belong to us...
Hello World
Thông thường, tất cả các vỏ cổ điển đều phải được đặt /bin
và nếu bạn không muốn đặt chúng ở đó vì bất kỳ lý do gì, thực sự không có vấn đề gì khi đặt một liên kết tượng trưng vào /bin
các vị trí thực của chúng (hoặc có thể /bin
chính nó là một liên kết tượng trưng), vì vậy Tôi sẽ luôn luôn đi với #!/bin/sh
và #!/bin/bash
. Có quá nhiều thứ sẽ phá vỡ nếu những thứ này không còn hoạt động nữa. Không phải POSIX sẽ yêu cầu vị trí này (POSIX không chuẩn hóa tên đường dẫn và do đó nó thậm chí không chuẩn hóa tính năng shebang) nhưng chúng rất phổ biến, ngay cả khi một hệ thống không cung cấp /bin/sh
, nó vẫn có thể hiểu #!/bin/sh
và biết phải làm gì với nó và có thể nó chỉ tương thích với mã hiện có.
Nhưng đối với các trình thông dịch tùy chọn hiện đại hơn, không chuẩn, như Perl, PHP, Python hoặc Ruby, thì nó không thực sự được chỉ định ở bất kỳ nơi nào chúng nên được đặt. Họ có thể ở /usr/bin
nhưng họ cũng có thể nằm trong /usr/local/bin
hoặc trong một chi nhánh phân cấp hoàn toàn khác nhau ( /opt/...
, /Applications/...
, vv). Đó là lý do tại sao chúng thường sử dụng #!/usr/bin/env xxx
cú pháp shebang.