Biến môi trường và thông số vị trí
Trước khi chúng ta bắt đầu thảo luận về $INTEGER
loại biến, chúng ta cần hiểu chúng thực sự là gì và chúng khác với các biến môi trường như thế nào. Các biến như $INTEGER
được gọi là tham số vị trí. Điều này được mô tả trong tiêu chuẩn POSIX (Giao diện hệ điều hành di động), phần 2.1 (nhấn mạnh của tôi):
- Shell thực thi một hàm (xem Lệnh Định nghĩa Hàm), tích hợp sẵn (xem Tiện ích tích hợp đặc biệt), tệp thực thi hoặc tập lệnh, đặt tên của các đối số là tham số vị trí được đánh số từ 1 đến n và tên của lệnh (hoặc trong trường hợp hàm trong tập lệnh, tên của tập lệnh) làm tham số vị trí được đánh số 0 (xem Tìm kiếm và Thực thi lệnh).
Ngược lại, các biến như $HOME
và $PATH
là biến môi trường. Định nghĩa của chúng được mô tả trong phần 8 của tiêu chuẩn :
Các biến môi trường được định nghĩa trong chương này ảnh hưởng đến hoạt động của nhiều tiện ích, chức năng và ứng dụng. Có các biến môi trường khác chỉ được quan tâm đến các tiện ích cụ thể. Các biến môi trường chỉ áp dụng cho một tiện ích duy nhất được xác định là một phần của mô tả tiện ích.
Chú ý mô tả của họ. Các tham số vị trí có nghĩa là xuất hiện trước một lệnh, tức là command positional_arg_1 positional_arg_2...
. Chúng được cung cấp bởi người dùng để ra lệnh cho những gì cụ thể phải làm. Khi bạn làm như vậy echo 'Hello' 'World'
, nó sẽ in ra các chuỗi Hello
và World
bởi vì đây là các tham số vị trí để echo
- những thứ bạn muốn thao tác echo
. Và echo
được xây dựng sao cho nó hiểu các tham số vị trí là các chuỗi được in (trừ khi chúng là một trong những cờ tùy chọn như -n
). Nếu bạn làm điều này với lệnh khác, nó có thể không hiểu gì Hello
vàWorld
là bởi vì có lẽ nó mong đợi một con số Lưu ý rằng các tham số vị trí không được "kế thừa" - một quy trình con không biết về các tham số vị trí của cha mẹ trừ khi được truyền rõ ràng cho quy trình con. Thường thì bạn thấy các tham số vị trí được truyền bằng các tập lệnh bao bọc - những tham số có thể kiểm tra phiên bản đã có của lệnh hoặc thêm tham số vị trí bổ sung vào lệnh thực sẽ được gọi.
Ngược lại, các biến môi trường có nghĩa là ảnh hưởng đến nhiều chương trình. Chúng là các biến môi trường , vì chúng được đặt bên ngoài chương trình (nhiều hơn về điều này bên dưới). Một số biến môi trường nhất định như HOME
hoặc PATH
có định dạng cụ thể, ý nghĩa cụ thể và chúng sẽ có nghĩa giống nhau cho từng chương trình. HOME
biến sẽ có nghĩa tương tự với tiện ích bên ngoài như /usr/bin/find
hoặc shell của bạn (và do đó là tập lệnh) - đó là thư mục chính của tên người dùng theo quy trình chạy. Lưu ý rằng các biến môi trường có thể được sử dụng để tính toán cho hành vi lệnh cụ thể, ví dụUID
biến môi trường có thể được sử dụng để kiểm tra xem tập lệnh có chạy với quyền root hay không và phân nhánh cho các hành động cụ thể tương ứng. Các biến môi trường có thể kế thừa - các tiến trình con có được bản sao môi trường của cha mẹ. Xem thêm Nếu các quy trình kế thừa môi trường của cha mẹ, tại sao chúng ta cần xuất khẩu?
Nói tóm lại, sự khác biệt chính là các biến môi trường được đặt bên ngoài lệnh và không có nghĩa là thay đổi (thông thường), trong khi các tham số vị trí là những thứ được xử lý bởi lệnh và chúng thay đổi.
Không chỉ khái niệm vỏ
Điều tôi nhận thấy từ các bình luận là bạn đang trộn lẫn thiết bị đầu cuối và vỏ, và thực sự sẽ khuyên bạn nên đọc về các thiết bị đầu cuối thực sự mà trước đây là các thiết bị vật lý. Ngày nay, "thiết bị đầu cuối" mà chúng ta thường đề cập đến, cửa sổ có nền đen và văn bản màu xanh lá cây thực sự là một phần mềm, một quá trình. Terminal là một chương trình chạy shell, trong khi shell cũng là một chương trình nhưng là chương trình đọc những gì bạn nhập vào để thực thi (nghĩa là, nếu đó là shell tương tác; shell không tương tác là các tập lệnh và sh -c 'echo foo'
kiểu gọi). Thêm về vỏ ở đây .
Đây là một sự khác biệt quan trọng, nhưng cũng quan trọng để nhận ra rằng thiết bị đầu cuối là một chương trình và do đó tuân thủ các quy tắc tương tự về môi trường và các tham số vị trí. gnome-terminal
Khi bạn bắt đầu sẽ xem xét SHELL
biến môi trường của bạn và sinh ra lớp vỏ mặc định phù hợp cho bạn, trừ khi bạn chỉ định một số lệnh khác với -e
. Giả sử tôi đã thay đổi shell mặc định của mình thành ksh
- gnome-terminal sau đó sẽ sinh ra ksh
thay vì bash
. Đó cũng là một ví dụ về cách môi trường được sử dụng bởi các chương trình. Nếu tôi nói rõ ràng gnome-terminal
với -e
việc chạy shell cụ thể - nó sẽ làm điều đó, nhưng nó sẽ không vĩnh viễn. Ngược lại, môi trường có nghĩa là hầu hết không thay đổi (nhiều hơn về sau).
Vì vậy, như bạn có thể thấy, các biến môi trường và vị trí là cả hai thuộc tính của một tiến trình / lệnh, không chỉ là shell. Khi nói đến shell script, chúng cũng tuân theo mô hình được thiết lập bởi ngôn ngữ lập trình C. Ví dụ như main
hàm C thường trông giống như
int main(int argc, char **argv)
, trong đó argc
số lượng đối số dòng lệnh và argv
là mảng tham số dòng lệnh hiệu quả, và sau đó có environ
chức năng (trên Linux đó man -e 7 environ
) để truy cập vào những thứ như đường dẫn thư mục chính của người dùng, danh sách các thư mục PATH
nơi chúng ta có thể tìm kiếm tệp thực thi, v.v. Các kịch bản Shell cũng được mô hình hóa theo cách tương tự. Trong thuật ngữ shell, chúng ta có các tham số vị trí $1
, $2
v.v., trong khi đó $#
là số lượng tham số vị trí. Thế còn $0
? Đó là tên của bản thực thi, một lần nữa cũng được mô hình hóa từ ngôn ngữ lập trình C - argv[0]
sẽ là tên của C "thực thi" của bạn. Và điều này khá đúng với hầu hết các ngôn ngữ lập trình và viết kịch bản .
Shell tương tác và không tương tác
Một trong những điều tôi đã gợi ý là sự khác biệt giữa các vỏ tương tác và không tương tác . Lời nhắc nơi bạn nhập lệnh - tương tác, nó tương tác với người dùng. Ngược lại khi bạn có một tập lệnh shell hoặc bạn chạy bash -c''
không tương tác.
Và đây là nơi phân biệt trở nên quan trọng. Shell mà bạn đã chạy là một tiến trình, được sinh ra với các tham số vị trí (đối với bash
shell đăng nhập là một "... có ký tự đầu tiên của đối số 0 là - hoặc bắt đầu bằng tùy chọn --login." ( Tham khảo ) )
Ngược lại, các kịch bản và shell được khởi chạy với -c
tùy chọn có thể tận dụng lợi thế $1
và $2
đối số. Ví dụ,
$ bash -c 'echo $1; stat $2' sh 'Hello World' /etc/passwd
Hello World
File: '/etc/passwd'
Size: 2913 Blocks: 8 IO Block: 4096 regular file
Device: 801h/2049d Inode: 6035604 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-08-12 14:48:37.125879962 -0600
Modify: 2017-08-12 14:48:37.125879962 -0600
Change: 2017-08-12 14:48:37.137879811 -0600
Birth: -
Lưu ý rằng tôi cũng đã sử dụng sh
ở đó, bởi vì -c
tùy chọn nhỏ là lấy tham số vị trí đầu tiên và gán cho nó $0
, không giống như thường là một tên của chương trình.
Một điều quan trọng cần chú ý là các tham số vị trí là cái mà tôi gọi là "framable". Lưu ý cách thức, lần đầu tiên chúng tôi khởi chạy bash
với các tham số vị trí của riêng nó, nhưng các tham số vị trí đó đã trở thành tham số echo
và stat
. Và mỗi chương trình hiểu nó theo cách riêng của nó. Nếu chúng tôi đưa ra stat
một chuỗi Hello World
và không có tệp nào Hello World
thì nó sẽ tạo ra lỗi; bash
coi nó như một chuỗi đơn giản nhưng stat
hy vọng chuỗi đó là một tên tệp hiện có. Ngược lại, tất cả các chương trình sẽ đồng ý rằng biến môi trường HOME
là một thư mục (trừ khi lập trình viên mã hóa nó theo cách không hợp lý).
Chúng ta có thể loay hoay với các biến môi trường và các tham số vị trí không?
Về mặt kỹ thuật, chúng ta có thể loay hoay với cả hai, nhưng chúng ta không nên loay hoay với các biến môi trường, trong khi chúng ta thường phải cung cấp các tham số vị trí. Ví dụ, chúng ta có thể chạy các lệnh trong shell với việc thêm một biến:
$ hello=world bash -c 'echo $hello'
world
Chúng ta cũng có thể đặt các biến vào môi trường chỉ bằng cách sử dụng export variable=value
từ trong shell hoặc script. Hoặc chúng ta có thể chạy một lệnh với môi trường hoàn toàn trống rỗng với env -c command arg1 arg2
. Tuy nhiên, thông thường không nên loay hoay với môi trường, đặc biệt là sử dụng các biến chữ hoa hoặc ghi đè các biến môi trường hiện có. Lưu ý rằng mặc dù không phải là một tiêu chuẩn.
Đối với các tham số vị trí, cách đặt chúng là rõ ràng, chỉ cần thêm chúng vào lệnh, nhưng cũng có những cách để đặt chúng thông minh khác , cũng như thay đổi danh sách các tham số đó thông qua shift
lệnh.
Tóm lại, mục đích của hai điều này là khác nhau, và chúng tồn tại vì một lý do. Tôi hy vọng mọi người có được cái nhìn sâu sắc từ câu trả lời này, và thật vui khi đọc nó giống như tôi đã viết câu trả lời này.
Lưu ý về lệnh set
Các set
lệnh, theo cư xử của nhãn hiệu như vậy (từ bash dẫn sử dụng, nhấn mạnh thêm):
Không có tùy chọn, tên và giá trị của từng biến shell được hiển thị theo định dạng có thể được sử dụng lại làm đầu vào để đặt hoặc đặt lại các biến hiện được đặt.
Nói cách khác, set
xem xét các biến cụ thể cho shell, ví dụ, một số trong số đó xảy ra trong môi trường HOME
. Bằng cách tương phản các lệnh như env
và printenv
nhìn vào biến môi trường thực tế mà lệnh chạy. Xem thêm này .
export 3
thể biến$3
thành một biến môi trường. Bạn không thểunset 3
; và bạn không thể gán$3
giá trị mới bằng cách sử dụng3=val
.