Biến với dấu ngoặc kép là $ $ ()


12

Tôi đã viết kịch bản này:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

Tôi là một người mới đang cố gắng học nhanh và tôi có một câu hỏi về dấu ngoặc kép của biến .

Đặt một biến giữa $ (), tôi hiểu nó, nhưng tại sao các dấu ngoặc kép cần thiết ngay cả trong ifcâu lệnh? Là nó để thực hiện một lệnh lồng nhau?


5
Lưu ý rằng điều while [ true ]đó tạo ra một vòng lặp vô hạn, nhưng có lẽ không phải vì lý do bạn nghĩ nó xảy ra; while [ false ] cũng tạo ra một vòng lặp vô hạn, bởi vì với một đối số duy nhất, sẽ [ ... ]thành công nếu đối số đó là một chuỗi không trống. while truesẽ thực sự chạy một lệnh có tên true(luôn luôn thành công).
chepner

4
Bạn không đặt một biến bên trong $(). Bạn đặt một lệnh bên trong $().
tự đại diện

Câu trả lời:


22

Dấu ngoặc kép ngăn chặn "tách từ". Đó là: chia nhỏ các biến thành nhiều mục tại các ký tự khoảng trắng (hoặc chính xác hơn là tại các khoảng trắng, tab và dòng mới như được định nghĩa trong giá trị của $IFSbiến shell mặc định ).

Ví dụ,

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

Ở đây chúng ta định nghĩa howmanyhàm chỉ cho chúng ta biết có bao nhiêu tham số vị trí được đưa ra. Như bạn có thể thấy, có hai mục được truyền cho biến và với dấu ngoặc kép, văn bản trong biến được coi là một đơn vị.

Điều này rất quan trọng để truyền thông tin chính xác. Ví dụ: nếu biến chứa đường dẫn đến tệp và tên tệp chứa khoảng trắng ở bất kỳ đâu trong đường dẫn, lệnh bạn đang cố chạy có thể không thành công hoặc cho kết quả không chính xác. Nếu chúng ta cố gắng tạo một tệp có $varbiến, touch $varsẽ tạo hai tệp, nhưng touch "$var"chỉ một.

