2つ以上の変数に&&と||演算子を正しく使用するには?

2つ以上の変数に&&と||演算子を正しく使用するには?

複数の変数の状態を比較しようとしています。ユースケースは、他のオプションから利用可能な実行可能ファイルに基づいて複数の「モード」のうちの1つのみを選択(または自動選択)する必要があるスクリプトです。いくつかの構文図は次のとおりです。

 [ [--fzf,-f]|[--rofi,-r]|[--dmenu,-d] ]

これらの変数は、コマンド引数が存在するかどうかによって定義されます。たとえば、--fzfコマンドライン引数にまたはがある場合、関連変数は "1"に設定され、ユーザーがスクリプトを実行しようとしていることを示します。より良い方法の節が不足しています。-ffzffzf mode

次の演算子ステートメントは、&&コマンドラインにコマンドパラメータがない場合に発生する状況を説明します。デフォルトでは、「モード」を選択しないと、スクリプトは階層化された方法で使用する1つのモードのみを自動的に選択します。

次の演算子ステートメントは、||(ユーザー)パターンを選択したがデフォルトの実行可能ファイルが見つからない場合に発生する状況に対処する必要があります。

これは&&演算子の元のバージョンです:

if [[ $fzf = 0 && $rofi = 0 ]]; then
        if command_exists fzf; then
            fzf=1
        elif command_exists rofi; then
            rofi=1
        fi
    fi

到着

if [[ $fzf = 0 && $rofi = 0 && $dmenu = 0 ]]; then
        if command_exists fzf; then
            fzf=1
        elif command_exists rofi; then
            rofi=1
        elif command_exists dmenu; then
            dmenu=1
        fi
    fi

最後に、||の元のテキストは次のようになります。オペレーター:

if [[ $rofi = 1 || $fzf = 0 ]]; then
        command_exists rofi || die "Could not find rofi in \$PATH"
        menu="$rofi_cmd"
elif [[ $fzf = 1 || $rofi = 0 ]]; then
        command_exists fzf || die "Could not find fzf in \$PATH"
        menu="$fzf_cmd"
else
        die "Could not find either fzf or rofi in \$PATH"
fi

到着

if [[ $rofi = 1 || $fzf = 0 || $dmenu = 0 ]]; then
        command_exists rofi || die "Could not find rofi in \$PATH"
        menu="$rofi_cmd"
    elif [[ $fzf = 1 || $rofi = 0 || $dmenu = 0 ]]; then
        command_exists fzf || die "Could not find fzf in \$PATH"
        menu="$fzf_cmd"
    elif [[ $dmenu = 1 || $rofi = 0 || $fzf = 0 ]]; then
        command_exists dmenu || die "Could not find dmenu in \$PATH"
        menu="$dmenu_cmd"
    else
        die "Could not find either fzf or rofi or dmenu in \$PATH"
    fi

これはエラーを発生させないようですが、これを行う正しい方法ではなく、期待どおりに動作しない可能性があると思います(-x使用して出力を表示したときに誤った値を報告するようです)。

私はこれを知っていますこれしかし、(まだ)2つ以上の変数がある例が見つかりませんでした(上記で試したようなもの)。

上記の原文はいくつか抜粋されました。これスクリプト。基本的にサポートを追加しようとしています。なぜなら、サポートは(上に示されている)とサポートされてdmenuいるからです。rofifzf

完全な修正は次のとおりです。スクリプトpassword-store依存関係として必要です。

私はBash 5.0.3を使用しています。

2つ以上の変数に&&と||演算子を正しく使用するには?

答え1

ユーザーがそれらのうちの1つだけを選択した場合は、変数を使用して選択内容を保持し、各オプションの変数を削除します。とにかく、私たちはすべての組み合わせにあまり興味を持っていません。 3つの中から1つを選択し、「設定解除/デフォルト」に1つを選択するだけです。

だから:

#!/bin/sh
cmd=             # empty value for default
case "$1" in
    --rofi|-r)  cmd=rofi ;;
    --fzf|-f)   cmd=fzf ;;
    --dmenu|-d) cmd=dmenu ;;
