การขยายตัวแปรอัตโนมัติภายในคำสั่ง bash [[]]


13

เมื่อพิจารณาตัวแปรในbashคุณต้องใช้$เครื่องหมาย อย่างไรก็ตามดูเหมือนว่าสิ่งต่อไปนี้ใช้ได้ดี:

x=5
[[ x -gt 2 ]]

ใครสามารถอธิบายสิ่งนี้ได้บ้าง

แก้ไข: (ข้อมูลเพิ่มเติม)

สิ่งที่ฉันหมายถึงคืออะไรและทำไมคำสั่ง [[]] ถึงการยกเลิกตัวแปร x ของฉันโดยไม่มีเครื่องหมาย $ และใช่ถ้า x = 1 คำสั่งจะถูกประเมินเป็นเท็จ (สถานะส่งคืน 1)


2
คุณหมายความว่าอย่างไรที่ "ทำงานได้ดี" และการประเมินของคุณจะเปลี่ยนไปถ้าคุณx=1ตามมาด้วย[[ x -gt 2]]?
nohillside

ฉันหมายความว่าอย่างไรและทำไมคำสั่ง [[]] ถึงการยกเลิกตัวแปร x ของฉันโดยไม่มีเครื่องหมาย $ และใช่ถ้า x = 1 ข้อความนั้นเป็นเท็จ (สถานะส่งคืน 1)
ผู้เยี่ยมชม

คำตอบ:


9

เหตุผลก็คือ-eqบังคับให้มีการประเมินผลทางคณิตศาสตร์ของข้อโต้แย้ง

ผู้ประกอบการทางคณิตศาสตร์: -eq, -gt, -lt, -ge, -leและ-neภายใน[[ ]](ใน ksh, zsh และทุบตี) หมายถึงการขยายเป็นชื่อตัวแปรในภาษา C $โดยอัตโนมัติไม่จำเป็นต้องสำหรับชั้นนำ

  • เพื่อยืนยันเราจะต้องดูเป็นรหัสแหล่งทุบตี คู่มือไม่มีการยืนยันโดยตรง

    ภายในtest.cการประมวลผลของตัวดำเนินการทางคณิตศาสตร์อยู่ในฟังก์ชั่นนี้:

    arithcomp (s, t, op, flags)

    ที่ไหนsและtเป็นตัวถูกดำเนินการทั้งสอง ตัวถูกดำเนินการถูกส่งไปยังฟังก์ชันนี้:

    l = evalexp (s, &expok);
    r = evalexp (t, &expok);

    ฟังก์ชั่นที่evalexpกำหนดไว้ภายในexpr.cซึ่งมีส่วนหัวนี้:

    /* expr.c -- arithmetic expression evaluation. */

    ดังนั้นใช่ทั้งสองด้านของตัวดำเนินการทางคณิตศาสตร์ตก (โดยตรง) ในการประเมินผลการแสดงออกทางคณิตศาสตร์ โดยตรงไม่มี buts ไม่ใช่ ifs


ในทางปฏิบัติด้วย:

 $ x=3

ทั้งสองอย่างนี้ล้มเหลว:

 $ [[ x = 4 ]] && echo yes || echo no
 no

 $ [[ x = 3 ]] && echo yes || echo no
 no

ซึ่งถูกต้องxไม่ถูกขยายและxไม่เท่ากับตัวเลข

อย่างไรก็ตาม:

 $ [[ x -eq 3 ]] && echo yes || echo no
 yes

 $ [[ x -eq 4 ]] && echo yes || echo no
 no

ตัวแปรที่ชื่อxได้รับการขยาย (แม้ไม่มี $)

สิ่งนี้จะไม่เกิดขึ้นสำหรับ a […]ใน zsh หรือ bash (เป็น ksh)


นั่นคือสิ่งที่เกิดขึ้นภายใน$((…)):

 $ echo $(( x + 7 ))
 10

และโปรดเข้าใจว่านี่คือ recursive (มาก) (ยกเว้นในเส้นประและสับ):

 $ a=b b=c c=d d=e e=f f=3
 $ echo "$(( a + 7 ))" 
 10

A 😮

