จะรับค่าตัวแปรได้อย่างไรถ้าชื่อตัวแปรถูกเก็บเป็นสตริง?


142

ฉันจะดึงค่าตัวแปร bash ได้อย่างไรถ้าฉันมีชื่อตัวแปรเป็นสตริง?

var1="this is the real value"
a="var1"
Do something to get value of var1 just using variable a.

บริบท:

ฉันมี AMI ( ภาพเครื่องของ Amazon ) บางส่วนและฉันต้องการเริ่มต้นบางกรณีของ AMI แต่ละอัน ทันทีที่บูตเสร็จฉันต้องการตั้งค่าแต่ละอินสแตนซ์ตามประเภท AMI ฉันไม่ต้องการอบสคริปต์หรือคีย์ลับจำนวนมากใน AMI ใด ๆ ดังนั้นฉันจึงเตรียมสคริปต์เริ่มต้นทั่วไปและฉันวางไว้บนS3ด้วยลิงก์ที่สาธารณชนสามารถเข้าถึงได้ ใน rc.local ฉันใส่โค้ดชิ้นเล็ก ๆ ซึ่งดึงข้อมูลสคริปต์เริ่มต้นและเรียกใช้งาน นี่คือทั้งหมดที่ฉันมีใน AMIs จากนั้น AMI แต่ละตัวจะเข้าถึงสคริปต์การกำหนดค่าทั่วไปซึ่งใช้ได้กับ AMI ทั้งหมดและสคริปต์การตั้งค่าพิเศษสำหรับแต่ละตัว สคริปต์เหล่านี้เป็นแบบส่วนตัวและต้องการ URL ที่ลงชื่อเพื่อเข้าถึง

ดังนั้นตอนนี้เมื่อฉันเริ่มต้นอินสแตนซ์ของ AMI (my_private_ami_1) ฉันจะส่ง URL ที่ลงชื่อแล้วสำหรับไฟล์อีกหนึ่งไฟล์ที่แสดงใน S3 ซึ่งมี URL ที่ลงชื่อสำหรับสคริปต์ส่วนตัวทั้งหมดในแง่ของคู่คีย์ / ค่า

config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
เมื่อสคริปต์เริ่มต้นทำงานจะทำการดาวน์โหลดไฟล์ด้านบนและทำการsourceติดตั้ง จากนั้นจะตรวจสอบประเภท AMI และเลือกสคริปต์การตั้งค่าที่ถูกต้องสำหรับตัวเอง

ami\_type=GET AMI TYPE #ex: sets ami\_type to my\_private\_ami\_1
setup\_url=GET THE SETUP FILE URL BASED ON AMI\_TYPE # this is where this problem arises

ดังนั้นตอนนี้ฉันสามารถมีรหัสทั่วไปที่สามารถใช้งานอินสแตนซ์โดยไม่คำนึงถึงประเภท AMI และอินสแตนซ์สามารถดูแลตัวเองได้

คำตอบ:


257

คุณสามารถใช้${!a}:

var1="this is the real value"
a="var1"
echo "${!a}" # outputs 'this is the real value'

นี่คือตัวอย่างของการขยายพารามิเตอร์ทางอ้อม:

${parameter}รูปแบบพื้นฐานของการขยายตัวพารามิเตอร์ ค่าของ parameterถูกแทนที่

หากอักขระตัวแรกของparameterคือเครื่องหมายอัศเจรีย์ (!) อักขระนั้นจะแนะนำระดับการเปลี่ยนทิศทางของตัวแปร Bash ใช้ค่าของตัวแปรที่เกิดขึ้นจากส่วนที่เหลือparameterเป็นชื่อของตัวแปร จากนั้นตัวแปรนี้จะถูกขยายและใช้ค่านั้นในส่วนที่เหลือของการทดแทนมากกว่าค่าของparameterตัวเอง


4
สิ่งนี้ใช้ได้กับฉันใน OSX ทุบตี แต่ไม่ใช่เดเบียน บนเดเบียนฉันได้รับBad substitutionข้อผิดพลาด
23inhouse

11
@ 23inhouse คุณกำลังเรียกใช้สคริปต์ของคุณอยู่/bin/shใช่ไหม ถ้าเป็นเช่นนั้นลองใช้/bin/bashแทน จาก Debian Squeeze เป็นต้นไป/bin/shก็เปลี่ยนไปเป็นsymlink สำหรับdashbashแทน dashไม่รองรับไวยากรณ์นี้โดยเฉพาะและจะส่งออกBad substitutionข้อผิดพลาด
Phil Ross

8
มีวิธีที่จะทำให้งานนี้สำหรับชื่อตัวแปรที่เป็นของอาร์เรย์หรือไม่
Loupax

ใช้งานได้กับ Ubuntu กับทุบตี
Sergey P. aka Azure

