Tên biến đổi nối liền


12

Tôi có thể làm điều này, nhưng nó đòi hỏi phải tạo ra một chuỗi biến sau đó hủy bỏ nó. Có cách nào để rút ngắn nó thành một tuyên bố đơn giản hơn?

#!/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

Tôi đã thử rất nhiều thứ như ${!"${fruit}_COLOUR"}hoặc \$${fruit}_COLOURnhiều biến thể khác, nhưng cách duy nhất có hiệu quả là sử dụng một chuỗi.


Các !cú pháp là một lá cờ trên thay biến mà về cơ bản nói “thay hai lần”. Nó không thay đổi yêu cầu rằng những gì bên trong ${…}phải là một tên biến. Vì vậy, không, bạn không thể tránh sử dụng một biến có chứa tên, trừ khi bạn sử dụng một phương thức khác ( eval).
Gilles 'SO- ngừng trở nên xấu xa'

Bạn cũng có thể sử dụng các mảng kết hợp để làm điều này sạch sẽ hơn nhiều. Tuy nhiên nó không phải là một câu trả lời trực tiếp cho câu hỏi của bạn, vì vậy chỉ cần thêm nó dưới dạng một nhận xét.
Patrick

Câu trả lời:


10

Trước hết, bạn không cần phải sử dụng $(echo $FRUITS)trong fortuyên bố. Sử dụng chỉ $FRUITSlà đủ. Sau đó, bạn có thể loại bỏ một trong các dòng bên trong vòng lặp, bằng cách sử dụng eval.

Việc evalđơn giản chỉ ra bash để thực hiện đánh giá thứ hai cho câu lệnh sau (tức là nhiều hơn so với đánh giá bình thường của nó). Người \$sống sót đánh giá đầu tiên là $, và đánh giá tiếp theo sau đó coi đây $là sự bắt đầu của một tên biến, phân giải thành "Màu vàng", v.v.

Bằng cách này, bạn không cần phải có một bước riêng biệt tạo nên một chuỗi tạm thời (đó là điều tôi tin là mục đích chính của câu hỏi của bạn).

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

Đối với một phương pháp thay thế, như Patrick đã đề cập trong một nhận xét (ở trên), thay vào đó, bạn có thể sử dụng một mảng kết hợp , trong đó chỉ mục của một phần tử không cần phải là một số nguyên. Bạn có thể sử dụng một chuỗi, chẳng hạn như tên của một loại trái cây. Dưới đây là một ví dụ, sử dụng mảng kết hợp của 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

Bạn có thể sử dụng bash-dựng sẵn evalđể làm điều đó:

#!/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

Lưu ý ký hiệu đô la ngược. Về cơ bản, đường dây "eval" gây ra bash thay thế $fruit${fruit_color}sau đó sử dụng evalđể thực hiện một vòng thứ hai của trạm biến áp trước khi gọi echo.


1

Bạn không cần một tuyên bố tệ hại. evallà một gợi ý trong hầu hết các ngôn ngữ mà bạn đang làm sai.

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}"

Tôi cho rằng sẽ an toàn hơn khi sử dụng evalvì ít nhất mọi người biết rằng nó nguy hiểm, ${!var}cũng nguy hiểm bash(cũng là một lỗ hổng tiêm lệnh nếu $chosen_prefixnằm dưới sự kiểm soát của kẻ tấn công) và ít người biết đến nó hơn. Hãy thử var_name='a[$(uname>&2)0]' bash -c 'v=${!var_name}'. Cách tiếp cận tốt nhất ở đây là mảng kết hợp.
Stéphane Chazelas
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.