esac

if [ -z "$cmd" ]; then
    echo "no command set, looking for default"
    if command_exists fzf; then
        cmd=fzf
    elif
        ...
    fi
fi

echo "running with command $cmd"
# actually do something

ここで選択したパターンはに保存されますcmd。実際、私はユーザーに2つのコマンドを与える可能性さえ与えませんでした。最初のパラメータをフラグとして読むだけです。これで、またはgetoptを使用すると実際には機能しませんが、getoptsモードを設定する前にモードがすでに(再)設定されていることを確認できます。

#!/bin/sh
error_if_cmd_set() {
    if [ -n "$1" ]; then
        echo "command already set"
        exit 1
    fi
}
cmd=
for arg in "$@"; do
    case "$1" in
        --rofi|-r)  error_if_cmd_set "$cmd"; cmd=rofi ;;
        --fzf|-f)   error_if_cmd_set "$cmd"; cmd=fzf ;;
        --dmenu|-d) error_if_cmd_set "$cmd"; cmd=dmenu ;;
    esac
done
# ...
echo "using command $cmd"

答え2

正直なところ、あなたがこのチェックで何をしたいのかわかりません。次のいずれかのプログラムが利用可能であることを確認する必要がある場合:

programs=(fzf rofi dmenu)
available=()

for prog in "${programs[@]}"; do
  location=$(type -P $prog) && available+=("$location")
done

(( ${#available[@]} == 0 )) && die "none of ${programs[*] are available"

# what is your logic for choosing one if multiple are available?
menu=${available[0]}


あなたの意見によれば、ケースブランチでより多くの作業をプッシュする方がきれいになります。

    fzf_exists=0
    rofi_exists=0
    dmenu_exists=0

    # reorder these if you want: the _last_ one to succeed is the default
    if command_exists dmenu; then
        dmenu_exists=1
        menu_default=$dmenu_cmd
    fi
    if command_exists rofi; then 
        rofi_exists=1
        menu_default=$rofi_cmd
    fi
    if command_exists fzf; then 
        fzf_exists=1
        menu_default=$fzf_cmd
    fi

    if [[ -z $menu_default ]]; then
        die "none of fzf, rofi, dmenu are available"
    fi

    while true; do 
        case "$1" in
            -f|--fzf) 
                (( fzf_exists )) || die "fzf is not available"
                [[ -n $menu ]]   && die "choose only one of fzf/rofi/dmenu"
                menu=$fzf_cmd
                shift 
                ;;
            -r|--rofi)
                (( rofi_exists )) || die "rofi is not available"
                [[ -n $menu ]]    && die "choose only one of fzf/rofi/dmenu"
                menu=$rofi_cmd
                shift 
                ;;
            -d|--dmenu)
                (( dmenu_exists )) || die "dmenu is not available"
                [[ -n $menu ]]     && die "choose only one of fzf/rofi/dmenu"
                menu=$dmenu_cmd
                shift 
                ;;
            # ...
        esac
    done

    : ${menu:=$menu_default}

このcommand_exists機能return 1exit 1

答え3

そしてオペレータはうまく||いきます&&。シナリオの翻訳方法に問題がありましたが、引き続き論理的なクリーンアップを要求しましたが、答えを受け取ることができませんでした。そのため、コピーされたスクリプトが機能していることを私がどのように理解したかに応じて、この質問を追加しました。

1つの興味深いことは、その質問に投稿されていないいくつかのスクリプトが間違って翻訳されたということです。

if [[ $fzf = 1 && $rofi = 1 && $dmenu = 1 ]]; then
    die 'Either --fzf,-f or --rofi,-r or --dmenu,-d must be given, not more than one'
fi

簡単に検討すると、声明が次のような内容に従う必要があるという意図があることがわかります。

if [[ $(( $fzf + $rofi + $dmenu )) > 1 ]]; then
    die 'Either --fzf,-f or --rofi,-r or --dmenu,-d must be given, not more than one'
fi

他の問題がある可能性があります。すべての関連情報を提供することが重要です。

関連情報