Truyền mảng kết hợp làm danh sách tham số cho tập lệnh


9

Trong một kịch bản, tôi có một mảng kết hợp như:

declare -A VARS=( ["key1"]="value1" ["key2"]="value" )

Có một lệnh duy nhất để chuyển đổi nó thành một danh sách tham số trong biểu mẫu

--key1=value1 --key2=value2

mà không phải viết lại bằng tay

 --key1="${VARS[key1]}" --key2="${VARS[key2]}"

trường hợp sử dụng mà tôi có trong đầu là chuyển mảng sang tập lệnh dưới dạng danh sách các tham số, như

my_script.sh $(to_param_list $VARS)

Để mở rộng nhận xét tôi đã đưa ra trên câu trả lời @Kusalananda, trường hợp sử dụng chính xác của tôi như sau: Tôi có một tập lệnh được sử dụng để xây dựng trình cài đặt tự giải nén bằng cách sử dụng makeelf và tập lệnh này nhận được một số tham số cần được phân tách giữa:

  • tham số cho chính tập lệnh
  • tham số cho trình cài đặt bên trong trình cài đặt tự giải nén

Các tập lệnh sau đó xây dựng trình cài đặt như thế này:

to_param_list installer_param_list installer_param_array
./makeself ./path/to/sourcedir ./path/to/created/installer "My installer" ./path/to/install/inside/package "${installer_param_list[@]}"

Tuy nhiên, tôi đã kiểm tra tham số truyền với tập lệnh cài đặt rất đơn giản bên trong gói:

while ! -z "$1" ; do
    echo "$1"
    shift
done

và truyền một mảng như:

installer_param_array=( ["upgrade-from"]="19 .2.0" ["upgrade-to"]="19.3.0" )

kết quả trong đầu ra này:

--upgrade-to=19.3.0
--upgrade-from=19
.2.0

Nó không trả lời câu hỏi, nhưng một cách khác (trong bash, một trong các thẻ) là my_script.sh "$(declare -p thearray)$". Trong myscript.sh bạn đọc nó với source /dev/stdin <<<"$1"Sau đó bạn có thearraytrong kịch bản của bạn. Bạn có thể có các đối số khác cùng với mảng. Bạn có thể truyền nhiều biến: my_script.sh "$(declare -p var1 var2 ...)"trong đối số duy nhất này.
Đaminh 108

Câu trả lời:


13

Với chức năng trợ giúp:

#!/bin/bash

to_param_list () {
    declare -n outlist=$1
    declare -n inhash=$2

    for param in "${!inhash[@]}"; do
        outlist+=( "--$param=${inhash[$param]}" )
    done
}

declare -A my_vars=( ["key1"]="value1" ["key2"]="value" )

to_param_list list my_vars
my_script.sh "${list[@]}"

Lệnh cuối cùng trong đoạn script trên sẽ mở rộng tương đương với việc đã viết

my_script.sh "--key2=value" "--key1=value1"

Các to_param_listchức năng lấy tên của một biến mảng và tên của một biến mảng kết hợp và sử dụng chúng để tạo ra hai "tên tài liệu tham khảo" các biến trong hàm (namerefs đã được giới thiệu trong bashphiên bản 4.3). Sau đó chúng được sử dụng để điền vào biến mảng đã cho với các khóa và giá trị ở định dạng thích hợp từ mảng kết hợp.

Vòng lặp trong hàm lặp lại "${!inhash[@]}", đó là danh sách các khóa được trích dẫn riêng lẻ trong mảng kết hợp của bạn.

Khi hàm gọi trở lại, tập lệnh sẽ sử dụng mảng để gọi tập lệnh hoặc lệnh khác của bạn.

Chạy ở trên với

declare -A my_vars=( ["key1"]="hello world" ["key2"]="some thing" ["key3"]="* * *" )

to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"

kịch bản sẽ xuất ra

Arg: --key2=some thing
Arg: --key3=* * *
Arg: --key1=hello world

Điều này cho thấy các tùy chọn được tạo mà không cần tách từ hoặc tên tệp có hiệu lực. Nó cũng cho thấy rằng thứ tự của các khóa có thể không được giữ nguyên do việc truy cập các khóa ra khỏi một mảng kết hợp sẽ làm như vậy theo một thứ tự khá ngẫu nhiên.


