Khi nào tôi phải sử dụng #! / Bin / bash và khi nào #! / Bin / sh?


104

Khi nào là #!/bin/bashthích hợp hơn #!/bin/shtrong một kịch bản shell?


55
Khi bạn đang sử dụng các bashhàm và cú pháp thay vì các shhàm và cú pháp.
Mokubai


3
Nếu nó giúp được bất cứ ai, tôi đã nhận thấy rằng vimsẽ làm nổi bật bashchủ nghĩa nếu tập lệnh của bạn có #!/bin/shshebang. Tôi chỉ thay đổi nó bashnếu mọi thứ đủ lông để bắt đầu cần các tính năng bash.
SeldomNeedy

@SeldomNeedy Một số điều nó làm nổi bật theo mặc định hoạt động tốt trong bất kỳ vỏ POSIX nào. $(...)đặc biệt đáng ghét. Ngoài ra, một số trong số chúng rất tinh tế [ <(...)cmd >& filekhông nhận được bất kỳ lỗi đánh dấu nào, ví dụ, chúng chỉ không làm nổi bật đặc biệt cho ý nghĩa của chúng, có hoặc không g:is_bash]
Random832

@ Random832 Có vẻ như gần đây đã có một số hoạt động về chủ đề này trong trình theo dõi vấn đề của vim .
SeldomNeedy

Câu trả lời:


150

Nói ngắn gọn:

  • Có một số shell thực hiện một superset của đặc tả sh POSIX . Trên các hệ thống khác nhau, /bin/shcó thể là một liên kết đến tro, bash, dash, ksh, zsh, & c. (Mặc dù vậy, nó sẽ luôn tương thích với sh - không bao giờ là csh hoặc cá.)

  • Miễn là bạn shchỉ sử dụng các tính năng, bạn có thể (và thậm chí có thể nên) sử dụng #!/bin/shvà tập lệnh sẽ hoạt động tốt, bất kể đó là shell nào.

  • Nếu bạn bắt đầu sử dụng các tính năng dành riêng cho bash (ví dụ: mảng), bạn nên yêu cầu bash cụ thể - bởi vì, ngay cả khi /bin/shđã gọi bash trên hệ thống của bạn , nó có thể không có trên hệ thống của mọi người khác và tập lệnh của bạn sẽ không chạy ở đó. (Tất nhiên tương tự áp dụng cho zsh và ksh.) Bạn có thể sử dụng shellcheck để xác định bashism.

  • Ngay cả khi tập lệnh chỉ dành cho sử dụng cá nhân, bạn có thể nhận thấy rằng một số HĐH thay đổi /bin/shtrong quá trình nâng cấp - ví dụ như trên Debian, nó được sử dụng để bash, nhưng sau đó đã được thay thế bằng dấu gạch ngang rất nhỏ. Các kịch bản sử dụng bashism nhưng đã #!/bin/shđột nhiên bị phá vỡ.

Tuy nhiên:

  • Thậm chí #!/bin/bashlà không chính xác. Trên các hệ thống khác nhau, bash có thể sống trong /usr/binhay /usr/pkg/binhay /usr/local/bin.

  • Một lựa chọn đáng tin cậy hơn là #!/usr/bin/env bash, sử dụng $ PATH. (Mặc dù envbản thân công cụ này cũng không được bảo đảm nghiêm ngặt, /usr/bin/envnhưng vẫn hoạt động trên nhiều hệ thống hơn /bin/bash.)


10
Câu trả lời tốt đẹp. Ý của bạn là làm cho nó CW?
Mokubai

3
Chỉ là một nhận xét nếu bash được chạy từ /bin/shđó nó sẽ cố gắng bắt chước shcác tính năng (xem trang man ), không chắc các tính năng cụ thể của bash có sẵn trong chế độ này. envcông cụ là một công cụ posix nên được tìm thấy trong hầu hết các bản phân phối, nhưng tôi không chắc vì một số có thể không tôn trọng posix.
Brice

