การแยกการอ้างอิงชื่อตัวแปร


12

ฉันสามารถทำสิ่งนี้ได้ แต่มันต้องมีการสร้างสตริงของตัวแปรแล้วทำการพิจารณาอีกครั้ง มีวิธีการย่อให้สั้นลงในคำสั่งที่ง่ายขึ้น

#!/bin/bash

FRUITS="BANANA APPLE ORANGE"

BANANA_COLOUR="Yellow"
APPLE_COLOUR="Green or Red"
ORANGE_COLOUR="Blue"

for fruit in $( echo $FRUITS ); do
    fruit_colour="${fruit}_COLOUR"
    echo $fruit is ${!fruit_colour}
done

ฉันได้ลองหลายสิ่งหลายอย่างเช่น${!"${fruit}_COLOUR"}หรือ\$${fruit}_COLOURตัวแปรอื่น ๆ อีกมากมาย แต่วิธีเดียวที่ทำงานได้คือการใช้สตริง


!ไวยากรณ์ธงแทนค่าตัวแปรนั้นโดยทั่วไปว่า“แทนครั้งที่สองว่า” มันไม่เปลี่ยนความต้องการที่สิ่งที่อยู่ภายใน${…}จะต้องเป็นชื่อตัวแปร ดังนั้นไม่คุณไม่สามารถหลีกเลี่ยงการใช้ตัวแปรที่มีชื่อได้เว้นแต่คุณจะใช้วิธีการอื่น ( eval)
Gilles 'หยุดความชั่วร้าย'

คุณสามารถใช้อาเรย์แบบเชื่อมโยงเพื่อทำสิ่งนี้ได้อย่างหมดจดมากขึ้น อย่างไรก็ตามไม่ใช่คำตอบสำหรับคำถามของคุณโดยตรงดังนั้นเพียงเพิ่มเป็นความคิดเห็น
Patrick

คำตอบ:


11

ก่อนอื่นคุณไม่จำเป็นต้องใช้$(echo $FRUITS)ในforคำสั่ง ใช้แค่ $FRUITSพอ evalจากนั้นคุณสามารถทำไปด้วยหนึ่งในสายภายในวงโดยใช้

evalเพียงแค่บอกทุบตีเพื่อให้เป็นครั้งที่สองการประเมินผลของคำสั่งดังต่อไปนี้ (คือหนึ่งมากกว่าการประเมินผลตามปกติ) การ\$มีชีวิตรอดของการประเมินครั้งแรกเป็น$และการประเมินผลครั้งต่อไปจะถือว่าสิ่งนี้$เป็นจุดเริ่มต้นของชื่อตัวแปรซึ่งจะเปลี่ยนเป็น "สีเหลือง" เป็นต้น

วิธีนี้คุณไม่จำเป็นต้องมีขั้นตอนแยกต่างหากซึ่งสร้างสตริงชั่วคราว (ซึ่งเป็นสิ่งที่ฉันเชื่อว่าเป็นเจตนาหลักของคำถามของคุณ)

for fruit in $FRUITS ;do
    eval echo $fruit is \$${fruit}_COLOUR
done

สำหรับวิธีทางเลือกดังที่Patrickกล่าวไว้ในความคิดเห็น (ด้านบน) คุณสามารถใช้อาเรย์แบบเชื่อมโยงซึ่งดัชนีขององค์ประกอบไม่จำเป็นต้องเป็นจำนวนเต็ม คุณสามารถใช้สตริงเช่นชื่อประเภทผลไม้ นี่คือตัวอย่างการใช้อาเรย์แบบเชื่อมโยงของ bash:

# This declares an associative array (It unsets it if it already exists)
declare -A colour
colour['BANANA']="Yellow"
colour["APPLE"]="Green or Red"
colour[MARTIAN ORANGE]="Blue"

for fruit in BANANA APPLE "MARTIAN ORANGE" ;do 
    echo "$fruit" is "${colour[$fruit]}"
done

6

คุณสามารถใช้ bash-builtin evalเพื่อทำสิ่งนี้:

#!/bin/bash
FRUITS="BANANA APPLE ORANGE"
BANANA_COLOUR="Yellow"
APPLE_COLOUR="Green or Red"
ORANGE_COLOUR="Blue"

for fruit in $( echo $FRUITS );
do
    fruit_colour=${fruit}_COLOUR
    eval echo $fruit is \$${fruit_colour}
done

สังเกตเครื่องหมายแบ็กสแลชดอลลาร์ โดยทั่วไป "EVAL" สาเหตุสายbash การ substitue สำหรับ$fruitและ${fruit_color}แล้วใช้evalในการทำรอบที่สองของ substition echoก่อนที่จะเรียก


1

คุณไม่ต้องการข้อความยืนยัน evalเป็นคำใบ้ในภาษาส่วนใหญ่ที่คุณทำผิด

foo_var=10
bar_var=12

# Build a string with your var name of choice
chosen_prefix='foo'
var_name="${chosen_prefix}_var"

# Use bang to deference it
chossen_value=${!var_name}
echo "Chossen value: ${chossen_value}"

ฉันขอยืนยันว่าปลอดภัยกว่าที่จะใช้evalเพราะอย่างน้อยผู้คนรู้ว่ามันอันตราย${!var}เป็นเรื่องอันตรายในbash(เช่นช่องโหว่การฉีดคำสั่งหาก$chosen_prefixอยู่ภายใต้การควบคุมของผู้โจมตี) และมีคนจำนวนน้อยที่รับรู้ ลองvar_name='a[$(uname>&2)0]' bash -c 'v=${!var_name}'ดู วิธีที่ดีที่สุดที่นี่คืออาเรย์แบบเชื่อมโยง
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.