และค่อนข้างเสี่ยง:

 $ x='a[$(date -u)]'
 $ [[ x -eq 3 ]] && echo yes || echo no
 bash: Tue Dec  3 23:18:19 UTC 2018: syntax error in expression (error token is "Dec  3 23:18:19 UTC 2018")

ข้อผิดพลาดทางไวยากรณ์สามารถหลีกเลี่ยงได้อย่างง่ายดาย:

 $ a=3; x='a[$(date -u >/dev/tty; echo 0)]'

 $ [[ x -eq 3 ]] && echo yes || echo no
 Tue Dec  4 09:02:06 UTC 2018
 yes

ตามที่พูดไป: ล้างข้อมูลของคุณให้สะอาด

 $ [[ ${x//[^0-9]} -eq 3 ]] && echo yes || echo no
 no

สิ้นสุด😮


ทั้งภายนอก (เก่ากว่า) /usr/bin/test(ไม่ใช่บิวอินtest) และยังเก่ากว่าและภายนอกexprไม่ขยายนิพจน์เท่านั้นจำนวนเต็ม (และเห็นได้ชัดว่าเป็นจำนวนเต็มทศนิยมเท่านั้น):

 $ /usr/bin/test "x" -eq 3
 /usr/bin/test: invalid integer x

 $ expr x + 3
 expr: non-integer argument

น่าสนใจ มันไม่ยากที่จะบอกว่าสิ่งนี้เป็นไปได้อย่างไร - การ[[ตรวจพบคำหลักตัวดำเนินการและตัวถูกดำเนินการเมื่อคำสั่งถูกอ่านและไม่ใช่หลังจากการขยาย จึง[[สามารถรักษาในวิธีที่ชาญฉลาดมากขึ้นกว่าการพูด-eq [แต่สิ่งที่ฉันสงสัยคือ: เราสามารถหาเอกสารเกี่ยวกับลอจิกแบบลอจิคัลที่ใช้ในการตีความคำสั่งผสมได้ที่ไหน? มันไม่ได้ดูค่อนข้างชัดเจนกับผมและผมเห็นได้ชัดว่าไม่สามารถหาคำอธิบายที่น่าพอใจในหรือman info bash
fra-san

Bash ไม่มีเอกสารนี้ทุกที่ที่ฉันสามารถหาได้ มีเป็นชนิดของคำอธิบายในคน ksh93 : เปรียบเทียบคณิตศาสตร์ต่อไปนี้ล้าสมัยนอกจากนี้ยังได้รับอนุญาต: exp1 -eq exp2 มีข้อความนี้ในเป็นส่วนของมนุษย์ zshbuiltinsดำเนินการทางคณิตศาสตร์คาดหวังจำนวนเต็มข้อโต้แย้งมากกว่าการแสดงออกทางคณิตศาสตร์ ซึ่งยืนยันว่าข้อโต้แย้งบางอย่างจะถือเป็นนิพจน์ทางคณิตศาสตร์โดยการทดสอบ builtin ภายใต้เงื่อนไขที่ไม่ได้ระบุในใบเสนอราคา ฉันจะยืนยันด้วยซอร์สโค้ด…. …test
NotAnUnixNazi

7

ตัวถูกดำเนินการของการเปรียบเทียบตัวเลข-eq, -gt, -lt, -ge, -leและ-neถูกนำมาเป็นนิพจน์ทางคณิตศาสตร์ ด้วยข้อ จำกัด บางอย่างพวกเขายังคงต้องเป็นคำของคำเดี่ยว

พฤติกรรมของชื่อตัวแปรในนิพจน์เชิงคำนวณอธิบายไว้ในShell Arithmetic :

อนุญาตให้ใช้ตัวแปรเชลล์ในฐานะตัวถูกดำเนินการ การขยายพารามิเตอร์จะดำเนินการก่อนการแสดงออกของการประเมิน ภายในนิพจน์ตัวแปรเชลล์อาจถูกอ้างอิงด้วยชื่อโดยไม่ต้องใช้ไวยากรณ์การขยายพารามิเตอร์ ตัวแปรเชลล์ที่เป็นค่า null หรือ unset ประเมินเป็น 0 เมื่ออ้างอิงโดยชื่อโดยไม่ใช้ไวยากรณ์การขยายพารามิเตอร์

และนอกจากนี้ยังมี:

ค่าของตัวแปรจะถูกประเมินเป็นนิพจน์ทางคณิตศาสตร์เมื่อมีการอ้างอิง

แต่ฉันไม่สามารถหาส่วนของเอกสารที่กล่าวว่าการเปรียบเทียบเชิงตัวเลขใช้การแสดงออกทางคณิตศาสตร์ มันไม่ได้อธิบายไว้ในเงื่อนไข Constructsใต้[[และไม่เป็นมันอธิบายไว้ในทุบตีเงื่อนไขการแสดงออก

แต่จากการทดลองดูเหมือนว่าจะทำงานได้ดังที่ได้กล่าวไว้ข้างต้น

ดังนั้นสิ่งนี้ทำงาน:

a=6
[[ a -eq 6 ]] && echo y 
[[ 1+2+3 -eq 6 ]] && echo y
[[ "1 + 2 + 3" -eq 6 ]] && echo y

สิ่งนี้ด้วย (ค่าของตัวแปรถูกประเมิน):

b='1 + 2 + 3'
[[ b -eq 6 ]] && echo y

แต่นี่ไม่ใช่ ไม่ใช่คำเดียวของเชลล์เมื่อ[[ .. ]]วิเคราะห์คำดังนั้นมีข้อผิดพลาดทางไวยากรณ์ในเงื่อนไข:

[[ 1 + 2 + 3 -eq 6 ]] && echo y

ในบริบททางคณิตศาสตร์อื่น ๆ ไม่จำเป็นต้องมีนิพจน์ที่ไม่มีช่องว่าง สิ่งนี้จะพิมพ์ออก999มาในขณะที่เครื่องหมายวงเล็บแบ่งเขตนิพจน์ทางคณิตศาสตร์ในดัชนีอย่างไม่น่าสงสัย:

a[6]=999; echo ${a[1 + 2 + 3]}

ในทางตรงกันข้ามการ=เปรียบเทียบคือการจับคู่รูปแบบและไม่เกี่ยวข้องกับคณิตศาสตร์และการขยายตัวของตัวแปรอัตโนมัติที่ทำในบริบททางคณิตศาสตร์ (โครงสร้างตามเงื่อนไข):

เมื่อใช้ตัวดำเนินการ==และ!=ตัวดำเนินการสตริงทางด้านขวาของตัวดำเนินการจะถือเป็นรูปแบบและจับคู่ตามกฎที่อธิบายไว้ด้านล่างในการจับคู่รูปแบบราวกับว่าเปิดใช้งานตัวเลือกเปลือก extglob ประกอบเป็นเหมือน===

ดังนั้นนี่เป็นเท็จเนื่องจากสตริงแตกต่างกันอย่างเห็นได้ชัด:

[[ "1 + 2 + 3" = 6 ]] 

เช่นนี้แม้ว่าค่าตัวเลขจะเท่ากัน:

[[ 6 = 06 ]] 

และที่นี่ด้วยการเปรียบเทียบสตริง ( xและ6) มีความแตกต่าง:

x=6
[[ x = 6 ]]

สิ่งนี้จะขยายตัวแปรแม้ว่านี่จะเป็นจริง:

x=6
[[ $x = 6 ]]

ไม่สามารถหาส่วนของเอกสารที่กล่าวว่าจริง ๆ แล้วการเปรียบเทียบเชิงตัวเลขใช้การแสดงออกทางคณิตศาสตร์ ยืนยันอยู่ในรหัส
NotAnUnixNazi

สิ่งที่ใกล้เคียงที่สุดคือคำอธิบายarg1 OP arg2บอกว่า args อาจเป็นจำนวนเต็มบวกหรือลบซึ่งฉันเดาว่าควรจะบอกเป็นนัยว่าพวกเขากำลังถือว่าเป็นนิพจน์ทางคณิตศาสตร์ สับสนก็หมายความว่าพวกเขาไม่สามารถเป็นศูนย์ :)
Barmar

@Barmar ใช่มั้ย แต่นั่นก็นำไปใช้กับการเปรียบเทียบตัวเลข[ด้วยเช่นกันและนั่นไม่ใช่การแสดงออกทางคณิตศาสตร์ แต่ Bash จะบ่นเกี่ยวกับจำนวนเต็มไม่ใช่
ilkkachu

@ilkkachu [เป็นคำสั่งภายนอกมันไม่สามารถเข้าถึงตัวแปรเชลล์ได้ มันมักจะปรับให้เหมาะสมกับคำสั่งในตัว แต่ก็ยังคงทำงานเหมือนเดิม
Barmar

@Barmar สิ่งที่ฉันหมายถึงคือวลี"Arg1 และ arg2 อาจเป็นจำนวนเต็มบวกหรือลบ" ปรากฏในทุบตีเงื่อนไขการแสดงออกและรายการที่นำไปใช้เพียงเป็น[ [[ถึงแม้จะมี[ตัวถูกดำเนินการ-eqและเพื่อน ๆ / ต้องเป็นจำนวนเต็มดังนั้นคำอธิบายจึงใช้ การใช้ "ต้องเป็นจำนวนเต็ม" เพื่อหมายถึง "ถูกตีความว่าเป็นนิพจน์ทางคณิตศาสตร์" ไม่ได้ใช้ในทั้งสองกรณี (อย่างน้อยก็อาจเป็นเพราะการ[แสดงเหมือนคำสั่งปกติอย่างที่คุณพูด)
ilkkachu

1

ใช่การสังเกตของคุณถูกต้องการขยายตัวแปรดำเนินการกับนิพจน์ภายใต้เครื่องหมายวงเล็บเหลี่ยมสองคู่[[ ]]ดังนั้นคุณไม่จำเป็นต้องใส่$ชื่อตัวแปรไว้ข้างหน้า

นี่คือการระบุไว้อย่างชัดเจนในbashคู่มือ:

[[แสดงออก]]

(... ) การแยกคำและการขยายชื่อพา ธ จะไม่ดำเนินการกับคำระหว่าง [[และ]]; การขยายตัวตัวหนอนพารามิเตอร์และการขยายตัวแปรการขยายเลขคณิตการทดแทนคำสั่งการทดแทนกระบวนการและการลบเครื่องหมายถูกดำเนินการ

โปรดสังเกตว่านี่ไม่ใช่กรณีของ single-bracket version [ ]เนื่องจาก[ไม่ใช่คีย์เวิร์ดของเชลล์ (ไวยากรณ์) แต่เป็นคำสั่ง (ใน bash มันคือบิวด์อินเชลล์อื่น ๆ สามารถใช้ภายนอกเพื่อทดสอบ)


1
ขอบคุณสำหรับการตอบกลับ ดูเหมือนว่าจะใช้งานได้กับตัวเลขเท่านั้น x = city [[$ x == city]] สิ่งนี้ใช้ไม่ได้หากไม่มีเครื่องหมาย $
ผู้เยี่ยมชม

3
ดูเหมือนว่าจะมีมากขึ้นที่นี่: (x=1; [[ $x = 1 ]]; echo $?)return 0, (x=1; [[ x = 1 ]]; echo $?)return 1, ie การขยายตัวพารามิเตอร์จะไม่ดำเนินการxเมื่อเราเปรียบเทียบสตริง (x=1; echo $((x+1)))พฤติกรรมลักษณะนี้เช่นการประเมินผลทางคณิตศาสตร์เรียกโดยการขยายตัวทางคณิตศาสตร์คือสิ่งที่เกิดขึ้นใน (เกี่ยวกับการประเมินผลการคำนวณman bashระบุว่า "ภายในนิพจน์ตัวแปรเปลือกอาจถูกอ้างอิงโดยใช้ชื่อโดยไม่ต้องใช้การขยายตัวพารามิเตอร์ไวยากรณ์).
fra ซัง

@ fra-san แท้จริงแล้วเนื่องจาก-gtตัวดำเนินการคาดหวังว่าจำนวนดังนั้นนิพจน์ทั้งหมดจะถูกประเมินอีกครั้งราวกับว่าข้าง(())ในในทางกลับกัน==คาดว่าสตริงดังนั้นแทนที่จะฟังก์ชั่นการจับคู่รูปแบบจะถูกเรียก ฉันไม่ได้ขุดลงในซอร์สโค้ด แต่ฟังดูสมเหตุสมผล
jimmij

[เป็นตัวเชลล์ใน bash
Nizam Mohamed

1
@NizamMohamed มันเป็นตัวภายใน แต่ก็ยังไม่ใช่คำหลัก
Kusalananda
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.