8
Không, trên thực tế, POSIX tuyên bố cụ thể không thể giả sử là trong /bin: " Các ứng dụng cần lưu ý rằng PATH tiêu chuẩn cho vỏ không thể được coi là / bin / sh hoặc / usr / bin / sh, và nên được xác định bằng cách thẩm vấn PATH được trả về bởi getconf PATH, đảm bảo rằng tên đường dẫn được trả về là một tên đường dẫn tuyệt đối và không phải là một vỏ được tích hợp sẵn ". Cũng xem câu hỏi này và, cụ thể câu trả lời này .
terdon

12
Hmm, vậy, điều đó có nghĩa là POSIX không thực sự xác định một cách di động để có #!các kịch bản hoạt động?
grawity

4
POSIX sh không phải là Bourne: nó bắt nguồn từ ksh sớm hơn là hậu duệ trực tiếp của Bourne. Phân biệt chúng rất dễ dàng: echo foo ^ catphát ra foo ^ cattrong POSIX sh và chỉ phát ra footrong Bourne (như ^một nhân vật ống ở đó).
Charles Duffy

18

Sử dụng shebang tương ứng với shell mà bạn thực sự sử dụng để phát triển và gỡ lỗi tập lệnh của mình. Tức là nếu vỏ đăng nhập của bạn là bash, và bạn chạy tập lệnh của bạn dưới dạng thực thi trong thiết bị đầu cuối của bạn, sử dụng #!/bin/bash. Đừng chỉ cho rằng vì bạn chưa sử dụng mảng (hoặc bất kỳ bashtính năng nào bạn biết), bạn an toàn để chọn bất kỳ vỏ nào bạn thích. Có nhiều sự khác biệt tinh tế giữa các shell ( echo, hàm, vòng lặp, bạn đặt tên cho nó) mà không thể được phát hiện nếu không có thử nghiệm thích hợp.

Hãy xem xét điều này: nếu bạn rời đi #!/bin/bashvà người dùng của bạn không có nó, họ sẽ thấy một thông báo lỗi rõ ràng, đại loại như

Error: /bin/bash not found

Hầu hết người dùng có thể khắc phục điều này dưới một phút bằng cách cài đặt gói thích hợp. Mặt khác, nếu bạn thay thế shebang bằng #!/bin/shvà kiểm tra nó trên một hệ thống có /bin/shliên kết tượng trưng /bin/bash, người dùng của bạn sẽ không bashgặp rắc rối. Rất có thể họ sẽ thấy một thông báo lỗi khó hiểu như:

Error in script.sh line 123: error parsing token xyz

Điều này có thể mất nhiều giờ để khắc phục và sẽ không có manh mối nào về việc họ nên sử dụng loại vỏ nào.

Không có nhiều lý do tại sao bạn muốn sử dụng một lớp vỏ khác trong shebang. Một lý do là khi vỏ bạn đã sử dụng không phổ biến. Một cách khác là để đạt được hiệu suất shnhanh hơn đáng kể trên một số hệ thống, VÀ kịch bản của bạn sẽ là một nút cổ chai hiệu năng. Trong trường hợp đó, hãy kiểm tra kỹ kịch bản của bạn với shell đích, sau đó thay đổi shebang.


@Hastur Tôi không nhận được bình luận của bạn. Shebang chắc chắn sẽ xác định shell nào sẽ được sử dụng để chạy script, bạn có nghĩ câu trả lời của tôi ngụ ý khác không?
Dmitry Grigoryev

1
Quên đi. Tôi đã hiểu nhầm phần "tại sao bạn muốn sử dụng" ...
Hastur

6

Bạn chỉ nên sử dụng #! /bin/sh.

Bạn không nên sử dụng các tiện ích mở rộng bash (hoặc zsh hoặc fish hoặc ...) trong tập lệnh shell.

Bạn chỉ nên viết các kịch bản shell hoạt động với bất kỳ triển khai ngôn ngữ shell nào (bao gồm tất cả các chương trình "tiện ích" đi cùng với chính shell). Ngày nay, bạn có thể lấy POSIX.1-2001 ( không phải -2008) làm thẩm quyền cho những gì trình bao và tiện ích có khả năng, nhưng lưu ý rằng một ngày nào đó bạn có thể được yêu cầu chuyển tập lệnh của mình sang hệ thống cũ (ví dụ Solaris hoặc AIX) có vỏ và các tiện ích bị đóng băng vào khoảng năm 1992.

