특정 순서로 배열을 반복합니다.

특정 순서로 배열을 반복합니다.

무언가를 포함하거나 포함하지 않을 수 있는 배열이 있고 BMC배열의 각 요소를 반복하고 있습니다. 루프의 마지막 반복이 되고 BMC싶은 내용이 포함되어 있는 경우 .BMC

배열은 명령줄 옵션을 사용하여 생성되므로 큰 변경 없이는 생성 순서를 강제할 수 없을 것 같습니다.

예:

#!/usr/bin/env bash

while getopts cnBba opt; do
    case $opt in
        c)  options+=(CPLD);;
        n)  options+=(NIC);;
        B)  options+=(BMC);;
        b)  options+=(BIOS);;
        a)  all=true;;
    esac
done


# This is probably really confusing because it's redacted but this script also
# allows for -a to be used and then I'll query an endpoint that gives me a list
# of all available firmware for the given server.  It could return some or all
# of the possible options in no particular order.
mapfile -t types < <(curl some_firmware_endpoint)
if [[ $all == true ]]; then
    options=($(printf '%s\n' "${types[@]}" NIC | sort -u))
fi

for opt in "${options[@]}"; do
    echo "option is $opt"
done

다음과 같은 출력이 생성됩니다.

$ ./script.sh -Bbcn
option is BMC
option is BIOS
option is CPLD
option is NIC

하지만 나는 다음과 같은 출력을 원합니다.

$ ./script.sh -Bbcn
option is BIOS
option is CPLD
option is NIC
option is BMC

해결책이 있지만 배열에 빈 요소가 생성됩니다. 추가로 처리할 수 있다고 확신하지만 더 정확한 방법이 있을 수 있습니다.

내 솔루션:

#!/usr/bin/env bash

while getopts cnBba opt; do
    case $opt in
        c)  options+=(CPLD);;
        n)  options+=(NIC);;
        B)  options+=(BMC);;
        b)  options+=(BIOS);;
        a)  all=true;;
    esac
done


# This is probably really confusing because it's redacted but this script also
# allows for -a to be used and then I'll query an endpoint that gives me a list
# of all available firmware for the given server.  It could return some or all
# of the possible options in no particular order.
mapfile -t types < <(curl some_firmware_endpoint)
if [[ $all == true ]]; then
    options=($(printf '%s\n' "${types[@]}" NIC | sort -u))
fi

if [[ "${options[@]}" =~ BMC ]]; then
    options=("${options[@]/BMC/}" BMC)
fi

for opt in "${options[@]}"; do
    echo "option is $opt"
done

다음을 생성합니다.

$ ./script.sh -Bbcn
option is
option is BIOS
option is CPLD
option is NIC
option is BMC

-a 모든 옵션:

all 옵션은 서버 유형에 대한 펌웨어 목록이 있는 엔드포인트(기본적으로 json 개체)를 쿼리합니다. 일부 서버에서는 해당 값만 반환할 수도 있고 BIOS BMC일부 서버에서는 BIOS BMC NIC CPLD SATA StorageController더 많은 값을 반환할 수도 있습니다. json 개체가 구성되는 방식으로 인해 대부분은 NIC 펌웨어를 별도로 나열하므로 -a를 사용할 때 해당 오류를 수동으로 추가하고 해당 특정 NIC에 대한 펌웨어가 없는 경우 해당 오류를 별도로 처리한 다음 그 시점부터 처리합니다.일부거기에 네트워크 카드도 있습니다. sort -u네트워크 카드가 두 번 나열되면 삭제하겠습니다.

답변1

나중에 배열을 만듭니다. 이 옵션을 사용하는 경우 -aenpoint를 호출하고 BMC마지막 문자열 재정렬을 사용합니다 sed. 사용하지 않으면 주어진 명령줄 옵션에서 배열을 만듭니다.

(참고: 여기서는 들여쓰기를 위해 리터럴 탭을 사용하고 있습니다. 여기서는 공백이 있으면 문서가 제대로 작동하지 않습니다. 또는 수동으로 들여쓰기를 제거할 수 있습니다.)

#!/bin/bash

# Fake endpoint response.
get_valid () {
        cat <<-END_LIST
                BIOS
                BMC
                tiny green peas
                CPLD
                MICROBE
        END_LIST
}

a_opt=false
unset -v c_opt n_opt B_opt b_opt

while getopts acnBb opt; do
        case $opt in
                a)      a_opt=true      ;;
                c)      c_opt=1 ;;
                n)      n_opt=1 ;;
                B)      B_opt=1 ;;
                b)      b_opt=1 ;;
                *)      :       # error handling here
        esac
done


if "$a_opt"; then
        readarray -t options < <(
                get_valid |
                sed -e '/^BMC$/{ h; d; }' \
                    -e '${ p; g; /^$/d; }'
        )
else
        options=(
                ${c_opt:+CPLD}
                ${n_opt:+NIC}
                ${b_opt:+BIOS}
                ${B_opt:+BMC}
        )
fi

printf 'Option: "%s"\n' "${options[@]}"

Without -a: options위 코드의 배열은 할당에 나열된 순서대로 각 구문 분석 옵션에 해당하는 문자열을 사용하여 생성됩니다. 문자열은 해당 변수가 설정된 경우에만 배열에 포함됩니다 _opt(실제 값은 중요하지 않음).

