$ {! FOO} และ zsh


13

${!FOO}ทำการทดแทนสองครั้งในbashความหมายมันใช้ค่า (สตริง) ของ FOO และใช้มันเป็นชื่อตัวแปร
zshไม่รองรับคุณสมบัตินี้

มีวิธีที่จะทำให้งานนี้ในเดียวกันbashและzsh?

พื้นหลัง:

ฉันมีรายการตัวแปรสภาพแวดล้อมเช่น

PATH MAIL EDITOR

และต้องการพิมพ์ชื่อตัวแปรก่อนหลังจากนั้นค่าของมัน

ใช้งานbashได้ แต่ไม่ใช่zsh:

for VAR in LIST
do
        echo $VAR
        echo ${!VAR}
done

ควรเป็นไปได้ด้วย "วิธีเก่า" ด้วยevalแต่ฉันไม่สามารถทำงาน:

for VAR in LIST
do
        echo $VAR
        echo `eval \$$VAR`
done

ฉันไม่เคยจะเข้าใจว่าทำไมฉันถึงไม่สามารถทำการทดแทนอย่างลึกซึ้งโดยพลการอย่างเช่น${${VAR}}หรือแม้ว่า${${${VAR}}}จำเป็นต้องมีดังนั้นคำอธิบายสำหรับสิ่งนั้นก็จะดีเช่นกัน


1
ฮะ! ฉันคิดว่า zsh ควรจะมีคุณสมบัติเพิ่มเติม
ЯрославРахматуллин

4
ไม่จำเป็นต้องคุยโวbashมันมีฟังก์ชั่นเดียวกัน (และอีกมาก) ใช้รูปแบบที่แตกต่างกัน${(p)FOO}เท่านั้น
Profpatsch

2
@ ЯрославРахматуллин, (e)สถานะการขยายพารามิเตอร์ของ zsh ถูกเพิ่มใน zsh-2.6-beta17 (พฤษภาคม 1996), (P)ตั้งค่าสถานะใน 3.1.5-pws-7 (1999) bash's ${!var}ใน 2.0 (ธันวาคม 1996)
Stéphane Chazelas

คำตอบ:


18

ทั้ง bash และ zsh มีวิธีดำเนินการขยายทางอ้อม แต่ใช้ไวยากรณ์ต่างกัน

ก็พอที่ง่ายต่อการดำเนินการขยายตัวทางอ้อมโดยใช้eval; ใช้งานได้ใน POSIX และเชลล์เป้าหมายส่วนใหญ่ ระมัดระวังในการพูดอย่างถูกต้องในกรณีที่ค่ามีตัวอักษรที่มีความหมายพิเศษในเปลือก

eval "value=\"\${$VAR}\""
echo "$VAR"
echo "$value"

${${VAR}}ไม่ทำงานเพราะไม่ใช่คุณสมบัติที่เชลล์ใช้งาน ${VAR}สิ่งที่อยู่ภายในวงเล็บต้องเป็นไปตามกฎไวยากรณ์ซึ่งไม่รวมถึง (ใน zsh นี่คือไวยากรณ์ที่ได้รับการสนับสนุน แต่ทำสิ่งที่แตกต่าง: การแทนที่แบบซ้อนดำเนินการแปลงอย่างต่อเนื่องบนค่าเดียวกัน; ${${VAR}}เทียบเท่ากับ$VARเนื่องจากสิ่งนี้ทำการแปลงข้อมูลประจำตัวสองครั้งบนค่า)


19

ความคิดเห็นภายใต้คำถามเดิมโดย Profpatsch ให้ตัวอย่าง${(p)FOO}ในการส่งออกเนื้อหาของการอ้างอิงตัวแปรทางอ้อม ธงไม่ถูกต้องหรือพิมพ์ผิดควรเป็นตัวพิมพ์ใหญ่ P และไม่ใช่ตัวพิมพ์เล็ก ${(P)FOO}การใช้งาน:

ต่อไปนี้ควรสร้างผลลัพธ์ที่ต้องการ:

#! /bin/zsh
LIST=(PATH MAIL EDITOR)
for VAR in ${LIST}
do
    print "${VAR}:  ${(P)VAR}"
done

จากหน้าzshexpn ; section - แฟล็กการขยายพารามิเตอร์ :

P

  This forces the value of the parameter name to be interpreted as a further
  parameter name,  whose  value  will  be used where appropriate.  Note that
  flags set with one of the typeset family of commands (in particular case
  transformations) are not applied to the value of name used in this fashion.

  If used with a nested parameter or command substitution, the result of that
  will be taken as a parameter  name  in the  same  way.   For  example,  if
  you  have  `foo=bar'  and `bar=baz', the strings ${(P)foo}, ${(P)${foo}}, and
  ${(P)$(echo bar)} will be expanded to `baz'

มีครั้งหนึ่งที่ฉันอ่านว่าทำไม${${${VAR}}}ไม่สร้างผลลัพธ์ที่คุณคาดหวัง แต่ในเวลานี้ฉันหามันไม่เจอ คุณสามารถทำสิ่งต่อไปนี้:

first="second" ; second="third" ; third="fourth" ; fourth="fifth"
print ${(P)${(P)${(P)first}}}
fifth

3

คุณใช้ eval ไม่ถูกต้อง ในค่าตัวอย่างของคุณเป็น $ VAR นำหน้าด้วย "$" (เช่น `$ VALUE ') จะถูกดำเนินการเป็นคำสั่ง นั่นไม่ใช่สิ่งที่คุณต้องการ คุณต้องการประเมินการขยายตัวของตัวแปรที่มีชื่อมาจากตัวแปรอื่น

$ for i in `echo PATH MAIL EDITOR`; 
    do eval moo="\${$i}" 
    echo $moo 
done
/usr/local/sbin:/usr/local/bin:/usr/sbin:/u (...)
/var/mail/root
nano

0

{ba,z}sh วิธีการแก้

นี่คือฟังก์ชันที่ใช้งานได้ทั้ง {ba, z} sh ฉันเชื่อว่ามันเป็นไปตาม POSIX

มันเตือนเมื่อได้รับ:

  • อินพุตว่าง
  • มีมากกว่าหนึ่งอาร์กิวเมนต์
  • ชื่อตัวแปรที่ไม่ได้ตั้งค่า

# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
  if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one non-empty argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}

ไม่มีfunctionมิได้[[...]]ใน shPOSIX
Stéphane Chazelas

ไม่ควรมีการตรวจสอบอาร์กิวเมนต์[ "$#" -eq 0 ](หรืออาจ[ "$#" -ne 1 ]เป็นเพราะคุณคาดว่าจะมีข้อโต้แย้งเดียว)
Stéphane Chazelas

@ StéphaneChazelasขอบคุณอัปเดตแล้ว มีเชลล์อ้างอิงสำหรับการปฏิบัติตาม POSIX หรือไม่ ฉันคิดว่าshell-checkอาจช่วยด้วย
Tom Hale

@ StéphaneChazelasการทดสอบอีกครั้งฉันต้องการที่จะล้มเหลวเมื่อได้รับอาร์กิวเมนต์เป็นโมฆะ ฉันได้เพิ่มข้อเสนอแนะที่สองของคุณ ไชโย!
Tom Hale

ผมเห็นด้วยกับข้อความ${1-}ที่กำหนด==a='"" -o -n 1' [ -z $a ] 0แต่จะไม่$#เป็นโทเค็นเดียวเสมอหรือ
Tom Hale
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.