Cái gì, nghiêm túc?!

Vâng, rất nghiêm trọng.

Đây là điều: Shell là một ngôn ngữ lập trình khủng khiếp . Điều duy nhất nó có cho nó là /bin/shtrình thông dịch kịch bản một và duy nhất mà mọi cài đặt Unix được đảm bảo có.

Đây là một điều khác: một số lần lặp của trình thông dịch Perl 5 lõi ( /usr/bin/perl) có nhiều khả năng sẽ có sẵn trên bản cài đặt Unix được chọn ngẫu nhiên hơn (/(usr|opt)(/(local|sfw|pkg)?)?/bin/bashlà. Các ngôn ngữ kịch bản tốt khác (Python, Ruby, node.js, v.v. - Tôi thậm chí sẽ bao gồm PHP và Tcl trong danh mục đó khi so sánh với shell) cũng gần như có sẵn như bash và các shell mở rộng khác.

Do đó, nếu bạn có tùy chọn viết tập lệnh bash , thay vào đó , bạn có tùy chọn sử dụng ngôn ngữ lập trình không tệ.

Bây giờ, các tập lệnh shell đơn giản , loại chỉ chạy một vài chương trình theo trình tự từ công việc định kỳ hoặc một cái gì đó, không có gì sai khi để chúng làm tập lệnh shell. Nhưng các kịch bản shell đơn giản không cần mảng hoặc hàm hoặc [[thậm chí. Và bạn chỉ nên viết các kịch bản shell phức tạp khi bạn không có lựa chọn nào khác. Ví dụ, các kịch bản Autoconf là các kịch bản lệnh shell. Nhưng các tập lệnh đó phải chạy trên mọi hiện thân /bin/shcó liên quan đến chương trình được cấu hình. và điều đó có nghĩa là họ không thể sử dụng bất kỳ tiện ích mở rộng nào. Bạn có thể không cần quan tâm đến các Unix độc quyền cũ ngày nay, nhưng có lẽ bạn nên quan tâm đến các BSD nguồn mở hiện tại, một số trong đó không cài đặtbashtheo mặc định và các môi trường nhúng chỉ cung cấp cho bạn một vỏ tối thiểu và busybox.

Tóm lại, thời điểm bạn thấy mình muốn một tính năng không có sẵn trong ngôn ngữ shell di động, đó là dấu hiệu cho thấy tập lệnh đã trở nên quá phức tạp để giữ nguyên tập lệnh shell. Thay vào đó hãy viết lại bằng ngôn ngữ tốt hơn.


4
Xin lỗi nhưng điều đó hơi ngớ ngẩn. Có, nếu bạn đang viết một cái gì đó sẽ được i) phân phối và ii) cho các môi trường khác nhau, bạn nên tuân thủ cơ bản sh. Tuy nhiên, điều đó khác xa với việc bạn không bao giờ nên sử dụng các loại đạn khác. Có hàng ngàn (có thể nhiều hơn nữa) các kịch bản được viết mỗi ngày và phần lớn trong số chúng sẽ chỉ chạy trên một máy duy nhất. Lời khuyên của bạn có ý nghĩa về mã sản xuất, nhưng không phải cho các công việc "sysdaminy" hàng ngày mà hầu hết các kịch bản được viết cho. Nếu tôi biết rằng tập lệnh của tôi sẽ chỉ được sử dụng bởi tôi và trên máy Linux, bash vẫn ổn.
terdon

1
@terdon Vấn đề là nếu bạn có tùy chọn viết tập lệnh bash, thì bạn cũng có tùy chọn viết tập lệnh perl (hoặc bất cứ điều gì) và đây luôn là lựa chọn tốt hơn.
zwol

@terdon Câu trả lời chắc chắn không ngớ ngẩn, bình luận của bạn thay vào đó chỉ đơn giản là ngây thơ. Các kịch bản Shell rất tệ khi gỡ lỗi so với Perl và như vậy, người ta có thể dễ dàng sử dụng các IDE hoàn chỉnh với gỡ lỗi đồ họa và tất cả. Ngoài ra, hầu hết các tập lệnh được viết mỗi ngày cho một số điều nhỏ cần làm có xu hướng được thay đổi và thay đổi lại, phát triển, được sao chép làm ví dụ cho các đồng nghiệp hoặc mạng, v.v ... Rất có thể không có lý do nào để mạo hiểm như vậy.
Thorsten Schöning

@ ThorstenSchöning Tôi nói hơi ngớ ngẩn. Nếu đây là Unix & Linux hoặc thậm chí Stack Overflow, tôi thậm chí có thể đồng ý. Nếu chúng ta đang nói về thực tiễn tốt nhất nói chung, hoặc các lập trình viên chuyên nghiệp, tất nhiên tốt nhất là bạn nên bám vào POSIX sh cơ bản. Tuy nhiên, đây là Super User , một trang web nhắm đến người dùng cuối và nói với mọi người rằng đừng bao giờ sử dụng bash hoặc zsh khi viết kịch bản shell, theo tôi là cực kỳ.
terdon

@terdon Theo tôi, điều thực sự quan trọng hơn là không khuyến khích người dùng cuối viết các tập lệnh shell phức tạp. Các chuyên gia có mức độ kỹ năng trung bình cao hơn (do đó họ có khả năng đối phó với những cạm bẫy của kịch bản lệnh vỏ phức tạp), nên biết chính xác những môi trường nào họ cần có thể mang theo và được trả tiền không từ bỏ trong thất vọng.
zwol

4

Nói chung nếu thời gian quan trọng hơn chức năng, bạn sẽ sử dụng shell nhanh hơn. sh thường được đặt bí danh cho dấu gạch ngang và có xu hướng được sử dụng cho các tác vụ cron gốc hoặc các hoạt động theo lô trong đó mỗi giây (nano) tính.


2
Tải một trình thông dịch shell bổ sung từ hệ thống tập tin có thể phủ nhận lợi thế đó và bất cứ điều gì trong đó nano giây (có cùng độ lớn với chu kỳ máy thực tế) là miền của C và các lập trình viên ngôn ngữ lắp ráp.
rackandboneman

Nanoseconds chỉ tạo ra sự khác biệt trong công việc định kỳ nếu bạn có hàng tỷ người trong số họ và cron sẽ không thể xử lý nhiều công việc như vậy.
Dmitry Grigoryev

1
Ngay cả khi mckenzm phóng đại một chút, không thể phủ nhận rằng có nhiều hiệu suất hơn là tốt hơn! Đừng gác máy ở phần "nano". (Nanoseconds thậm chí không được tính bằng C trừ khi đó là vòng lặp bên trong trên hệ thống thời gian thực hoặc tương tự!)
jpaugh

1
@jpaugh Trừ khi bạn làm việc với một hệ thống (di sản) giả định rằng các hoạt động nhất định sẽ mất ít nhất một khoảng thời gian cụ thể. Nó xảy ra!
JAB

3

Để ngắn gọn hơn nữa, hãy sử dụng shnếu tính di động trên hầu hết các hệ thống là quan trọng nhất và bashnếu bạn muốn sử dụng một số tính năng cụ thể của nó, như mảng, nếu phiên bản bash hỗ trợ.


1
  • sh (đối với hầu hết các trường hợp), bash nếu được yêu cầu cụ thể (hoặc ksh, hoặc bất cứ điều gì)

Con đường an toàn nhất. khi được mã hóa cứng, sẽ là / usr / bin / shellOfChoicenhưng một quy ước mới mà tôi đang cố gắng sử dụng luôn ngay bây giờ - vì 'vị trí mặc định' thông qua thay đổi PATH có thể thay đổi là:

#! / usr / bin / env sh hoặc
#! / usr / bin / env bash
hoặc ví dụ: các tập lệnh perl
#! / usr / bin / env perl -w

Tất nhiên, khi bạn có lý do để tập lệnh KHÔNG BAO GIỜ được tự động lấy PATH mới thì tiếp tục mã hóa nó - và sau đó / usr / bin / thứ gì đó nên là con đường có khả năng sử dụng nhất.

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.