With -a: options끝점에서 반환된 문자열에서 배열이 생성됩니다. 엔드포인트의 출력에서 ​​해당 문자열이 한 줄에 있는 경우 BMC마지막 줄이 처리될 때까지 예약된 공간에 저장하여 끝으로 이동합니다.

$ bash script -bBcn
Option: "CPLD"
Option: "NIC"
Option: "BIOS"
Option: "BMC"
$ bash script -a
Option: "BIOS"
Option: "tiny green peas"
Option: "CPLD"
Option: "MICROBE"
Option: "BMC"

답변2

특별한 경우는 특별하며 루프 후에 수동으로 쫓아낼 수 있습니다.

...
handle_opt() {
    echo "option is $1"
}

do_BMC=0
for opt in "${options[@]}"; do
    if [[ $opt = BMC ]]; then do_BMC=1; continue; fi
    handle_opt "$opt"
done

if [[ $do_BMC= 1 ]]; then
    handle_opt "BMC"
fi

또는 옵션이 상수 배열에서 처리되는 순서를 수정하고 연관 배열을 사용하여 어떤 옵션이 활성화되었는지 알려줍니다. 이는 외부 프로그램에서 입력을 읽는 것이 그렇게 간단하지 않다는 것을 의미합니다 mapfile. (이것은 Kusalananda의 솔루션의 변형으로 볼 수 있습니다.)

테스트되지 않았지만 다음과 같은 아이디어를 얻을 수 있습니다.

order=(CPLD BIOS NIC BMC)  # keep BMC last
declare -A options         # associative array

while getopts cnBba opt; do
    case $opt in
        c)  options[CPLD]=1 ;;
        n)  options[NIC]=1  ;;
        B)  options[BMC]=1  ;;
        b)  options[BIOS]=1 ;;
        a)  all=true ;;
    esac
done

if [[ $all == true ]]; then
    while IFS= read -r line; do
        options[$line]=1;
    done < <(curl some_firmware_endpoint)
fi

for opt in "${order[@]}"; do
    if [[ "${options[$opt]}" != 1 ]]; then continue; fi

    handle_opt "$opt"
done

답변3

B옵션을 반복한 다음 options[]나중에 스칼라 변수를 사용하여 채울 때 이 결과를 얻을 수 있다는 점에 유의하세요 .

$ cat script.sh
#!/usr/bin/env bash

while getopts cnBba opt; do
    case $opt in
        c)  options+=(CPLD);;
        n)  options+=(NIC);;
        B)  gotB=1;;
        b)  options+=(BIOS);;
        a)  all=true;;
    esac
done

# mapfile and it's associated loop would go here

(( gotB )) && options+=(BMC)

for opt in "${options[@]}"; do
    echo "option is $opt"
done

$ ./script.sh -Bbcn
option is BIOS
option is CPLD
option is NIC
option is BMC

$ ./script.sh -bcn
option is BIOS
option is CPLD
option is NIC

또는 bash 버전이 이를 지원하는 경우 -v별도의 옵션 배열을 유지 getopts하고 B그 안에서 인덱싱을 테스트할 수 있습니다.

$ cat script.sh
#!/usr/bin/env bash

declare -A opts

while getopts cnBba opt; do
    opts["$opt"]=''
    case $opt in
        c)  options+=(CPLD);;
        n)  options+=(NIC);;
        b)  options+=(BIOS);;
        a)  all=true;;
    esac
done

# mapfile and it's associated loop would go here

[[ -v opts[B] ]] && options+=(BMC)

for opt in "${options[@]}"; do
    echo "option is $opt"
done

또는 먼저 다른 배열의 끝에 추가하려는 옵션을 저장한 다음 options[]해당 배열에서 채웁니다.

$ cat script.sh
#!/usr/bin/env bash

while getopts cnBba opt; do
    case $opt in
        c)  options+=(CPLD);;
        n)  options+=(NIC);;
        B)  deferred+=(BMC);;
        b)  options+=(BIOS);;
        a)  all=true;;
    esac
done

# mapfile and it's associated loop would go here

for opt in "${deferred[@]}"; do
    options+=("$opt")
done

for opt in "${options[@]}"; do
    echo "option is $opt"
done

주석에서 언급한 대로 mapfile위 스크립트 사이와 스크립트에 연관된 루프를 추가하십시오.while getoptsBoptions[]

답변4

답변해 주셔서 감사합니다. 저는 결국 이렇게 선택했습니다.

#!/usr/bin/env bash

while getopts cnBba opt; do
    case $opt in
        c)  options+=(CPLD);;
        n)  options+=(NIC);;
        B)  options+=(BMC);;
        b)  options+=(BIOS);;
        a)  all=true;;
    esac
done

mapfile -t types < <(curl some_firmware_endpoint)
if [[ $all == true ]]; then
    options=($(printf '%s\n' "${types[@]}" NIC | sort -u))
fi

if [[ "${options[@]}" =~ BMC ]]; then
    options=("${options[@]/BMC/}" BMC)
fi

for opt in "${options[@]}"; do
    [[ -z "$opt" ]] && continue
    echo "option is $opt"
done

옵션에 포함된 경우 BMC제거된 다음 끝에서 읽습니다. 그러면 null 요소가 생성되므로 루프에서 해당 요소가 null인지 확인하고 해당 반복을 건너뜁니다.

관련 정보