Các biến như $ 0 và $ 1 shell / biến môi trường?


17

Có biến trong vỏ như $0, $1, $2, $?vv

Tôi đã cố gắng in các biến shell và môi trường bằng lệnh sau:

set

Nhưng những biến này không có trong danh sách.

Vì vậy, về cơ bản các biến này không được coi là biến shell / môi trường, phải không? (mặc dù để xuất chúng, bạn phải đặt trước chúng bằng một $, giống như bạn làm với các biến shell / môi trường)


3
Các tham số vị trí không phải là biến. Bạn không export 3thể biến $3thành một biến môi trường. Bạn không thể unset 3; và bạn không thể gán $3giá trị mới bằng cách sử dụng 3=val.
Kaz

Câu trả lời:


25

Các biến là một trong ba loại khác nhau của các tham số trong vỏ.

  1. Một biến là một tham số có tên là một định danh shell hợp lệ; bắt đầu bằng _hoặc một chữ cái, theo sau là 0 hoặc nhiều chữ cái, số hoặc _.
  2. Vị trí thông số là các thông số được đánh số $1, $2, ...
  3. Các tham số đặc biệt đều có tên một ký tự và ngoài ra $0, chúng đều là các ký tự dấu chấm câu khác nhau.

set chỉ hiển thị các biến của shell.

Một tập hợp con của các biến shell là các biến môi trường, có các giá trị được kế thừa từ môi trường khi shell khởi động hoặc được tạo bằng cách đặt exportthuộc tính trên một tên hợp lệ.


1
Lưu ý rằng sethiển thị tất cả các tham số trong zsh(không phải $ 1, $ 2 ... mà là $ *, $ @) và các hàm trong bash và bosh. Một số shell như ksh93 và các phiên bản cũ hơn của vars env đầu ra không được ánh xạ tới các biến shell. ( env 1=foo ksh -c setsẽ in 1=foo)
Stéphane Chazelas

11

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ề $INTEGERloạ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):

  1. 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$PATHlà 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 HelloWorldbở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ì HelloWorldlà 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ư HOMEhoặc PATHcó đị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. HOMEbiến sẽ có nghĩa tương tự với tiện ích bên ngoài như /usr/bin/findhoặ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ụUIDbiế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-terminalKhi bạn bắt đầu sẽ xem xét SHELLbiế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 kshthay 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-terminalvới -eviệ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ư mainhàm C thường trông giống như

int main(int argc, char **argv)

, trong đó argcsố lượng đối số dòng lệnh và argvlà mảng tham số dòng lệnh hiệu quả, và sau đó có environchứ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 PATHnơ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, $2v.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 bashshell đă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 -ctùy chọn có thể tận dụng lợi thế $1$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ì -ctù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 bashvớ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ố echostat. 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 statmột chuỗi Hello Worldvà không có tệp nào Hello Worldthì nó sẽ tạo ra lỗi; bashcoi nó như một chuỗi đơn giản nhưng stathy 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 HOMElà 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=valuetừ 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 shiftlệ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 setlệ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, setxem 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ư envprintenvnhìn vào biến môi trường thực tế mà lệnh chạy. Xem thêm này .


"Trong shell tương tác, bạn không thể tham chiếu $ 1, $ 2, v.v." Có tham chiếu trực tiếp cho điều này không? Tôi đã gặp phải những trường hợp kỳ lạ khi những thứ này được đặt trong môi trường vỏ tương tác và tôi không chắc liệu đây có được coi là 'không chuẩn' hay không.
dùng5359531

@ user5359531 Thành thật mà nói, tôi không chắc là tôi đã lấy nó từ đâu. Có lẽ bởi vì khi tôi đăng câu trả lời vào năm 2017, tôi có thể đã tham khảo rằng bạn không thể làm điều gì đó như thế 1="foo"nhưng sau đó tôi phát hiện ra rằng theo định nghĩa POSIX, một "từ" (đó là tên của một đối tượng như biến hoặc hàm) không thể bắt đầu với một chữ số (xem một câu hỏi tôi đã đăng về chủ đề này ). Các tham số vị trí rõ ràng là ngoại lệ cho quy tắc này.
Sergiy Kolodyazhnyy

