特定の順序で配列を繰り返します。

特定の順序で配列を繰り返します。

何かを含めたり含めたりできない配列があり、配列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ネットワークカードが2回リストされたら削除します。

答え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 -aoptionsエンドポイントから返された文字列から配列が生成されます。エンドポイントの出力で対応する文字列が1行にある場合は、最後の行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であることを確認し、その反復をスキップします。

関連情報