1
@DoronShai นี่เป็นตัวอย่างของการขยายพารามิเตอร์ทางอ้อมซึ่งมีการบันทึกไว้ในคู่มืออ้างอิง Bash ที่gnu.org/software/bash/manual/html_node/…
Phil Ross

29
X=foo
Y=X
eval "Z=\$$Y"

ตั้งค่า Z เป็น "foo"

ดูแลการใช้evalตั้งแต่นี้อาจช่วยให้ excution accidential ${Y}รหัสผ่านค่าใน ซึ่งอาจทำให้เกิดอันตรายจากการฉีดรหัส

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

Y="\`touch /tmp/eval-is-evil\`"

/tmp/eval-is-evilจะสร้าง นอกจากนี้ยังอาจจะมีบางrm -rf /แน่นอน


บันทึกหางของฉัน,
ไท

9

แก้ไขคำค้นหาของฉันและรับมัน :)

eval a=\$$a
ขอบคุณที่สละเวลา.


ระมัดระวังการใช้ eval เนื่องจากอาจทำให้เกิดการโค้ดผิดพลาดผ่านค่า${Y}ต่างๆ ดูการเพิ่มของฉันในคำตอบของผู้ใช้ "anon"
ลองจับในที่สุด

นี่เป็นคำตอบเดียวที่ทำงานได้ แดกดันว่ามันเป็นของคุณเอง!
Ctrl S

8

สำหรับผู้ใช้ zsh เพื่อนของฉันวิธีการทำสิ่งเดียวกันกับคำตอบที่ยอมรับคือการใช้:

${(P)a}

มันเรียกว่าการเปลี่ยนชื่อพารามิเตอร์

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

หากใช้กับพารามิเตอร์ที่ซ้อนกันหรือการทดแทนคำสั่งผลลัพธ์ของการนั้นจะถูกนำมาเป็นชื่อพารามิเตอร์ในลักษณะเดียวกัน ตัวอย่างเช่นหากคุณมี 'foo = bar' และ 'bar = baz' สตริง $ {(P) foo}, $ {(P) $ {foo}} และ $ {(P) $ (echo bar) } จะถูกขยายเป็น 'baz'

เช่นเดียวกันถ้าการอ้างอิงซ้อนกันนิพจน์ที่มีแฟล็กจะถูกจัดการเสมือนว่าถูกแทนที่ด้วยชื่อพารามิเตอร์โดยตรง มันเป็นข้อผิดพลาดถ้าการแทนที่ที่ซ้อนกันนี้สร้างอาร์เรย์ที่มีมากกว่าหนึ่งคำ ตัวอย่างเช่นหาก 'name = assoc' โดยที่พารามิเตอร์ assoc เป็นอาร์เรย์ที่เชื่อมโยงดังนั้น '$ {$ {(P) name} [elt]}' หมายถึงองค์ประกอบของการเชื่อมโยงที่เชื่อมโยง 'elt'


0

เชลล์ที่ทันสมัยรองรับอาร์เรย์แล้ว (และแม้กระทั่งอาเรย์เชื่อมโยง) ดังนั้นโปรดใช้พวกเขาและใช้น้อย eval

var1="this is the real value"
array=("$var1")
# or array[0]="$var1"

จากนั้นเมื่อคุณต้องการโทรหา echo $ {array [0]}


มันจะทำงานถ้าฉันได้รับสตริงจากอาร์กิวเมนต์ commandline (เช่น as $1)?
jena

0

ตามคำตอบ: https://unix.stackexchange.com/a/111627

###############################################################################
# Summary: Returns the value of a variable given it's name as a string.
# Required Positional Argument: 
#   variable_name - The name of the variable to return the value of
# Returns: The value if variable exists; otherwise, empty string ("").
###############################################################################
get_value_of()
{
    variable_name=$1
    variable_value=""
    if set | grep -q "^$variable_name="; then
        eval variable_value="\$$variable_name"
    fi
    echo "$variable_value"
}

test=123
get_value_of test
# 123
test="\$(echo \"something nasty\")"
get_value_of test
# $(echo "something nasty")

0

มีปัญหาเดียวกันกับอาร์เรย์นี่คือวิธีการถ้าคุณจัดการอาร์เรย์เช่นกัน:

array_name="ARRAY_NAME"
ARRAY_NAME=("Val0" "Val1" "Val2")

ARRAY=$array_name[@]
echo "ARRAY=${ARRAY}"
ARRAY=("${!ARRAY}")
echo "ARRAY=${ARRAY[@]}"
echo "ARRAY[0]=${ARRAY[0]}"
echo "ARRAY[1]=${ARRAY[1]}"
echo "ARRAY[2]=${ARRAY[2]}"

สิ่งนี้จะออก:

ARRAY=ARRAY_NAME[@]
ARRAY=Val0 Val1 Val2
ARRAY[0]=Val0
ARRAY[1]=Val1
ARRAY[2]=Val2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.