@ user5359531 Tôi đã xóa phần câu trả lời, vì nó không đặc biệt chính xác. Bạn có thể tham khảo $1, $2v.v. trong shell tương tác, và trên thực tế, điều đó được thực hiện bằng setlệnh, thường để vượt qua /bin/shgiới hạn không có mảng. Cảm ơn đã mang đến sự chú ý của tôi. Tôi cũng sẽ chỉnh sửa câu trả lời trong vài ngày tới, vì nó cần thêm một chút đánh bóng và cập nhật.
Sergiy Kolodyazhnyy

Cảm ơn bạn đã làm rõ. Vấn đề tôi đang gặp là với conda, khi bạn chạy source conda/bin/activate, nó kiểm tra cho dù $1, $2vv, được thiết lập, nhằm xác định nếu nó được chạy như một kịch bản với các đối số hay không. Điều này kết thúc việc phá vỡ các hệ thống có những thiết lập trong môi trường tương tác vì một số lý do. Tôi hy vọng tìm hiểu xem hành vi không chuẩn này có phải là một lỗ hổng trong hệ thống để đặt các biến này trong môi trường tương tác hay trên chương trình để sử dụng chúng để xác định xem nó có chạy dưới dạng tập lệnh hay không.
dùng5359531

@ user5359531 Tôi khuyên bạn nên gửi báo cáo lỗi cho condanhà phát triển hoặc bất kỳ ai là tác giả ban đầu của tập lệnh đó, vì việc kiểm tra các ${N}tham số chắc chắn là cách sai. Có những câu hỏi về cùng một chủ đề ở đâyở đây , và ít nhiều cách di động là kiểm tra xem ${0}có giống với tên tập lệnh hay không, trong khi bashthực sự có biến môi trường cho mục đích đó
Sergiy Kolodyazhnyy

4

Các $1, $2, $3, ..., ${10}, ${11}biến được gọi là tham số vị trí và được trình bày trong phần hướng dẫn sử dụng bash3.4.1

3.4.1 Thông số vị trí

Tham số vị trí là một tham số được biểu thị bằng một hoặc nhiều chữ số, khác với một chữ số 0. Các tham số vị trí được gán từ các đối số của shell khi nó được gọi và có thể được gán lại bằng lệnh dựng sẵn. Tham số vị trí N có thể được tham chiếu là $ {N} hoặc $ N khi N bao gồm một chữ số. Các tham số vị trí có thể không được gán cho với các câu lệnh gán. Các bộ dựng và dịch chuyển được sử dụng để đặt và hủy đặt chúng (xem Lệnh dựng sẵn Shell). Các tham số vị trí được tạm thời thay thế khi hàm shell được thực thi (xem Hàm Shell).

Khi một tham số vị trí bao gồm nhiều hơn một chữ số được mở rộng, nó phải được đặt trong dấu ngoặc nhọn.

Đối với $?$0, các tham số đặc biệt này được đề cập trong phần tiếp theo3.4.2

3.4.2 Các thông số đặc biệt

Vỏ xử lý một số thông số đặc biệt. Các thông số này chỉ có thể được tham chiếu; giao cho họ không được phép.

...

?

($?) Mở rộng đến trạng thái thoát của đường ống tiền cảnh được thực hiện gần đây nhất.

0

($ 0) Mở rộng thành tên của tập lệnh shell hoặc shell. Điều này được đặt ở khởi tạo shell. Nếu Bash được gọi với một tệp lệnh (xem Shell Sc Script), $ 0 được đặt thành tên của tệp đó. Nếu Bash được bắt đầu với tùy chọn -c (xem Gọi Bash), thì $ 0 được đặt thành đối số đầu tiên sau chuỗi được thực thi, nếu có. Mặt khác, nó được đặt thành tên tệp được sử dụng để gọi Bash, như được đưa ra bởi đối số zero.


4

$1, $2... là các tham số vị trí , chúng không phải là biến, chứ đừng nói đến biến môi trường.