[ "$currentoutput" != "$lastoutput" ]Phần của bạn cũng vậy . Thử nghiệm đặc biệt này thực hiện một so sánh trên hai chuỗi. Khi kiểm tra chạy, [lệnh sẽ cần xem 3 đối số - một chuỗi văn bản, !=toán tử và một chuỗi văn bản khác. Giữ dấu ngoặc kép ngăn chia tách từ và [lệnh sẽ thấy chính xác 3 đối số đó. Bây giờ điều gì xảy ra nếu các biến không được trích dẫn?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

Ở đây, phân tách từ xảy ra, và thay vào đó [nhìn thấy hai chuỗi helloworldtheo sau !=, theo sau là hai chuỗi khác hi world. Điểm chính là không có dấu ngoặc kép, nội dung của các biến được hiểu là các đơn vị riêng biệt chứ không phải là toàn bộ một mục.

Chỉ định thay thế lệnh không yêu cầu dấu ngoặc kép như trong

var=$( df )

nơi bạn có dfđầu ra của lệnh được lưu vào var. Tuy nhiên, đó là một thói quen tốt để luôn tăng gấp đôi các biến trích dẫn và thay thế lệnh $(...)trừ khi bạn thực sự muốn đầu ra được coi là các mục riêng biệt.


Bên cạnh đó,

while [ true ]

một phần có thể là

while true

[là một lệnh đánh giá các đối số của nó và [ whatever ]luôn luôn đúng bất kể bên trong là gì. Ngược lại, while truesử dụng lệnh trueluôn trả về trạng thái thoát thành công (và đó chính xác là những gì whilevòng lặp cần). Sự khác biệt là rõ ràng hơn một chút và thực hiện ít thử nghiệm hơn. Ngoài ra, bạn cũng có thể sử dụng :thay vìtrue

Các trích dẫn kép echo "" date and Timemột phần có thể có thể được gỡ bỏ. Họ chỉ chèn một chuỗi trống và thêm không gian vào đầu ra. Nếu đó là mong muốn, vui lòng giữ chúng ở đó, nhưng không có giá trị chức năng cụ thể trong trường hợp này.

lsusb >> test.log

Phần này có lẽ có thể được thay thế bằng echo "$currentoutput" >> test.log. Không có lý do để chạy lsusblại sau khi nó đã được chạy currentoutput=$(lsusb). Trong trường hợp các dòng mới phải được bảo toàn ở đầu ra - người ta có thể thấy giá trị khi chạy lệnh nhiều lần, nhưng trong trường hợp lsusbkhông cần điều đó. Càng gọi ít lệnh bên ngoài thì càng tốt, bởi vì mỗi lệnh gọi lệnh không tích hợp sẽ phát sinh chi phí CPU, sử dụng bộ nhớ và thời gian thực hiện (mặc dù các lệnh có thể được tải sẵn từ bộ nhớ).


Xem thêm:


1
Câu trả lời tuyệt vời, nếu bạn chưa có, bạn nên viết một cuốn sách về bash. Nhưng trong phần cuối không nên echo "$currentoutput" >> test.log tốt hơn printf '%s\n' "$currentoutput" >> test.log ?
pLumo

2
@RoVo Có, printfnên được ưu tiên cho tính di động. Vì chúng tôi đang sử dụng tập lệnh cụ thể bash ở đây nên nó có thể được miễn sử dụng echo. Nhưng quan sát của bạn rất đúng
Sergiy Kolodyazhnyy

1
@SergiyKolodyazhnyy, ... Tôi không chắc điều đó echohoàn toàn đáng tin cậy ngay cả khi vỏ được biết là bash (và giá trị của cờ xpg_echoposixđược biết); nếu giá trị của bạn có thể -nhoặc -e, nó có thể biến mất thay vì được in.
Charles Duffy

4

Trong currentoutput="$(lsusb)"lsusb không phải là một biến, nó là một lệnh. Câu lệnh này làm gì, nó thực thi lsusblệnh và gán đầu ra của nó cho currentoutputbiến.

Cú pháp cũ hơn cho điều này là

currentoutput=`lsusb`

bạn có thể tìm thấy nó trong nhiều ví dụ và kịch bản

Để trả lời phần khác của câu hỏi của bạn, if [ ]chỉ là cách cú pháp ifđược xác định trong bash. Xem thêm trong https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html


3
Tôi nghĩ rằng điều quan trọng để nói rằng đó [ ]thực sự là testlệnh. Bạn cũng có thể sử dụng các ifcâu lệnh với các lệnh khác, vì nó dựa vào kiểm tra 0 hoặc khác không về mã thoát của chúng.
Arronical

... thực sự, chạy if grep -qe "somestring" filetốt hơn nhiều so với chạy grep -qe "somestring" file; if [ $? = 0 ]; then ..., do đó, tuyên bố if [ ...là một phần của định nghĩa ifcú pháp không chỉ gây hiểu lầm mà dẫn đến các thực tiễn xấu.
Charles Duffy

4

Sau đây chạy lệnh bên ngoài commandvà trả về đầu ra của nó.

"$(command)"

Nếu không có dấu ngoặc / ngoặc đơn, điều này sẽ tìm kiếm một biến thay vì chạy lệnh:

"$variable"

Đối với sự khác biệt giữa $variable"$variable", điều này trở nên có liên quan khi $variablechứa khoảng trắng. Khi sử dụng "$variable", toàn bộ nội dung biến sẽ được chèn vào một chuỗi ngay cả khi nội dung bao gồm khoảng trắng. Khi sử dụng $variablenội dung của biến có thể được mở rộng thành một danh sách đối số của nhiều đối số.


HI @thomasrutter, tôi xin lỗi, ý tôi là dấu ngoặc kép ... Tôi chỉnh sửa nhận xét của mình ngay bây giờ!
Shankhara

0

Để có trái ngược - bash khuyến cáo bạn sử dụng [[ ... ]]trong [ ... ]xây dựng để tránh việc trích dẫn các biến trong các thử nghiệm và do đó những vấn đề liên quan đến chia tách từ mà những người khác đã chỉ ra.

[được cung cấp trong bash để tương thích POSIX với các tập lệnh có nghĩa là chạy theo #!/bin/shhoặc những phần được chuyển sang bash - phần lớn, bạn nên tránh nó theo hướng có lợi [[.

ví dụ

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal

bashkhông đưa ra khuyến nghị như vậy; nó cung cấp [[ ... ]] có thể thuận tiện hơn và có một số chức năng [ ... ]không có, nhưng không có vấn đề gì với việc sử dụng [ ... ] chính xác .
chepner

@chepner - Có lẽ tôi nên nói rõ hơn ở đây. Chắc chắn đó không phải là một khuyến nghị rõ ràng trong trang bash nhưng được đưa ra [[mọi thứ [và hơn thế nữa, nhiều lần [mọi người đi lên và sau đó thử và cải thiện các tính năng của [[back để [chỉ thất bại và để lại lỗi rải rác - thật công bằng khi nói rằng đó là một khuyến nghị cộng đồng trong chừng mực hầu hết các hướng dẫn phong cách bash có liên quan sẽ khuyên không bao giờ sử dụng[ .
shalomb
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.