두 개 이상의 변수에 && 및 || 연산자를 올바르게 사용하는 방법은 무엇입니까?

두 개 이상의 변수에 && 및 || 연산자를 올바르게 사용하는 방법은 무엇입니까?

두 개 이상의 변수 상태를 비교하려고 합니다. 사용 사례는 다른 옵션 중에서 사용 가능한 실행 파일을 기반으로 여러 "모드" 중 하나만 선택(또는 자동 선택)해야 하는 스크립트입니다. 일부 구문 다이어그램은 다음과 같습니다.

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

이러한 변수는 명령 인수의 존재 여부에 따라 정의됩니다. 예를 들어 --fzf명령줄 인수에 또는 가 있으면 관련 변수는 "1"로 설정되어 사용자가 스크립트를 실행하려고 함을 나타냅니다. 더 나은 방법 절이 부족합니다.-ffzffzf mode

다음 연산자 명령문은 &&명령줄에 명령 매개변수가 없는 경우 발생하는 상황을 설명합니다. 기본적으로 "모드"를 선택하지 않으면 스크립트는 계층화된 방식으로 사용할 하나의 모드만 자동으로 선택합니다.

다음 연산자 명령문은 ||(사용자가) 패턴을 선택했지만 기본 실행 파일을 찾을 수 없는 경우 발생하는 상황을 다루어야 합니다.

이것은 && 연산자의 원래 버전입니다:

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사용하고 출력을 볼 때 잘못된 값을 보고하는 것 같기 때문입니다).

나 이런 글 알아요이것하나, 하지만 (아직) 두 개 이상의 변수가 있는 예를 찾지 못했습니다(위에서 시도한 것과 같은).

위 원문은 일부 발췌되었습니다.이것스크립트. 기본적으로 에 대한 지원을 추가하려고 합니다. 왜냐하면 에 대한 지원은 및 (위에 표시됨) dmenu만 지원하기 때문입니다 .rofifzf

완전한 수정은 다음과 같습니다스크립트. password-store종속성으로 필요합니다 .

저는 배쉬 5.0.3을 사용하고 있습니다.

두 개 이상의 변수에 && 및 || 연산자를 올바르게 사용하는 방법은 무엇입니까?

답변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. 사실, 저는 사용자에게 두 개의 명령을 내릴 수 있는 가능성조차 주지 않았습니다. 단지 첫 번째 매개변수를 플래그로 읽으면 됩니다. 이제 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

그리고 운영자는 잘 ||작동합니다 &&. 대본 번역 방식에 문제가 있었지만, 계속해서 논리 정리를 요청했지만 답변을 받지 못했습니다. 그래서 복사된 스크립트가 작동하고 있다는 것을 내가 어떻게 이해했는지에 따라 이 질문을 추가했습니다.

한 가지 흥미로운 점은 해당 질문에 게시되지 않은 일부 스크립트가 잘못 번역되었다는 것입니다.

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

다른 문제가 있을 수 있습니다. 모든 관련 정보를 제공하는 것이 중요합니다.

관련 정보