Trong Bourne giống như thuật ngữ vỏ, $somethingđược gọi là tham số mở rộng (còn vỏ ${something#pattern}và nhiều hơn nữa trong một số vỏ như ${array[x]}, ${param:offset}, ${x:|y}và nhiều nhà khai thác mở rộng hơn).

Có nhiều loại tham số khác nhau:

  • các biến như $foo,$PATH
  • tham số vị trí ( $1, $2... các đối số mà tập lệnh của bạn nhận được)
  • các thông số đặc biệt khác như $0, $-, $#, $*, $@, $$, $!, $?...

tên biến trong Bourne như shell phải bắt đầu bằng một ký tự chữ cái (bất kỳ được nhận biết bởi miền địa phương, hoặc giới hạn ở a-zA-Z tùy thuộc vào vỏ) và gạch dưới và theo sau là 0 hoặc nhiều ký tự chữ số hoặc dấu gạch dưới.

Tùy thuộc vào hệ vỏ, các biến có thể có các loại khác nhau (vô hướng, mảng, hàm băm) hoặc các thuộc tính nhất định (chỉ đọc, xuất, chữ thường ...).

Một số các biến được tạo ra bởi vỏ hoặc có ý nghĩa đặc biệt với vỏ (như $OPTIND, $IFS, $_...)

Các biến shell có thuộc tính export được tự động xuất dưới dạng các biến môi trường thành các lệnh mà shell thực thi.

Biến môi trường là một khái niệm tách biệt với các biến vỏ. Xuất một biến shell không phải là cách duy nhất để chuyển một biến môi trường sang thực thi lệnh.

VAR=foo
export VAR
printenv VAR

sẽ chuyển một VARbiến môi trường cho printenvlệnh (mà chúng tôi đang nói để in nội dung của nó), nhưng bạn cũng có thể làm:

env VAR=foo printenv VAR

hoặc là:

perl -e '$ENV{VAR}="foo"; exec "printenv", "VAR"'

ví dụ.

Biến môi trường có thể có bất kỳ tên nào (có thể chứa bất kỳ ký tự nào =và thậm chí có thể trống). Không nên đặt tên không tương thích với tên biến vỏ giống như Bourne cho biến môi trường, nhưng có thể:

$ env '#+%=whatever' printenv '#+%'
whatever

Shell sẽ ánh xạ các biến môi trường mà chúng nhận được cho các biến shell chỉ cho các biến môi trường có tên là biến shell hợp lệ (và trong một số shell bỏ qua một số biến đặc biệt như $IFS).

Vì vậy, trong khi bạn có thể chuyển một 1biến môi trường cho một lệnh:

$ env '1=whatever' printenv 1
whatever

Điều đó không có nghĩa là việc gọi shell với biến môi trường đó sẽ đặt giá trị của $1tham số:

$ env '1=whatever' sh -c 'echo "$1"' script-name foo bar
foo

3

Không, đây là những thông số của kịch bản. Ví dụ: nếu bạn gọi tập lệnh của mình như:

mynicescript.sh one two three

sau đó bên trong tập lệnh, sẽ có các tham số này là

$1 = one
$2 = two
$3 = three

và $ 0 là tên của chính tập lệnh.

Vì vậy, khi bạn ở ngoài tập lệnh, các biến này không có sẵn (ngoại trừ $ 0, hiển thị / bin / bash - chính trình bao).


"Vì vậy, khi bạn ở ngoài tập lệnh, các biến này không khả dụng" Ý bạn là gì khi "bên ngoài tập lệnh" , bởi vì tôi có thể thấy các giá trị của các biến này trong thiết bị đầu cuối của mình.
user7681202

2
@ user7681202: Những cái nào bạn có thể thấy trong thiết bị đầu cuối của bạn? $0sẽ trỏ đến quy trình đầu cuối hiện tại của bạn (có thể là bash) và $?chỉ đơn giản là mã thoát của quy trình cuối cùng.
Jesse_b

Tôi đã cố gắng chạy gnome-terminalvới các đối số ( gnome-terminal Hello World). Tôi có thể nhìn thấy $0, nhưng tôi không thể thấy $1$2.
user7681202

@Jesse_b Cảm ơn bạn, tôi đã thêm nội dung $ 0 vào câu trả lời.
Jaroslav Kucera

1
@ user7681202 Thiết bị đầu cuối gnome không phải là vỏ, nó là trình giả lập thiết bị đầu cuối (như xterm, konsole, v.v.). Shell chạy bên trong thiết bị đầu cuối và nó có thể là bash / sh / zsh / tcsh và nhiều hơn nữa. Kịch bản là tệp thực thi với tiêu đề thích hợp (như #! / Bin / bash) và nội dung có thể hiểu được bởi trình bao được chỉ định trong mặt nạ. Nó thường sử dụng hậu tố .sh
Jaroslav Kucera
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.