Bạn thực sự không thể sử dụng thay thế lệnh một cách an toàn ở đây vì kết quả của nó sẽ là một chuỗi đơn. Nếu không được trích dẫn, chuỗi này sau đó sẽ được phân chia trên các ký tự khoảng trắng (theo mặc định), chuỗi này sẽ chia cả khóa và giá trị của mảng kết hợp của bạn. Shell cũng sẽ thực hiện tên tập tin toàn cầu trên các từ kết quả. Trích dẫn kép thay thế lệnh sẽ không giúp ích vì điều đó sẽ dẫn đến việc gọi bạn my_script.shbằng một đối số duy nhất .


Về vấn đề của bạn vớimakeself :

Các makeselfkịch bản thực hiện điều này với các đối số cho kịch bản cài đặt của bạn:

SCRIPTARGS="$*"

Điều này lưu các đối số dưới dạng một chuỗi trong $SCRIPTARGS(nối, được phân tách bằng dấu cách). Điều này sau đó được chèn vào kho lưu trữ tự giải nén. Để các tùy chọn được phân tích cú pháp chính xác khi chúng được đánh giá lại (chúng là khi chạy trình cài đặt), bạn sẽ phải cung cấp thêm một bộ dấu ngoặc kép trong các giá trị của các tham số để chúng được phân tách chính xác.

installer_param_array=( ["upgrade-from"]="'19 .2.0'" ["upgrade-to"]="'19.3.0'" )

Lưu ý rằng đây không phải là một lỗi trong mã của tôi. Đó chỉ là một tác dụng phụ của makeselfviệc sản xuất mã shell dựa trên các giá trị do người dùng cung cấp.

Lý tưởng nhất là makeselfkịch bản nên viết mỗi đối số được cung cấp với một bộ trích dẫn bổ sung xung quanh chúng, nhưng nó không, có lẽ là do khó biết được tác động nào có thể có. Thay vào đó, nó để lại cho người dùng để cung cấp các báo giá thêm này.

Chạy lại bài kiểm tra của tôi từ trên cao, nhưng bây giờ với

declare -A my_vars=( ["key1"]="'hello world'" ["key2"]="'some value'" ["key3"]="'* * *'" )

to_param_list list my_vars
printf 'Arg: %s\n' "${list[@]}"

sản xuất

Arg: --key2='some value'
Arg: --key3='* * *'
Arg: --key1='hello world'

Bạn có thể thấy rằng các chuỗi này , khi được đánh giá lại bởi trình bao, sẽ không bị phân tách trên các khoảng trắng.

Rõ ràng, bạn có thể sử dụng mảng kết hợp ban đầu của mình và thay vào đó thêm dấu ngoặc kép trong to_param_listhàm bằng cách thay đổi

outlist+=( "--$param=${inhash[$param]}" )

vào

outlist+=( "--$param='${inhash[$param]}'" )

Một trong những thay đổi này đối với mã của bạn sẽ bao gồm các trích dẫn đơn trong các giá trị của các tùy chọn, do đó việc đánh giá lại các giá trị sẽ trở nên cần thiết .


Tôi đã thử giải pháp của bạn, tuy nhiên nếu một trong các giá trị trong mảng kết hợp bắt đầu chứa bất kỳ khoảng
trắng

@MatteoTassinari Không, không. Nếu đúng như vậy, thì bạn đã quên dấu ngoặc kép trong "--$param=${inhash[$param]}"hoặc trong "${list[@]}"hoặc tập lệnh nhận các tùy chọn sẽ làm gì đó sai khi phân tích chúng.
Kusalananda

Tôi có thể xác nhận rằng tôi đã sử dụng các trích dẫn như bạn đang hiển thị, các tham số này được chuyển đến github.com/megastep/makeself để tạo một trình cài đặt, khi thực thi, gọi một tập lệnh với các tham số đã cho, có lẽ trong đoạn này đã xảy ra lỗi
Matteo Tassinari

1
Không, không phải vậy, tôi sẽ cố gắng chỉnh sửa câu hỏi của mình để thể hiện tốt hơn trường hợp sử dụng của mình.
Matteo Tassinari

1
Tôi đã thêm trường hợp sử dụng thực tế của mình và lỗi tôi gặp phải
Matteo Tassinari
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.