Khi tôi chạy hai lệnh này, tôi nhận được
$ type cd
cd is a shell builtin
$ type if
if is a shell keyword
Nó được hiển thị rõ ràng cd
là một shell dựng sẵn và if
là một từ khóa shell. Vì vậy, sự khác biệt giữa shell dựng và từ khóa là gì?
Khi tôi chạy hai lệnh này, tôi nhận được
$ type cd
cd is a shell builtin
$ type if
if is a shell keyword
Nó được hiển thị rõ ràng cd
là một shell dựng sẵn và if
là một từ khóa shell. Vì vậy, sự khác biệt giữa shell dựng và từ khóa là gì?
Câu trả lời:
Có một sự khác biệt lớn giữa nội dung và từ khóa, theo cách Bash phân tích mã của bạn. Trước khi chúng tôi nói về sự khác biệt, hãy liệt kê tất cả các từ khóa và nội dung:
Nội dung:
$ compgen -b
. : [ alias bg bind break
builtin caller cd command compgen complete compopt
continue declare dirs disown echo enable eval
exec exit export false fc fg getopts
hash help history jobs kill let local
logout mapfile popd printf pushd pwd read
readarray readonly return set shift shopt source
suspend test times trap true type typeset
ulimit umask unalias unset wait
Từ khóa:
$ compgen -k
if then else elif fi case
esac for select while until do
done in function time { }
! [[ ]] coproc
Lưu ý rằng, ví dụ [
là một nội dung và đó [[
là một từ khóa. Tôi sẽ sử dụng hai cái này để minh họa sự khác biệt bên dưới, vì chúng là các toán tử nổi tiếng: mọi người đều biết chúng và sử dụng chúng thường xuyên (hoặc nên).
Một từ khóa được quét và hiểu bởi Bash từ rất sớm trong quá trình phân tích cú pháp. Điều này cho phép ví dụ như sau:
string_with_spaces='some spaces here'
if [[ -n $string_with_spaces ]]; then
echo "The string is non-empty"
fi
Điều này hoạt động tốt, và Bash sẽ vui vẻ đầu ra
The string is non-empty
Lưu ý rằng tôi đã không trích dẫn $string_with_spaces
. Trong khi đó:
string_with_spaces='some spaces here'
if [ -n $string_with_spaces ]; then
echo "The string is non-empty"
fi
cho thấy Bash không vui:
bash: [: too many arguments
Tại sao nó hoạt động với các từ khóa mà không phải với nội dung? bởi vì khi Bash phân tích mã, nó sẽ thấy [[
đó là một từ khóa và hiểu rất sớm rằng nó đặc biệt. Vì vậy, nó sẽ tìm kiếm sự đóng cửa ]]
và sẽ đối xử với bên trong một cách đặc biệt. Một dựng sẵn (hoặc lệnh) được coi là một lệnh thực tế sẽ được gọi với các đối số. Trong ví dụ cuối cùng này, bash hiểu rằng nó nên chạy lệnh [
với các đối số (hiển thị một dòng trên mỗi dòng):
-n
some
spaces
here
]
kể từ khi mở rộng biến, loại bỏ trích dẫn, mở rộng tên đường dẫn và tách từ xảy ra. Lệnh [
hóa ra được xây dựng trong trình bao, do đó, nó thực thi nó với các đối số này, dẫn đến lỗi, do đó khiếu nại.
Trong thực tế, bạn thấy rằng sự khác biệt này cho phép hành vi tinh vi, điều đó sẽ không thể xảy ra với các nội trang (hoặc lệnh).
Trong thực tế, làm thế nào bạn có thể phân biệt một nội dung với một từ khóa? đây là một thử nghiệm thú vị để thực hiện:
$ a='['
$ $a -d . ]
$ echo $?
0
Khi Bash phân tích cú pháp dòng $a -d . ]
, nó thấy không có gì đặc biệt (nghĩa là không có bí danh, không chuyển hướng, không từ khóa), vì vậy nó chỉ thực hiện mở rộng biến. Sau khi mở rộng biến, nó thấy:
[ -d . ]
Vì vậy, thực thi lệnh (dựng sẵn) [
với các đối số -d
, .
và ]
, tất nhiên là đúng (điều này chỉ kiểm tra xem có phải .
là một thư mục không).
Bây giờ hãy nhìn đi:
$ a='[['
$ $a -d . ]]
bash: [[: command not found
Oh. Đó là bởi vì khi Bash nhìn thấy dòng này, nó không thấy gì đặc biệt, và do đó mở rộng tất cả các biến và cuối cùng thấy:
[[ -d . ]]
Tại thời điểm này, việc mở rộng bí danh và quét từ khóa đã được thực hiện từ lâu và sẽ không được thực hiện nữa, vì vậy Bash cố gắng tìm lệnh được gọi [[
, không tìm thấy nó và phàn nàn.
Dọc theo cùng một dòng:
$ '[' -d . ]
$ echo $?
0
$ '[[' -d . ]]
bash: [[: command not found
và
$ \[ -d . ]
$ echo $?
0
$ \[[ -d . ]]
bash: [[: command not found
Bí danh mở rộng là một cái gì đó khá đặc biệt quá. Bạn đã thực hiện tất cả những điều sau đây ít nhất một lần:
$ alias ll='ls -l'
$ ll
.... <list of files in long format> ....
$ \ll
bash: ll: command not found
$ 'll'
bash: ll: command not found
Lý do là như nhau: mở rộng bí danh xảy ra lâu trước khi mở rộng biến và loại bỏ trích dẫn.
Từ khóa vs Bí danh
Bây giờ bạn nghĩ điều gì xảy ra nếu chúng ta xác định bí danh là một từ khóa?
$ alias mytest='[['
$ mytest -d . ]]
$ echo $?
0
Ồ, nó hoạt động! vì vậy bí danh có thể được sử dụng để từ khóa bí danh! rất vui được biết
Kết luận: nội dung thực sự hoạt động giống như các lệnh: chúng tương ứng với một hành động được thực thi với các đối số trải qua quá trình mở rộng biến trực tiếp và phân tách từ và tạo khối. Nó thực sự giống như có một lệnh bên ngoài ở đâu đó /bin
hoặc /usr/bin
được gọi với các đối số được đưa ra sau khi mở rộng biến, v.v. Lưu ý rằng khi tôi nói nó thực sự giống như có một lệnh bên ngoài, tôi chỉ có ý nghĩa đối với các đối số, tách từ, nối, mở rộng biến, vv Một dựng sẵn có thể sửa đổi trạng thái bên trong của vỏ!
Mặt khác, từ khóa được quét và hiểu từ rất sớm và cho phép thực hiện hành vi vỏ phức tạp: trình bao sẽ có thể cấm chia tách từ hoặc mở rộng tên đường dẫn, v.v.
Bây giờ hãy nhìn vào danh sách các nội dung và từ khóa và cố gắng tìm hiểu tại sao một số cần phải là từ khóa.
!
là một từ khóa. Có vẻ như có thể bắt chước hành vi của nó với một chức năng:
not() {
if "$@"; then
return false
else
return true
fi
}
nhưng điều này sẽ cấm các cấu trúc như:
$ ! ! true
$ echo $?
0
hoặc là
$ ! { true; }
echo $?
1
Tương tự như vậy time
: sẽ mạnh hơn khi có từ khóa để có thể điều chỉnh thời gian các lệnh và đường ống phức tạp phức tạp với các chuyển hướng:
$ time grep '^#' ~/.bashrc | { i=0; while read -r; do printf '%4d %s\n' "$((++i))" "$REPLY"; done; } > bashrc_numbered 2>/dev/null
Nếu time
nơi chỉ một lệnh (thậm chí BUILTIN), nó sẽ chỉ nhìn thấy những lập luận grep
, ^#
và /home/gniourf/.bashrc
, lần này, và sau đó sản lượng của nó sẽ đi qua các phần còn lại của đường ống. Nhưng với một từ khóa, Bash có thể xử lý mọi thứ! nó có thể time
đường ống hoàn chỉnh, bao gồm cả các chuyển hướng! Nếu time
chỉ là một lệnh, chúng ta không thể làm:
$ time { printf 'hello '; echo world; }
Thử nó:
$ \time { printf 'hello '; echo world; }
bash: syntax error near unexpected token `}'
Cố gắng sửa nó:
$ \time { printf 'hello '; echo world;
time: cannot run {: No such file or directory
Vô vọng.
Từ khóa vs bí danh?
$ alias mytime=time
$ alias myls=ls
$ mytime myls
Bạn nghĩ cái gì xảy ra?
Thực sự, một nội trang giống như một lệnh, ngoại trừ việc nó được xây dựng trong trình bao, trong khi từ khóa là thứ cho phép thực hiện các hành vi tinh vi! chúng ta có thể nói đó là một phần của ngữ pháp của shell.
=\
'bằng cách sử dụng man
, apropos
và help
và tôi đã không có bất kỳ may mắn. Bất cứ ý tưởng nơi tôi sẽ đi về việc tìm kiếm thông tin này? Chủ yếu là để trong tương lai tôi có thể thấy những gì khác trong đó, bởi vì tôi nghĩ rằng tôi đang thiếu một nguồn tham khảo.
\ll
, "ll"
hoặc 'll'
.
[[
năng suất \[[
nên nó không được phân tích thành bí danh. Phải cho đến nay? Nơi tôi bị lạc không nhận ra dấu gạch chéo ngược là một điều trích dẫn trong Bash, và tôi đã cố gắng tìm kiếm nó dưới bí danh và từ khóa và đã hoàn toàn bị mất ... Bây giờ thì tốt rồi. Trong phần Trích dẫn:> Dấu gạch chéo ngược không trích dẫn () là ký tự thoát.
\[[
là một mã thông báo duy nhất thuộc loại LiteralQuoteToken, trái ngược với [[
đó sẽ là OpenTestKeywordToken và yêu cầu một ]]
cú pháp đóng / biên dịch chính xác của CloseTestKeywordToken. Sau đó, trong (4), LiteralQuoteToken sẽ được đánh giá [[
là tên của lệnh bulitin / lệnh để thực thi và trong (6) Bash sẽ barf vì không có [[
lệnh dựng hoặc lệnh. Tốt đẹp. Tôi có thể quên các chi tiết chính xác theo thời gian, nhưng tại thời điểm này, cách Bash điều hành công cụ rõ ràng hơn nhiều đối với tôi; cảm ơn bạn.
man bash
gọi họ SHELL BUILTIN COMMANDS
. Vì vậy, một "shell dựng" giống như một lệnh thông thường, như grep
, v.v., nhưng thay vì được chứa trong một tệp riêng biệt, nó lại được tích hợp vào bash . Điều này làm cho chúng thực hiện hiệu quả hơn các lệnh bên ngoài.
Một từ khóa cũng được "mã hóa cứng vào Bash, nhưng không giống như một nội dung, một từ khóa không phải là một lệnh, mà là một tiểu đơn vị của một cấu trúc lệnh." Tôi giải thích điều này có nghĩa là các từ khóa không có chức năng một mình, nhưng yêu cầu các lệnh để làm bất cứ điều gì. (Từ liên kết, ví dụ khác là for
, while
, do
, và !
, và có rất nhiều trong câu trả lời của tôi cho câu hỏi khác của bạn.)
[[ is a shell keyword
nhưng [ is a shell builtin
. Tôi không biết tại sao.
[
đã tồn tại dưới dạng lệnh riêng biệt trở lại trong ngày. [[
không được chỉ định bởi tiêu chuẩn, vì vậy các nhà phát triển có thể chọn đưa nó làm từ khóa hoặc dưới dạng tích hợp.
Hướng dẫn sử dụng dòng lệnh đi kèm với Ubuntu không đưa ra định nghĩa về từ khóa, tuy nhiên hướng dẫn trực tuyến (xem sidenote) và thông số kỹ thuật tiêu chuẩn của Ngôn ngữ lệnh POSIX Shell , gọi chúng là "Từ dành riêng" và cả hai đều cung cấp danh sách các từ đó. Từ tiêu chuẩn POSIX:
Sự công nhận này chỉ xảy ra khi không có ký tự nào được trích dẫn và khi từ được sử dụng là:
Từ đầu tiên của lệnh
Từ đầu tiên theo sau một trong những từ dành riêng ngoài trường hợp, cho hoặc trong
Từ thứ ba trong một trường hợp lệnh (chỉ trong trường hợp này là hợp lệ)
Từ thứ ba trong lệnh for (chỉ trong và làm là hợp lệ trong trường hợp này)
Chìa khóa ở đây là từ khóa / từ dành riêng có ý nghĩa đặc biệt vì chúng tạo điều kiện cho cú pháp shell, phục vụ để báo hiệu một số khối mã nhất định như vòng lặp, lệnh ghép, câu lệnh phân nhánh (if / case), v.v. Chúng cho phép tạo các câu lệnh, nhưng tự - không làm gì cả, và trong thực tế nếu bạn nhập từ khóa như for
, until
, case
- vỏ sẽ mong đợi một tuyên bố đầy đủ, nếu không - lỗi cú pháp:
$ for
bash: syntax error near unexpected token `newline'
$
Ở cấp độ mã nguồn, các từ dành riêng cho bash được định nghĩa trong parese.y , trong khi các phần dựng sẵn có toàn bộ thư mục dành riêng cho chúng.
Chỉ mục GNU hiển thị [
dưới dạng từ dành riêng, tuy nhiên đây là lệnh tích hợp trong thực tế. [[
ngược lại là một từ dành riêng.
Xem thêm: Sự khác nhau giữa từ khóa, từ dành riêng và nội dung?