tiêu đề shell-script (#! / bin / sh so với #! / bin / csh)


92

Tại sao tất cả các tệp script đều bắt đầu bằng

#!/bin/sh

Hoặc với

#!/bin/csh

Có bắt buộc không? Mục đích của việc này là gì? Và sự khác biệt giữa hai là gì?


1
Đối với một script csh, bạn nên sử dụng #!/bin/csh -f; lệnh -fcho trình bao không lấy nguồn của người dùng .login.cshrc, điều này làm cho tập lệnh chạy nhanh hơn và tránh phụ thuộc vào thiết lập của người dùng. (Hoặc tốt hơn, không viết script csh.) Không sử dụng -fcho các script sh hoặc bash; nó không có cùng ý nghĩa.
Keith Thompson

Câu trả lời:


99

Điều này được gọi là Shebang:

http://en.wikipedia.org/wiki/Shebang_(Unix)

#! thông dịch viên [tùy chọn-arg]

Một shebang chỉ có liên quan khi một tập lệnh có quyền thực thi (ví dụ: chmod u + x script.sh).

Khi một trình bao thực thi tập lệnh, nó sẽ sử dụng trình thông dịch được chỉ định.

Thí dụ:

#!/bin/bash
# file: foo.sh
echo 1

$ chmod u+x foo.sh
$ ./foo.sh
  1

@Kolob Canyon bạn không cần, nhưng nó có thể giúp một số biên tập viên với cú pháp nổi bật (mặc dù có những cách thường khác để đạt được điều tương tự): unix.stackexchange.com/a/88730/193985
Braham Snyder

42

Các #!dòng nói với hạt nhân (đặc biệt là việc thực hiện các execvecuộc gọi hệ thống) rằng chương trình này được viết bằng một ngôn ngữ giải thích; tên đường dẫn tuyệt đối theo sau xác định trình thông dịch. Các chương trình được biên dịch sang mã máy bắt đầu bằng một chuỗi byte khác - trên hầu hết các Unix hiện đại, 7f 45 4c 46( ^?ELF) xác định chúng như vậy.

Bạn có thể đặt một đường dẫn tuyệt đối đến bất kỳ chương trình nào bạn muốn sau đó #!, miễn là chương trình đó không phải là #!tập lệnh. Kernel viết lại một lời gọi

./script arg1 arg2 arg3 ...

nơi ./scriptbắt đầu, giả sử #! /usr/bin/perl, như thể dòng lệnh thực sự

/usr/bin/perl ./script arg1 arg2 arg3

Hoặc, như bạn đã thấy, bạn có thể sử dụng #! /bin/shđể viết một kịch bản nhằm mục đích diễn giải sh.

Các #!dòng được chỉ xử lý nếu bạn trực tiếp gọi kịch bản ( ./scripttrên dòng lệnh); tệp cũng phải là tệp thực thi ( chmod +x script). Nếu bạn thực hiện sh ./script, #!dòng này là không cần thiết (và sẽ bị bỏ qua nếu có) và tệp không cần phải thực thi. Các điểm của tính năng này là để cho phép bạn trực tiếp gọi các chương trình giải thích ngôn ngữ mà không cần phải biết những gì ngôn ngữ mà họ được viết bằng. (Đỗ grep '^#!' /usr/bin/*- bạn sẽ khám phá ra rằng một lớn nhiều chương trình cổ phiếu này trong thực tế sử dụng tính năng này.)

Dưới đây là một số quy tắc để sử dụng tính năng này:

  • Các #!phải là hai đầu tiên byte trong file. Đặc biệt, tệp phải ở dạng mã hóa tương thích ASCII (ví dụ: UTF-8 sẽ hoạt động, nhưng UTF-16 thì không) và không được bắt đầu bằng "dấu thứ tự byte", nếu không hạt nhân sẽ không nhận ra nó là #!kịch bản.
  • Đường dẫn sau #!phải là đường dẫn tuyệt đối (bắt đầu bằng /). Nó không được chứa các ký tự khoảng trắng, tab hoặc dòng mới.
  • Đó là một phong cách tốt, nhưng không bắt buộc, để đặt một khoảng cách giữa dấu #!/. Không đặt nhiều hơn một khoảng trống ở đó.
  • Bạn không thể đặt các biến shell trên #!dòng, chúng sẽ không được mở rộng.
  • Bạn có thể đặt một đối số dòng lệnh sau đường dẫn tuyệt đối, ngăn cách với nó bằng một khoảng trắng. Giống như đường dẫn tuyệt đối, đối số này không được chứa các ký tự dấu cách, tab hoặc dòng mới. Đôi khi điều này là cần thiết để mọi thứ hoạt động ( #! /usr/bin/awk -f), đôi khi nó chỉ hữu ích ( #! /usr/bin/perl -Tw). Thật không may, bạn không thể đặt hai hoặc nhiều đối số sau đường dẫn tuyệt đối.
  • Một số người sẽ nói với bạn để sử dụng #! /usr/bin/env interpreterthay vì #! /absolute/path/to/interpreter. Điều này hầu như luôn luôn là một sai lầm. Nó làm cho hành vi của chương trình của bạn phụ thuộc vào $PATHbiến của người dùng gọi script. Và không phải tất cả các hệ thống đều có envngay từ đầu.
  • Các chương trình cần setuidhoặc setgidđặc quyền không thể sử dụng #!; chúng phải được biên dịch thành mã máy. (Nếu bạn không biết setuidlà gì , đừng lo lắng về điều này.)

Về vấn đề csh, nó liên quan shgần giống như sản phẩm thay thế trà nâng cao Nutrimat đối với trà. Nó có (hoặc đúng hơn là có; các triển khai hiện đại của shđã bắt kịp) một số lợi thế hơn shcho việc sử dụng tương tác, nhưng việc sử dụng nó (hoặc hậu duệ của nó tcsh) để viết kịch bản hầu như luôn là một sai lầm . Nếu bạn chưa quen với shell script nói chung, tôi thực sự khuyên bạn nên bỏ qua nó và tập trung vào sh. Nếu bạn đang sử dụng cshtương đối làm trình bao đăng nhập của mình, hãy chuyển sang bashhoặc zsh, để ngôn ngữ lệnh tương tác sẽ giống với ngôn ngữ kịch bản mà bạn đang học.


Các phiên bản gần đây của Linux cho phép trình thông dịch được chỉ định là một tập lệnh. Thực tế phổ biến là bỏ qua khoảng trắng sau dấu #!; không có bình luận về việc đó là phong cách tốt. Xem câu hỏi nàycâu trả lời của tôi để thảo luận về ưu và nhược điểm của bản #!/usr/bin/envhack.
Keith Thompson

@KeithThompson Tôi có ấn tượng rằng Linux là biến thể Unix phổ biến duy nhất cho phép trình thông dịch là một tập lệnh, vì vậy nó vẫn không phải là thứ để dựa vào. Kể từ khi tôi viết điều này, bản thân tôi đã gặp phải một tình huống đâu #!/usr/bin/envlà Điều Đúng, nhưng tôi vẫn cho rằng đó hầu như luôn là một ý kiến ​​tồi.
zwol

4

Điều này xác định shell (trình thông dịch lệnh) bạn đang sử dụng để thông dịch / chạy tập lệnh của mình. Mỗi shell hơi khác nhau về cách nó tương tác với người dùng và thực thi các script (chương trình).

Khi bạn nhập một lệnh tại dấu nhắc Unix, bạn đang tương tác với trình bao.

Ví dụ: #!/bin/cshđề cập đến C-shell, /bin/tcsht-shell, /bin/bashbash shell, v.v.

Bạn có thể biết mình đang sử dụng trình bao tương tác nào

 echo $SHELL

lệnh, hoặc cách khác

 env | grep -i shell

Bạn có thể thay đổi trình bao lệnh của mình bằng chshlệnh.

Mỗi loại có một bộ lệnh hơi khác nhau và cách gán các biến cũng như bộ cấu trúc lập trình riêng của nó. Ví dụ: câu lệnh if-else với bash trông khác với câu lệnh trong C-shell.

Trang này có thể được quan tâm vì nó "dịch" giữa cú pháp / lệnh bash và tcsh.

Sử dụng chỉ thị trong tập lệnh shell cho phép bạn chạy các chương trình bằng cách sử dụng shell khác. Ví dụ: tôi sử dụng tcshshell một cách tương tác, nhưng thường chạy các tập lệnh bash bằng cách sử dụng / bin / bash trong tệp script.

Qua một bên:

Khái niệm này cũng mở rộng cho các tập lệnh khác. Ví dụ: nếu bạn lập trình bằng Python, bạn sẽ đặt

 #!/usr/bin/python

ở đầu chương trình Python của bạn


Vậy có bắt buộc không? Làm cách nào để biết tôi đang thực sự sử dụng shell nào?
One Two Three

Vì vậy, nếu tôi đang viết script cho ai đó sử dụng trên máy của họ và tôi không biết họ đang sử dụng shell nào. (Thật không may, người này không biết gì về thứ này, vì vậy tất cả những gì anh ta có thể làm là chạy kịch bản mà không thay đổi bất kỳ thứ gì). Tôi có thể làm một cái gì đó như thế #! $SHELLnào? Điều này có đặt đúng vỏ vào Shebang không?
One Two Three

1
@OneTwoThree Hầu hết các hệ thống đều có shell tiêu chuẩn, nếu bạn viết tập lệnh bash hoặc csh thì bạn sẽ ổn. Họ đang sử dụng trình bao tương tác nào không quan trọng, đó là vẻ đẹp của !#/bin/bashchỉ thị ví dụ . Nó cho hệ thống biết shell cần sử dụng để thực thi shell script của bạn.
Levon

Giá trị của $SHELLkhông nhất thiết cho bạn biết bạn đang chạy trình bao nào vào lúc này; nó thường cho bạn biết trình bao mặc định của bạn . tcsh bộ $version$tcsh; bộ bash $BASH_VERSION. Không phải tất cả các shell nhất thiết phải có cơ chế giống nhau.
Keith Thompson

1
@OneTwoThree: #!Dòng phải khớp với cú pháp của script, không phải là shell tương tác được sử dụng bởi bất kỳ ai đang chạy script.
Keith Thompson
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.