Liệt kê các biến có tiền tố trong đó tiền tố được lưu trữ trong một biến khác


9

Tôi đang cố gắng liệt kê tất cả các biến có tiền tố nhất định, nhưng tiền tố đó được xác định động. Ví dụ:

prefix=apple_
apple_one=a1
apple_two=a2

Nếu tôi chỉ đơn giản là làm:

echo ${!apple_@}

Tôi có thể lấy tên biến bắt đầu bằng apple_. Tuy nhiên, tôi muốn làm điều này bằng cách sử dụng tiền tố biến bằng cách nào đó. Tất cả mọi thứ tôi đã cố gắng dẫn đến một sự thay thế xấu. Ví dụ tôi không thể:

echo ${!${prefix}@}

1
Tùy thuộc vào vấn đề chính xác của bạn, bạn có thể muốn xem xét sử dụng mảng.
Sparhawk

@Sparhawk Tôi đã suy nghĩ điều tương tự một lúc sau khi tôi gửi câu hỏi. Tôi thực sự đang cố gắng để phát minh lại bánh xe?
Angelo

Tôi chỉ đề cập đến nó bởi vì tôi đã cố gắng làm điều tương tự. Tôi đã tái cấu trúc để sử dụng các mảng, và mặc dù tôi phải sửa đổi những gì tôi đang làm về mặt khái niệm, cuối cùng nó cũng có ý nghĩa hơn. Và dường như ít hack hơn.
Sparhawk

Tôi đã cố gắng tránh các mảng kết hợp bởi vì tôi đã bị bỏng một chút trong thời gian trở lại do các vấn đề về tính di động. (Tôi nghĩ rằng họ đã bắt đầu hỗ trợ cho điều đó trong Bash 4.)
Angelo

Tôi có cùng yêu cầu này, với quy định bổ sung rằng tôi có thể xuất các biến, vì vậy các mảng bash được loại trừ.
philwalk

Câu trả lời:


7

Bash không phân tích mở rộng biến lồng nhau. Bạn có thể làm những gì bạn muốn với eval: xây dựng đoạn mã shell ${!apple_@}, sau đó sử dụng nó trong một lệnh mà bạn thực thi eval.

prefix=apple_
eval 'vars=(${!'"$prefix"'@})'

Đảm bảo rằng prefixchỉ chứa các ký tự định danh hợp lệ, nếu không đoạn trích có thể dẫn đến bất cứ điều gì.

evalthường là cần thiết để làm việc với các biến có tên được tính toán linh hoạt, nhưng trong trường hợp cụ thể này, có một cách hoàn toàn khác để làm điều tương tự: sử dụng hệ thống hoàn thành. Hệ thống hoàn thành của Bash hoạt động ngay cả từ một kịch bản. Nội dung compgenliệt kê một lần hoàn thành trên mỗi dòng, điều này không rõ ràng khi các lần hoàn thành có thể chứa các dòng mới, nhưng đây không phải là trường hợp ở đây - chúng thậm chí không chứa các ký tự đại diện, do đó, đầu ra có thể được phân chia bằng một mở rộng không được trích dẫn đơn giản. Điều này sẽ không xuất ra một cách an toàn nếu tiền tố chứa các ký tự không hợp lệ trong mã định danh.

vars=($(compgen -v "$prefix"))

2

Bạn có thể sử dụng env | grep '^prefix'.

Ví dụ:

$ env | grep '^LESS'
LESSBINFMT=*u.
LESS=-MMqx4ij3
LESSOPEN=| /usr/bin/lesspipe %s
LESSCLOSE=/usr/bin/lesspipe %s %s

Nếu bạn chỉ muốn các giá trị và không phải tên biến, hãy sử dụng awk:

$ env | awk -F= '/^LESS/ {print $2}'
*u.
-MMqx4ij3
| /usr/bin/lesspipe %s
/usr/bin/lesspipe %s %s

(hoặc in $ 1 chỉ cho các tên biến)


Cập nhật 2019-05-17

Tôi đã được đưa trở lại câu trả lời này bởi một upvote và nhận ra rằng có một sự cải thiện rõ ràng:

typeset -phoạt động tốt hơn env, nó xuất ra tất cả các biến cho dù chúng được xuất hay không. Ví dụ:

typeset -p | awk '$3 ~ /^apple_/ { print $3 }'

Để sử dụng một biến chứ không phải là một chuỗi cố định:

prefix=apple_
typeset -p | awk '$3 ~ "^"pfx { print $3 }' pfx="$prefix" 

hoặc chỉ xuất tiền tố $ để nó có sẵn trong mảng ENVIRON của awk:

export prefix=apple_
typeset -p | awk '$3 ~ "^"ENVIRON["prefix"] { print $3 }'

Lưu ý: với một chút công việc, điều này có thể được thực hiện để hoạt động với các shell khác như ksh hoặc zsh, nhưng điều quan trọng cần lưu ý là định dạng đầu ra cho typeset -pkhác với các shell khác, vì vậy tập lệnh awk sẽ phải được sửa đổi cho phù hợp họ


1
Không nếu các biến không được xuất. Và điều này có thể phá vỡ nếu có các biến môi trường có chứa ngắt dòng.
Gilles 'SO- ngừng trở nên xấu xa'

thật. không có gì hoàn hảo, và điều này gần đến mức có thể để đạt được những gì OP muốn. setcũng có thể được sử dụng nếu bạn không bận tâm đến các hàm shell trộn lẫn với các biến của mình.
cas

tốt, nó sẽ không đặt tên hàm nếu bạn bảo nó hành xử:set -o posix; set
mikeerv

@cas Có thể nhận được chính xác những gì OP muốn. Sẽ rất khó nếu bạn muốn một giải pháp POSIX, nhưng ${!prefix}cụ thể là bash ở nơi đầu tiên (thậm chí nó không phải là một tính năng ksh).
Gilles 'SO- ngừng trở nên xấu xa'

typeset -phoạt động tốt hơn env, xuất ra tất cả các biến cho dù chúng được xuất hay không. ví dụ typeset -p | awk '$3 ~ /^BASH/ {print $3}', và tránh việc sử dụng nguy hiểm tiềm tàng eval. Tôi nên nhớ loại đầu tiên khi tôi đăng câu trả lời này. đến một biến chứ không phải là một chuỗi cố định: prefix="BASH"; typeset -p | awk '$3 ~ "^"prefix { print $3}' prefix="$prefix"
cas

0
preset(){
    eval "printf %b $(#"
          set -- "${1##[0-9]*}" _\[:alnum:]
          [ -z   "${1##*[!$2]*}"  ]|| set |
          sed -ne"s/^\($1[$2]*\)=.*/"'"${\1+\1\\n}" \\/p')
"; }; #"

apple_one="a single apple"
apple_two="an apple and an
apple_three='is not actually set at all'
"

preset apple_

apple_one
apple_two
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.