Vì tệp không thuộc bất kỳ loại thực thi nào được hệ thống nhận ra và giả sử bạn đã có quyền thực thi tệp đó, nên execve()
cuộc gọi hệ thống thường sẽ thất bại với lỗi ENOEXEC
( không phải là thực thi ).
Điều gì xảy ra sau đó tùy thuộc vào ứng dụng và / hoặc chức năng thư viện được sử dụng để thực thi lệnh.
Ví dụ, đó có thể là shell, hàm execlp()
/ execvp()
libc.
Hầu hết các ứng dụng khác sẽ sử dụng một trong những ứng dụng đó khi chúng chạy lệnh. Ví dụ, họ sẽ gọi shell bằng system("command line")
hàm libc, thường sẽ gọi sh
để phân tích dòng lệnh đó (đường dẫn có thể được xác định tại thời điểm biên dịch (như /bin/sh
so với /usr/xpg4/bin/sh
trên Solaris)) hoặc gọi shell được lưu trữ $SHELL
bởi chính họ như vi
với !
lệnh của nó hoặc xterm -e 'command line'
nhiều lệnh khác ( su user -c
sẽ gọi shell đăng nhập của người dùng thay vì $SHELL
).
Nói chung, một tệp văn bản không có shebang không bắt đầu #
được coi là một sh
tập lệnh. Mà sh
nó sẽ thay đổi mặc dù.
execlp()
/ execvp()
, khi execve()
trở về ENOEXEC
thường sẽ gọi sh
nó. Đối với các hệ thống có nhiều hơn một sh
vì chúng có thể tuân thủ nhiều hơn một tiêu chuẩn, sh
điều này thường được xác định tại thời điểm biên dịch (của ứng dụng sử dụng execvp()
/ execlp()
bằng cách liên kết một blob mã khác nhau có liên quan đến một đường dẫn khác sh
). Chẳng hạn, trên Solaris, đó sẽ là /usr/xpg4/bin/sh
(một tiêu chuẩn, POSIX sh
) hoặc /bin/sh
(vỏ Bourne (vỏ cổ) trên Solaris 10 trở lên, ksh93 trong Solaris 11).
Khi nói đến đạn pháo, có rất nhiều biến thể. bash
, AT & T ksh
, shell Bourne thường sẽ tự giải thích tập lệnh (trong một quy trình con trừ khi exec
được sử dụng) sau khi đã mô phỏng a execve()
, đó là đặt tất cả các biến không được báo cáo, đóng tất cả các fds đóng-thực thi, loại bỏ tất cả các bẫy tùy chỉnh, bí danh, hàm ... ( bash
sẽ diễn giải tập lệnh trong sh
chế độ). yash
sẽ tự thực thi (với chế độ sh
như argv[0]
vậy sh
) để diễn giải nó.
zsh
, pdksh
, ash
Vỏ dựa trên thường sẽ gọi sh
(con đường mà xác định tại thời gian biên dịch).
Đối với csh
và tcsh
(và sh
của một số BSD đầu tiên), nếu ký tự đầu tiên của tệp là #
, thì họ sẽ tự thực thi để giải thích nó, và sh
nếu không. Điều đó quay trở lại thời tiền shebang, nơi csh
đã nhận ra #
là bình luận nhưng không phải là vỏ Bourne, vì vậy đó #
là một gợi ý rằng đó là một kịch bản csh.
fish
(ít nhất là phiên bản 2.4.0), chỉ trả về lỗi nếu execve()
không thành công (không cố xử lý nó như một tập lệnh).
Một số shell (như bash
hoặc AT & T ksh
) trước tiên sẽ cố gắng xác định một cách chính xác xem liệu tệp có thể có nghĩa là một tập lệnh hay không. Vì vậy, bạn có thể thấy rằng một số shell sẽ từ chối thực thi tập lệnh nếu nó có ký tự NUL trong vài byte đầu tiên.
Cũng lưu ý rằng nếu execve()
thất bại với ENOEXEC nhưng tệp có dòng shebang, một số shell cố gắng tự giải thích dòng shebang đó.
Vì vậy, một vài ví dụ:
- Khi
$SHELL
là /bin/bash
, xterm -e 'myscript with args'
sẽ có myscript
giải thích bởi bash
trong sh
chế độ. Trong khi với xterm -e myscript with args
, xterm
sẽ sử dụng execvp()
để kịch bản sẽ được giải thích bởi sh
.
su -c myscript
trên Solaris 10, nơi root
đăng nhập của shell /bin/sh
và /bin/sh
là shell Bourne sẽ được myscript
giải thích bởi shell Bourne.
/usr/xpg4/bin/awk 'BEGIN{system("myscript")'
trên Solaris 10 sẽ có nó được giải thích bởi /usr/xpg4/bin/sh
(tương tự cho /usr/xpg4/bin/env myscript
).
find . -prune -exec myscript {} \;
trên Solaris 10 (sử dụng execvp()
) sẽ được giải thích /bin/sh
ngay cả với /usr/xpg4/bin/find
, ngay cả trong môi trường POSIX (lỗi tuân thủ).
csh -c myscript
sẽ được giải thích bởi csh
nếu nó bắt đầu bằng #
, sh
nếu không.
Nói chung, bạn không thể chắc chắn cái vỏ nào sẽ được sử dụng để diễn giải kịch bản đó nếu bạn không biết nó sẽ được gọi như thế nào và bằng cách nào.
Trong mọi trường hợp, read -p
là bash
cú pháp đơn thuần, vì vậy bạn sẽ muốn đảm bảo rằng tập lệnh được diễn giải bởi bash
(và tránh .sh
phần mở rộng gây hiểu lầm đó ). Hoặc bạn biết đường dẫn của bash
tệp thực thi và sử dụng:
#! /path/to/bash -
read -p ...
Hoặc bạn có thể thử và dựa vào $PATH
tra cứu bash
thực thi (giả sử đã bash
được cài đặt) bằng cách sử dụng:
#! /usr/bin/env bash
read -p ...
( env
hầu như có mặt khắp nơi trong /usr/bin
). Ngoài ra, bạn có thể làm cho nó tương thích POSIX + Bourne trong trường hợp bạn có thể sử dụng /bin/sh
. Tất cả các hệ thống sẽ có một /bin/sh
. Trên hầu hết chúng sẽ tương thích (đối với hầu hết các phần) tương thích POSIX, nhưng bạn vẫn có thể tìm thấy bây giờ và sau đó là một vỏ Bourne ở đó.
#! /bin/sh -
printf >&2 'Enter a user name: '
read user
printf '%s\n' "$user"