ตัวแปรที่มีเครื่องหมายคำพูด“ $ ()”


12

ฉันเขียนสคริปต์นี้:

#!/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

ผมเป็นมือใหม่พยายามที่จะเรียนรู้ได้อย่างรวดเร็วและผมได้รับคำถามเกี่ยวกับตัวแปรที่เครื่องหมายอัญประกาศ

ใส่ตัวแปรระหว่าง $ () เอาล่ะ แต่ทำไมต้องใส่เครื่องหมายอัญประกาศในifงบ? มันจะทำให้คำสั่งซ้อนกัน?


5
โปรดทราบว่าwhile [ true ]จะสร้างวงวนไม่สิ้นสุด แต่อาจไม่ใช่เพราะเหตุผลที่คุณคิด while [ false ] ยังสร้างวงวนไม่สิ้นสุดเนื่องจากมีอาร์กิวเมนต์เดียว[ ... ]ประสบความสำเร็จหากอาร์กิวเมนต์นั้นเป็นสตริงที่ไม่ว่างเปล่า while trueจะเรียกใช้คำสั่งชื่อจริงtrue(ซึ่งจะประสบความสำเร็จเสมอ)
chepner

4
$()คุณไม่ใส่ภายในตัวแปร คุณใส่คำสั่ง$()ภายใน
Wildcard

คำตอบ:


23

เครื่องหมายคำพูดป้องกัน "คำแยก" นั่นคือ: การแบ่งตัวแปรออกเป็นหลายรายการที่ตัวอักขระช่องว่าง (หรือเพื่อให้แม่นยำมากขึ้นที่ช่องว่างแท็บและบรรทัดใหม่ตามที่กำหนดไว้ในค่าของ$IFSตัวแปรเชลล์เริ่มต้น)

ตัวอย่างเช่น,

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

ที่นี่เรากำหนดhowmanyฟังก์ชันที่เพิ่งแจ้งให้เราทราบว่ามีพารามิเตอร์ตำแหน่งเท่าใด อย่างที่คุณเห็นมีสองรายการที่ถูกส่งผ่านไปยังตัวแปรและด้วยอัญประกาศข้อความในตัวแปรจะถือเป็นหนึ่งหน่วย

นี่เป็นสิ่งสำคัญสำหรับการส่งผ่านข้อมูลที่ถูกต้อง ตัวอย่างเช่นหากตัวแปรมีพา ธ ไปยังไฟล์และชื่อไฟล์มีช่องว่างที่ใดก็ได้ในพา ธ คำสั่งที่คุณพยายามเรียกใช้อาจล้มเหลวหรือให้ผลลัพธ์ที่ไม่ถูกต้อง หากเราพยายามสร้างไฟล์ด้วย$varตัวแปรtouch $varจะสร้างสองไฟล์ แต่touch "$var"เพียงไฟล์เดียว

