Tại sao thiết lập một biến trước một lệnh hợp pháp trong bash?


68

Tôi vừa gặp một số câu trả lời như phân tích tệp văn bản được phân tách ... sử dụng cấu trúc:

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

trong đó IFSbiến được đặt trước readlệnh.

Tôi đã đọc qua tài liệu tham khảo bash nhưng không thể hiểu tại sao điều này là hợp pháp.

Tôi đã thử

$ x="once upon" y="a time" echo $x $y

từ dấu nhắc lệnh bash nhưng không có gì lặp lại. Ai đó có thể chỉ cho tôi nơi mà cú pháp được xác định trong tham chiếu cho phép biến IFS được đặt theo cách đó không? Đây có phải là trường hợp đặc biệt hay tôi có thể làm điều gì đó tương tự với các biến khác không?


Câu trả lời:


69

Đó là theo Shell Grammar, Simple Commands (nhấn mạnh thêm):

Một lệnh đơn giản là một chuỗi các phép gán biến tùy chọn theo sau là các từ và chuyển hướng được phân tách trống và được chấm dứt bởi một toán tử điều khiển. Từ đầu tiên chỉ định lệnh sẽ được thực thi và được truyền dưới dạng đối số 0. Các từ còn lại được truyền dưới dạng đối số cho lệnh được gọi.

Vì vậy, bạn có thể vượt qua bất kỳ biến bạn muốn. echoVí dụ của bạn không hoạt động vì các biến được truyền cho lệnh, không được đặt trong trình bao. Shell mở rộng $x$y trước khi gọi lệnh. Điều này hoạt động, ví dụ:

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time

Cảm ơn! Tôi đã làm rất nhiều việc trước khi hỏi, và đang cố gắng tìm ra nơi mà nó nói rằng trong tài liệu tham khảo, không có may mắn. Tôi đang cố gắng để viết tốt hơn các tập lệnh bash và câu trả lời của bạn sẽ giúp ích.
Mike Lippert

1
Hmm Tôi nghĩ rằng tôi cần tìm một tài liệu tham khảo tốt hơn, tôi (xem liên kết ở trên) không nói rằng nó không có phần Shell Grammar.
Mike Lippert

1
@MikeLippert Xem 3.7.4 trong tài liệu tham khảo đó ("Môi trường cho bất kỳ lệnh hoặc hàm đơn giản nào có thể được tăng tạm thời bằng cách thêm tiền tố vào nó bằng các phép gán tham số"). Tôi nghĩ rằng tài liệu tham khảo là từ một phiên bản cũ của bash. Tôi vừa chạy man bashtrên hệ thống của mình ...
derobert

29

Các biến được xác định trở thành giống như các biến môi trường trong quá trình rẽ nhánh.

Nếu bạn chạy

A="b" echo $A

sau đó bash đầu tiên mở rộng $Athành ""và sau đó chạy

A="b" echo

Đây là cách chính xác:

x="once upon" y="a time" bash -c 'echo $x $y'

Lưu ý các dấu ngoặc đơn trong bash -c, nếu không bạn có cùng một vấn đề như trên.

Vì vậy, ví dụ vòng lặp của bạn là hợp pháp vì lệnh bash dựng sẵn 'read' sẽ tìm IFS trong các biến môi trường của nó và tìm ,. Vì thế,

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done

sẽ in TEST is and I is test

Cuối cùng, như đối với cú pháp, trong một vòng lặp for, một chuỗi được mong đợi. Vì vậy, tôi đã phải sử dụng backticks để biến nó thành một lệnh. Tuy nhiên, trong khi các vòng lặp mong đợi cú pháp lệnh, chẳng hạn như IFS=, read xx yy zz.


1
Cảm ơn, tôi đã học được nhiều hơn một chút so với câu hỏi của bạn. Tôi cũng sẽ bỏ phiếu cho câu trả lời của bạn, nhưng tôi chưa được phép và tôi đã đánh dấu câu trả lời được chấp nhận ở câu đầu tiên.
Mike Lippert

1
Cảm ơn, tôi là những gì tôi đã hy vọng. Và một cuộc bỏ phiếu ít ý nghĩa hơn là nghe sự đánh giá cao của bạn!
Mike Fairhurst

Để làm rõ nhận xét của bạn dưới dòng mã đầu tiên: Có, với Abiến không đặt bash sẽ mở rộng $Athành một chuỗi trống nhưng để tránh nhầm lẫn, tôi sẽ không sử dụng ""vì mã không tương đương A="b" echo "". Sẽ không có tranh luận để echo.
pabouk

8

man bash

XUNG QUANH

[...] Môi trường cho bất kỳ lệnh hoặc chức năng đơn giản nào có thể được tăng cường tạm thời bằng cách thêm tiền tố vào nó bằng các phép gán tham số, như được mô tả ở trên trong PARAMETERS. Các câu lệnh gán này chỉ ảnh hưởng đến môi trường mà lệnh đó nhìn thấy.

Các biến được mở rộng trước khi việc gán biến diễn ra. Vì lý do rõ ràng var=xcũng sẽ làm việc theo cách khác, nhưng var=$othervarsẽ không. Tức $xlà của bạn là cần thiết trước khi nó có sẵn. Nhưng đó không phải là vấn đề chính. Vấn đề chính là dòng lệnh chỉ có thể được sửa đổi bởi môi trường shell nhưng việc gán không trở thành một phần của môi trường shell.

