$ {! FOO} và zsh


13

${!FOO}thực hiện thay thế kép bash, nghĩa là nó lấy giá trị (chuỗi) của FOO và sử dụng nó làm tên biến.
zshkhông hỗ trợ tính năng này.

Có cách nào để làm cho công việc này giống nhau trong bashzshkhông?

Lý lịch:

Tôi đã có một danh sách các biến môi trường, như

PATH MAIL EDITOR

và trước tiên muốn in tên biến và sau đó là giá trị của chúng.

Điều này hoạt động trong bashnhưng không zsh:

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

Nó có thể bằng cách nào đó có thể là cách khác với cách cũ eval, nhưng tôi không thể làm cho nó hoạt động được:

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

Tôi sẽ không bao giờ hiểu lý do tại sao tôi không thể đơn giản thực hiện các thay thế sâu tùy ý như ${${VAR}}hoặc thậm chí ${${${VAR}}}nếu cần, vì vậy một lời giải thích cho điều đó cũng tốt.


1
HAH! Tôi nghĩ zsh được cho là có nhiều tính năng hơn.
Ярослав Рахматуллин

4
Không cần phải khoe khoang bash, nó thực sự có cùng chức năng (và nhiều hơn nữa), chỉ sử dụng một mô hình khác, cụ thể là ${(p)FOO}.
Profpatsch

2
@ ЯрославРахматуллин, zsh của (e)cờ mở rộng tham số đã được bổ sung trong zsh-2,6-beta17 (tháng 5 năm 1996), các (P)lá cờ ở 3.1.5-PWS-7 (1999). bash's ${!var}in 2.0 (tháng 12 năm 1996).
Stéphane Chazelas

Câu trả lời:


18

Cả bash và zsh đều có cách thực hiện mở rộng gián tiếp, nhưng chúng sử dụng cú pháp khác nhau.

Nó đủ dễ dàng để thực hiện mở rộng gián tiếp bằng cách sử dụng eval; điều này hoạt động trong tất cả các vỏ POSIX và hầu hết Bourne. Hãy cẩn thận để trích dẫn đúng trong trường hợp giá trị chứa các ký tự có ý nghĩa đặc biệt trong vỏ.

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

${${VAR}}không hoạt động vì nó không phải là một tính năng mà bất kỳ shell nào thực hiện. Điều bên trong niềng răng phải tuân thủ các quy tắc cú pháp không bao gồm ${VAR}. (Trong zsh, đây là cú pháp được hỗ trợ, nhưng thực hiện một điều khác biệt: các thay thế lồng nhau thực hiện các phép biến đổi liên tiếp trên cùng một giá trị; ${${VAR}}tương đương với $VARviệc này thực hiện chuyển đổi nhận dạng hai lần trên giá trị.)


19

Nhận xét, theo câu hỏi ban đầu, bởi Profpatsch đưa ra ví dụ ${(p)FOO}để xuất nội dung của một tham chiếu biến gián tiếp. Cờ không chính xác hoặc lỗi chính tả, nó phải là chữ P viết hoa và không phải là chữ thường p. Sử dụng : ${(P)FOO}.

Sau đây sẽ tạo ra đầu ra mong muốn:

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

Từ trang người đàn ông zshExn ; phần - Cờ mở rộng tham số :

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'

Đã có lúc tôi đọc được tại sao ${${${VAR}}}không tạo ra kết quả mà bạn mong đợi, nhưng tại thời điểm này tôi không thể tìm thấy nó. Bạn có thể làm một cái gì đó như sau:

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

3

Bạn không sử dụng eval chính xác. Trong ví dụ của bạn, giá trị $ VAR có trước "$" (nghĩa là '$ VALUE') sẽ được thực thi như một lệnh. Đó không phải là những gì bạn muốn. Bạn muốn đánh giá sự mở rộng của một biến có tên được lấy từ một biến khác.

$ 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 giải pháp

Đây là một hàm hoạt động trong cả {ba, z} sh. Tôi tin rằng nó cũng tuân thủ POSIX.

Nó cảnh báo khi được đưa ra:

  • Đầu vào không
  • Nhiều hơn một đối số
  • Một tên biến không được đặt

# 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?}\""
}

Không có functioncũng như [[...]]trong POSIX sh.
Stéphane Chazelas

Kiểm tra không có đối số nên với [ "$#" -eq 0 ](hoặc có thể [ "$#" -ne 1 ]như bạn chỉ mong đợi một đối số).
Stéphane Chazelas

@ StéphaneChazelas Cảm ơn, đã cập nhật. Có một vỏ tham chiếu cho tuân thủ POSIX? Tôi đang nghĩ shell-checkcó thể giúp quá.
Tom Hale

@ StéphaneChazelas Thử nghiệm lại, tôi cũng muốn thất bại khi đưa ra một đối số null duy nhất. Tôi đã thêm đề xuất thứ 2 của bạn. Chúc mừng!
Tom Hale

Tôi đồng ý với trích dẫn đã ${1-}cho a='"" -o -n 1' [ -z $a ]== 0. Nhưng sẽ không $#luôn là một mã thông báo duy nhất?
Tom Hale
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.