Sự khác biệt giữa những thứ #! / Usr / bin / env bash và # # / usr / bin / bash, là gì?


372

Trong tiêu đề của tập lệnh Bash, sự khác biệt giữa hai câu lệnh đó là gì:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Khi tôi tham khảo env trang người đàn ông , tôi nhận được định nghĩa này:

 env - run a program in a modified environment

Nó có nghĩa là gì?



6
Ai có thể cho tôi biết tại sao câu hỏi này bị đóng, "liên quan đến lập trình hoặc phát triển phần mềm" không?
tarrsalah

1
Tôi đồng ý rằng nó không lạc đề, nhưng có lẽ nó là một bản sao của một số câu hỏi khác như câu hỏi này .
Keith Thompson

16
Câu hỏi này không nên được đánh dấu là lạc đề. Nó chỉ cần 5 người với số điểm trên 3000 để đánh dấu là "theo chủ đề" và nó có thể được mở lại. Đó là một câu hỏi - cụ thể là về lập trình.
Danijel-James W

3
Tôi bị sốc. Sốc khi thấy rằng tài liệu Linux đầy rẫy các tautology. xkcd.com/703 git-man-page-generator.lokaltog.net
mã allyour

Câu trả lời:


323

Chạy một lệnh thông qua /usr/bin/envcó lợi ích là tìm kiếm bất cứ phiên bản mặc định nào của chương trình trong env hiện tại của bạn mỉa mai .

Bằng cách này, bạn không phải tìm kiếm nó ở một nơi cụ thể trên hệ thống, vì những đường dẫn đó có thể ở các vị trí khác nhau trên các hệ thống khác nhau. Miễn là nó đi theo con đường của bạn, nó sẽ tìm thấy nó.

Một nhược điểm là bạn sẽ không thể vượt qua nhiều đối số (ví dụ: bạn sẽ không thể viết /usr/bin/env awk -f ) nếu bạn muốn hỗ trợ Linux, vì POSIX mơ hồ về cách diễn giải dòng này và Linux diễn giải mọi thứ sau lần đầu tiên không gian để biểu thị một đối số duy nhất. Bạn có thể sử dụng /usr/bin/env -Strên một số phiên bản củaenv để khắc phục điều này, nhưng sau đó tập lệnh sẽ trở nên ít di động hơn và phá vỡ trên các hệ thống khá gần đây (ví dụ như Ubuntu 16.04 nếu không muộn hơn).

Một nhược điểm khác là vì bạn không gọi một chương trình thực thi rõ ràng, nên nó có khả năng xảy ra lỗi và trên các vấn đề bảo mật của hệ thống nhiều người dùng (nếu ai đó quản lý để thực hiện cuộc gọi thực thi của họ bash ví dụ trong đường dẫn của bạn).

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

Trong một số tình huống, lần đầu tiên có thể được ưu tiên (như chạy các kịch bản python với nhiều phiên bản python, mà không phải làm lại dòng thực thi). Nhưng trong các tình huống mà bảo mật là trọng tâm, thì cái sau sẽ được ưu tiên hơn, vì nó hạn chế khả năng tiêm mã.


22
Một nhược điểm khác là bạn không thể chuyển một đối số bổ sung cho trình thông dịch.
Keith Thompson

1
@KeithThndry: Thông tin không chính xác .. Bạn có thể chuyển các tùy chọn cho trình thông dịch cơ bản bằng cách sử dụng / usr / bin / env!
Gaurav Agarwal

4
@GauravAgarwal: Không có trong hệ thống của tôi. Một tập lệnh chỉ chứa dòng này: #!/usr/bin/env echo Hellophàn nàn : /usr/bin/env: echo Hello: No such file or directory. Rõ ràng nó coi echo Hellonhư là một đối số duy nhất /usr/bin/env.
Keith Thompson

1
@ AndréLaszlo: envLệnh chắc chắn cho phép các đối số được truyền cho lệnh. Vấn đề là ngữ nghĩa của #!dòng và điều đó phụ thuộc vào kernel. Các nhân Linux gần đây không cho phép những thứ như #!/usr/bin/env command args, nhưng các nhân Linux cũ hơn và các hệ thống khác thì không.
Keith Thompson