Bạn trộn lẫn với các tính năng: Bạn muốn thay thế dòng lệnh nhưng đặt định nghĩa biến vào môi trường lệnh. Thay thế dòng lệnh phải được thực hiện bởi vỏ. Môi trường phải được sử dụng rõ ràng bởi lệnh được gọi. Liệu và làm thế nào điều này được thực hiện tùy thuộc vào lệnh.

Ưu điểm của việc sử dụng này là bạn có thể đặt môi trường cho một quy trình con mà không ảnh hưởng đến môi trường shell.

x="once upon" y="a time" bash -c 'echo $x $y'

hoạt động như bạn mong đợi vì trong trường hợp đó cả hai tính năng được kết hợp: Việc thay thế dòng lệnh không được thực hiện bởi shell gọi mà bởi shell quy trình con.


1
Nó tinh tế hơn thế một chút, bởi vì ví dụ này cũng hoạt động x="once upon" y="a time" eval 'echo $x $y'khi không có quy trình con nào tham gia vì evalnó là một nội dung. Tôi đoán các trích dẫn có liên quan từ các trang web là The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments. Xem xét ví dụ của câu hỏi nó phải theo cách này vì readnó cũng là một nội dung và nó hoạt động với trạng thái thay đổi theo thời gian IFS.
David Ongaro

4

Lệnh mà bạn cung cấp là khác nhau vì $x$yđược mở rộng trước khi các echolệnh chạy, vì vậy giá trị của họ trong hiện tại vỏ được sử dụng, không phải là giá trị mà echosẽ thấy trong môi trường của nó nếu nó là để xem xét.


Làm thế nào có thể mở rộng bất cứ thứ gì trong dòng lệnh sau khi lệnh chạy ...?
Hauke ​​Laging

Các lệnh gán trước cho xydành cho môi trường echochạy trong, không phải môi trường mà các đối số echođược mở rộng. Đối với IFS=, read xx yy zz, toàn bộ chuỗi được đọc, unplit, bằng readlệnh. Sau đó , chuỗi được chia theo giá trị của IFS, với các mảnh tương ứng được gán cho xx, yyzz.
chepner

Tôi chỉ muốn chỉ ra rằng từ ngữ "được mở rộng trước khi lệnh chạy" không có ý nghĩa gì nhiều vì sau khi bắt đầu lệnh, không còn gì được mở rộng nữa. Hơn nữa: Bạn đã không có một cái nhìn duy nhất về câu trả lời của tôi hay bạn dù sao cũng tin rằng tôi cần một lời giải thích về những gì đang xảy ra ...?
Hauke ​​Laging

1
Tôi không tuyên bố bạn cần một lời giải thích cũng như không có gì có thể được mở rộng sau khi lệnh chạy. Tuy nhiên, đúng là bashtrước tiên phân tích cú pháp dòng lệnh đã cho, nhận ra rằng có hai phép gán biến để áp dụng cho môi trường của lệnh sắp tới, xác định lệnh để chạy ( echo), mở rộng bất kỳ tham số nào được tìm thấy trong các đối số, sau đó chạy lệnh echovới các đối số mở rộng.
chepner

Trong trường hợp echotôi không chắc nó có thể "nhìn thấy" biến hay không, vì đó là lệnh dựng sẵn và do đó không chạy trong một lớp con có thể có môi trường riêng. Nhưng tôi đã thử nó evalcũng là một nội dung và nó thực sự biết về nó. Ví dụ: thử a=xyz eval 'echo $BASHPID $a; grep -z ^a /proc/$BASHPID/{,task/*}/environ'; echo $BASHPID $acho thấy achỉ được đặt bên trong evalmặc dù pid là như nhau và môi trường không bị thay đổi trong eval! (Để truy cập, /procbạn cần chạy nó trong Linux.) Có vẻ như bash thực hiện một số phép thuật bổ sung ở đây.
David Ongaro

2

Tôi sẽ cho bức tranh lớn hơn về " tại sao nó hợp pháp"

Trả lời: Để bạn có thể gọi hoặc gọi một chương trình và đối với lệnh đó chỉ sử dụng một biến với một biến cụ thể.

Ví dụ: Bạn có một thông số cho kết nối cơ sở dữ liệu được gọi là 'db_connection' và thông thường bạn chuyển vào 'test' làm tên cho kết nối cơ sở dữ liệu thử nghiệm của bạn. Trong thực tế, bạn thậm chí có thể đặt nó làm mặc định mà sau đó bạn không cần phải vượt qua một cách rõ ràng. Tuy nhiên, đôi khi bạn muốn làm việc với cơ sở dữ liệu ci. Vì vậy, bạn truyền vào param là 'ci' và sau đó chương trình được gọi sử dụng param cơ sở dữ liệu đó làm tên của db để sử dụng cho tất cả các cuộc gọi cơ sở dữ liệu. Đối với lần chạy tiếp theo, nếu bạn không lặp lại cách tiếp cận và chỉ cần gọi chương trình, biến sẽ trở về giá trị mặc định trước đó.


0

Bạn cũng có thể sử dụng ;. Nó sẽ được đánh giá trước vì nó là dấu phân cách lệnh.

x="once upon" y="a time"; echo $x $y

1
Điều này tạo ra hai biến shell và không tạo biến môi trường trong tiện ích đã cho. Đây là một điều rất khác nhau. Ngoài ra còn có tác dụng phụ là lớp vỏ hiện tại có các biến mới trong đó.
Kusalananda
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.