กันไปสำหรับ[ "$currentoutput" != "$lastoutput" ]ส่วนของคุณ การทดสอบนี้ทำการเปรียบเทียบสองสาย เมื่อการทดสอบรัน[คำสั่งจะต้องเห็นอาร์กิวเมนต์ 3 ตัวคือสตริงข้อความตัว!=ดำเนินการและสตริงข้อความอื่น การรักษาอัญประกาศคู่เป็นการป้องกันการแยกคำและ[คำสั่งจะเห็นอาร์กิวเมนต์ 3 ตัวนั้น ตอนนี้จะเกิดอะไรขึ้นถ้าตัวแปรไม่ได้ถูกอ้างอิง

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

นี่แยกคำที่เกิดขึ้นและแทนที่จะ[เห็นสองสายhelloและworldตามด้วยตามด้วยสองสายอื่น!=hi worldจุดสำคัญคือว่าไม่มีเครื่องหมายคำพูดคู่เนื้อหาของตัวแปรจะถูกเข้าใจเป็นหน่วยแยกต่างหากมากกว่าหนึ่งรายการทั้งหมด

การกำหนดคำสั่งทดแทนไม่จำเป็นต้องมีเครื่องหมายคำพูดคู่เหมือนใน

var=$( df )

ที่คุณมีออกคำสั่งของการบันทึกไว้ในdf varอย่างไรก็ตามมันเป็นนิสัยที่ดีที่จะเพิ่มค่าเป็นสองเท่าของตัวแปรอัญประกาศและการทดแทนคำสั่ง$(...)ยกเว้นว่าคุณต้องการให้เอาท์พุทเป็นรายการแยกต่างหาก


เมื่อสังเกตด้านข้าง

while [ true ]

ส่วนหนึ่งสามารถ

while true

[เป็นคำสั่งที่ประเมินการขัดแย้งของมันและ[ whatever ]เป็นจริงเสมอโดยไม่คำนึงถึงสิ่งที่อยู่ภายใน ในทางตรงกันข้ามให้while trueใช้คำสั่งtrueที่ส่งคืนสถานะการออกจากความสำเร็จเสมอ (และนั่นคือสิ่งที่whileลูปต้องการ) ความแตกต่างนั้นค่อนข้างชัดเจนมากขึ้นและทำการทดสอบน้อยลง หรือคุณสามารถใช้:แทนtrue

echo "" date and Timeสามารถลบเครื่องหมายคำพูดคู่ในส่วนได้ พวกเขาเพียงแค่แทรกสตริงว่างและเพิ่มพื้นที่พิเศษเพื่อเอาท์พุท หากเป็นที่ต้องการอย่าลังเลที่จะเก็บมันไว้ที่นั่น แต่ไม่มีค่าการใช้งานเฉพาะในกรณีนี้

lsusb >> test.log

echo "$currentoutput" >> test.logส่วนนี้อาจจะถูกแทนที่ด้วย ไม่มีเหตุผลที่จะทำงานไม่ได้อีกครั้งหลังจากที่ได้รับการเรียกใช้อยู่แล้วในlsusb currentoutput=$(lsusb)ในกรณีที่บรรทัดใหม่ต่อท้าย ต้องถูกเก็บรักษาไว้ในเอาต์พุต - เราสามารถเห็นค่าในการรันคำสั่งหลายครั้ง แต่ในกรณีที่lsusbไม่จำเป็นต้องทำเช่นนั้น คำสั่งภายนอกที่น้อยกว่าที่คุณเรียกใช้จะดีกว่าเพราะการโทรไปยังคำสั่งที่ไม่ใช่ในตัวทุกครั้งจะทำให้เกิดค่าใช้จ่ายใน CPU การใช้หน่วยความจำและเวลาดำเนินการ (แม้ว่าคำสั่งนั้น


ดูสิ่งนี้ด้วย:


1
bashคำตอบที่ดีถ้าคุณไม่ได้แล้วคุณควรจะเขียนหนังสือเกี่ยวกับ แต่ในส่วนสุดท้ายไม่ควรecho "$currentoutput" >> test.log ดีขึ้น printf '%s\n' "$currentoutput" >> test.log ?
pLumo

2
@RoVo ใช่printfควรเป็นที่ต้องการสำหรับการพกพา เนื่องจากเราใช้สคริปต์เฉพาะของ bash ที่นี่จึงสามารถใช้แก้ตัวechoได้ แต่การสังเกตของคุณนั้นถูกต้องมาก
Sergiy Kolodyazhnyy

1
@SergiyKolodyazhnyy, ... ฉันไม่แน่ใจว่าechoเป็นที่เชื่อถือได้อย่างสมบูรณ์แม้ว่าเชลล์จะทราบว่าเป็นทุบตี (และรู้จักคุณค่าของxpg_echoและposixค่าสถานะ) หากค่าของคุณอาจเป็น-nหรือ-eมันอาจหายไปแทนที่จะถูกพิมพ์
Charles Duffy

4

ในcurrentoutput="$(lsusb)"lsusb ไม่ใช่ตัวแปรมันเป็นคำสั่ง สิ่งที่คำสั่งนี้ทำมันจะรันlsusbคำสั่งและกำหนดเอาท์พุทให้กับcurrentoutputตัวแปร

ไวยากรณ์ที่เก่ากว่าสำหรับสิ่งนี้คือ

currentoutput=`lsusb`

คุณสามารถค้นหาได้ในตัวอย่างและสคริปต์

เพื่อตอบคำถามส่วนอื่น ๆ ของคุณif [ ]เป็นเพียงวิธีการที่ไวยากรณ์ifกำหนดไว้ใน bash ดูเพิ่มเติมในhttps://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html


3
ฉันคิดว่ามันสำคัญที่จะบอกว่า[ ]นี่เป็นtestคำสั่งจริงๆ คุณสามารถใช้ifคำสั่งกับคำสั่งอื่น ๆ ได้เช่นกันเนื่องจากมันขึ้นอยู่กับการทดสอบ 0 หรือไม่ใช่ศูนย์ของรหัสการออก
Arronical

... ที่จริงแล้วมันดีกว่าที่จะวิ่งif grep -qe "somestring" fileมากกว่าวิ่งgrep -qe "somestring" file; if [ $? = 0 ]; then ...ดังนั้นการอ้างว่าif [ ...เป็นส่วนหนึ่งของคำนิยามของifไวยากรณ์ไม่ใช่แค่ทำให้เข้าใจผิด แต่นำไปสู่การปฏิบัติที่ไม่ดี
Charles Duffy

4

ต่อไปนี้จะรันคำสั่งภายนอกcommandและส่งคืนเอาต์พุต

"$(command)"

หากไม่มีเครื่องหมายวงเล็บ / วงเล็บนี่จะค้นหาตัวแปรแทนการรันคำสั่ง:

"$variable"

สำหรับความแตกต่างระหว่าง$variableและ"$variable"สิ่งนี้จะเกี่ยวข้องเมื่อ$variableมีช่องว่าง เมื่อใช้"$variable"เนื้อหาทั้งหมดจะถูกแทรกลงในสายเดียวแม้ว่าเนื้อหาจะมีช่องว่าง เมื่อใช้$variableเนื้อหาของตัวแปรอาจจะขยายเข้าไปในรายการอาร์กิวเมนต์ของหลายข้อโต้แย้ง


HI @thomasrutter ฉันขอโทษฉันหมายถึงเครื่องหมายคำพูด ... ฉันแก้ไขความคิดเห็นของฉันตอนนี้!
Shankhara

1

หากต้องการแตก - bash แนะนำให้คุณใช้คำสั่ง[[ ... ]]over over [ ... ]เพื่อหลีกเลี่ยงการอ้างถึงตัวแปรในการทดสอบดังนั้นปัญหาที่เกี่ยวข้องกับการแยกคำที่คนอื่น ๆ ได้ชี้ให้เห็น

[มีให้ในทุบตีสำหรับ POSIX เข้ากันได้กับสคริปต์หมายถึงการทำงานภายใต้#!/bin/shหรือเป็นผู้รังเพลิงไปทุบตี - [[ส่วนใหญ่ที่คุณควรหลีกเลี่ยงมันในความโปรดปรานของ

เช่น

# 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

bashไม่แนะนำเช่นนั้น มันให้ [[ ... ]]ซึ่งสามารถอำนวยความสะดวกมากขึ้นและมีการทำงานบางอย่างที่[ ... ]ไม่ได้ แต่ไม่มีปัญหากับการใช้อย่างถูกต้อง[ ... ]
chepner

@chepner - บางทีฉันควรจะชัดเจนกว่านี้ แน่นอนว่ามันไม่ใช่คำแนะนำที่ชัดเจนใน manpage ทุบตี แต่[[สิ่งที่ทำนั้นทำได้ทุกอย่าง[และมากขึ้นและยังคงมีอยู่หลายต่อหลายครั้งที่[ผู้คนขึ้นไปแล้วลองปรับเปลี่ยนคุณสมบัติของ[[ย้อนหลัง[ให้ล้มเหลวเท่านั้นข้อเสนอแนะของชุมชนในเท่าที่มากที่สุดคำแนะนำสไตล์ทุบตีที่เกี่ยวข้องจะให้คำแนะนำที่ไม่เคยใช้งาน [
shalomb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.