1
Tại sao có backticks trong khối mã? Không nên xóa chúng?
Benjamin W.

64

Việc sử dụng #!/usr/bin/env NAMElàm cho tìm kiếm shell cho trận đấu đầu tiên của NAME trong biến môi trường $ PATH. Nó có thể hữu ích nếu bạn không biết đường dẫn tuyệt đối hoặc không muốn tìm kiếm nó.


9
Ít nhất bạn phải biết env ở đâu :).
devrimbaris 22/03/2016

1
Câu trả lời tuyệt vời. Giải thích ngắn gọn những gì env shebang làm, thay vì nói "chọn chương trình dựa trên cấu hình hệ thống của bạn"
De Novo

13

Thay vì xác định rõ ràng đường dẫn đến trình thông dịch như trong /usr/bin/bash/, bằng cách sử dụng lệnh env, trình thông dịch được tìm kiếm và khởi chạy từ bất cứ nơi nào nó được tìm thấy lần đầu tiên. Điều này có cả những mặt thăng trầm


Trên hầu hết các hệ thống, chúng sẽ có chức năng giống nhau, nhưng nó phụ thuộc vào vị trí thực thi bash và env của bạn. Không chắc chắn làm thế nào điều này sẽ ảnh hưởng đến các biến môi trường, mặc dù.
safay

2
"Có thể chỉ định trình thông dịch mà không sử dụng env, bằng cách cung cấp đường dẫn đầy đủ cho trình thông dịch. Một vấn đề là trên các hệ thống máy tính khác nhau, đường dẫn chính xác có thể khác nhau. Thay vào đó, sử dụng env, trình thông dịch được tìm kiếm và đặt tại thời gian tập lệnh được chạy. Điều này làm cho tập lệnh dễ di chuyển hơn, nhưng cũng làm tăng nguy cơ trình thông dịch sai được chọn vì nó tìm kiếm một kết quả khớp trong mọi thư mục trên đường dẫn tìm kiếm thực thi. Nó cũng gặp vấn đề tương tự đường dẫn đến nhị phân env cũng có thể khác nhau trên cơ sở mỗi máy. "- Wikipedia
Mike Clark

10

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 bashtừ /bin. Tuy nhiên, nếu họ bắt đầu với #!/usr/bin/env bash, họ sẽ tìm kiếm bashtrong$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 bashcá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 ~/binví dụ. Và bạn cũng có thể sửa đổi $PATHbiến của mình trong .bash_profiletệp để đưa ~/binvào làm mục nhập đầu tiên ( PATH=$HOME/bin:$PATHnhư ~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ó $PATHtheo 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 để bashsử 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 bashdự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 envlà 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 đó envsẽ 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 envchí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 sudokhông được cấu hình để làm sạch môi trường hoặc $PATHbị loại trừ khỏi việc dọn dẹp. Hãy để tôi chứng minh điều này:

Thường /binlà một nơi được bảo vệ tốt, chỉ rootcó 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ả bashvà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 bashsẽ chạy với giả mạo đó bash. Nếu sudogiữ $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/bashcó 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 sudolư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 /binvà 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 /bincác vị trí thực của chúng (hoặc có thể /binchí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#!/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/shvà 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/binnhưng họ cũng có thể nằm trong /usr/local/binhoặ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 xxxcú pháp shebang.


4

Tôi thấy nó hữu ích, bởi vì khi tôi không biết về env, trước khi tôi bắt đầu viết kịch bản, tôi đã làm điều này:

type nodejs > scriptname.js #or any other environment

và sau đó tôi đã sửa đổi dòng đó trong tệp thành shebang.
Tôi đã làm điều này, bởi vì tôi không luôn nhớ nút node trên máy tính của mình ở đâu - / usr / bin / hoặc / bin /, vì vậy đối với tôi envrất hữu ích. Có thể có chi tiết với điều này, nhưng đây là lý do của tôi

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.