쉘 스크립트에서 선택 메뉴를 작성하는 방법


134

간단한 bash 스크립트를 만들고 다음과 같이 선택 메뉴를 만들고 싶습니다.

$./script

echo "Choose your option:"

1) Option 1  
2) Option 2  
3) Option 3  
4) Quit  

그리고 사용자의 선택에 따라 다른 작업을 실행하고 싶습니다. 나는 bash 쉘 스크립팅 멍청한 놈입니다. 웹에서 몇 가지 답변을 찾았지만 실제로는 아무것도 얻지 못했습니다.


1
질문은 오래되고 보호되었지만 fzf를 사용합니다. 시도하십시오 seq 10 | fzf. 단점은 기본적으로 fzf가 설치되어 있지 않다는 것입니다. 여기에서 fzf를 찾을 수 있습니다 : github.com/junegunn/fzf
Lynch

답변:


152
#!/bin/bash
# Bash Menu Script Example

PS3='Please enter your choice: '
options=("Option 1" "Option 2" "Option 3" "Quit")
select opt in "${options[@]}"
do
    case $opt in
        "Option 1")
            echo "you chose choice 1"
            ;;
        "Option 2")
            echo "you chose choice 2"
            ;;
        "Option 3")
            echo "you chose choice $REPLY which is $opt"
            ;;
        "Quit")
            break
            ;;
        *) echo "invalid option $REPLY";;
    esac
done

루프를 종료 break해야 할 때마다 명령문을 추가하십시오 select. a break를 수행하지 않으면 select명령문이 반복되고 메뉴가 다시 표시됩니다.

세 번째 옵션에는 select해당 값에 액세스 할 수 있음을 나타 내기 위해 명령문에서 설정 한 변수를 포함 시켰습니다 . 선택하면 다음과 같이 출력됩니다.

you chose choice 3 which is Option 3

$REPLY프롬프트에서 입력 한 문자열 이 포함되어 있음을 알 수 있습니다 . ${options[@]}배열이 1 기반 인 것처럼 배열에 대한 인덱스로 사용됩니다 . 변수 $opt는 배열에서 해당 인덱스의 문자열을 포함합니다.

선택 사항은 다음 select과 같이 명령문 에서 직접 간단한 목록이 될 수 있습니다 .

select opt in foo bar baz 'multi word choice'

그러나 선택 중 하나의 공백으로 인해 이러한 목록을 스칼라 변수에 넣을 수 없습니다.

파일 중에서 선택하는 경우 파일 globbing을 사용할 수도 있습니다.

select file in *.tar.gz

@Abdull : 의도 된 동작입니다. "종료"옵션은 루프 break를 벗어나는를 실행합니다 select. break필요한 곳에 추가 할 수 있습니다 . 배쉬 수동 상태는 "명령은 휴식 명령이있는 선택 명령이 완료 지점, 실행될 때까지 각 선택 후 실행됩니다."
Dennis Williamson

FWIW, GNU bash 4.3.11과 함께 14.04에서 이것을 실행하면 9 행에서 오류가 발생합니다. ./test.sh: line 9: syntax error near unexpected token "Option 1" './test.sh : line 9 :` "Option 1")'`
Brian Morton

@BrianMorton : 나는 그것을 재현 할 수 없습니다. 스크립트 앞부분에 인용 부호 나 다른 문자가 누락되었거나 추가 문자가있을 수 있습니다.
Dennis Williamson

2
@dtmland : PS3select명령 프롬프트입니다 . 자동으로 사용되며 명시 적으로 참조 할 필요가 없습니다. PS3그리고 select문서.
Dennis Williamson

1
@Christian : 아니, 그것은 안 (하지만 난의 인덱스를 사용하는 경우 $optionscase대신 값의 문). 값을 사용하면 case진술 섹션의 기능이 더 잘 문서화된다고 생각 합니다.
데니스 윌리엄슨

56

새로운 답변 자체 는 아니지만 아직 받아 들여진 답변이 없으므로 선택과 정력에 대한 몇 가지 코딩 팁과 요령이 있습니다.

title="Select example"
prompt="Pick an option:"
options=("A" "B" "C")

echo "$title"
PS3="$prompt "
select opt in "${options[@]}" "Quit"; do 

    case "$REPLY" in

    1 ) echo "You picked $opt which is option $REPLY";;
    2 ) echo "You picked $opt which is option $REPLY";;
    3 ) echo "You picked $opt which is option $REPLY";;

    $(( ${#options[@]}+1 )) ) echo "Goodbye!"; break;;
    *) echo "Invalid option. Try another one.";continue;;

    esac

done

while opt=$(zenity --title="$title" --text="$prompt" --list \
                   --column="Options" "${options[@]}"); do

    case "$opt" in
    "${options[0]}" ) zenity --info --text="You picked $opt, option 1";;
    "${options[1]}" ) zenity --info --text="You picked $opt, option 2";;
    "${options[2]}" ) zenity --info --text="You picked $opt, option 3";;
    *) zenity --error --text="Invalid option. Try another one.";;
    esac

done

언급 할 가치가있는 것 :

  • 사용자가 명시 적으로 Quit (또는 zenity로 Cancel)을 선택할 때까지 둘 다 반복됩니다. 이는 대화식 스크립트 메뉴에 적합한 접근 방식입니다. 선택을 선택하고 동작을 수행 한 후 다른 선택을 위해 메뉴가 다시 표시됩니다. 선택은 한 시간이 의미하는 경우에만, 다만 사용 breakesac합니다 (zenity의 접근 방식은 더도 감소 될 수있다)

  • 둘 다 case가치 기반이 아니라 인덱스 기반입니다. 코딩하고 유지하기가 더 쉽다고 생각합니다.

  • 배열은 zenity접근 에도 사용됩니다 .

  • "종료"옵션은 최초의 원래 옵션 중 하나가 아닙니다. 필요할 때 "추가"되므로 어레이가 깨끗하게 유지됩니다. 어쨌든 "종료"는 zenity에 필요하지 않습니다. 사용자는 "취소"를 클릭하거나 창을 닫으면 종료됩니다. 둘 다 동일한 수정되지 않은 옵션 배열을 사용하는 방법에 주목하십시오.

  • PS3그리고 REPLY바르가 할 수 없는 이름을 바꿀 수. select그것들을 사용하도록 하드 코딩되어 있습니다. 스크립트의 다른 모든 변수 (옵션, 옵션, 프롬프트, 제목)는 원하는대로 조정할 수 있습니다.


멋진 설명입니다. 감사합니다. 이 질문은 여전히 ​​Google에서 높은 순위를 차지하므로 너무 닫혀 있습니다.
MountainX

동일한 사용할 수 case에 대한 구조를 select당신이를 위해 사용하고있는 버전 zenity: 버전 case "$opt" in . . . "${options[0]}" ) . . .(대신 $REPLY과 인덱스 1, 23).
Dennis Williamson

@DennisWilliamson, 그렇습니다. "실제"코드에서는 두 경우 모두 동일한 구조를 사용하는 것이 좋습니다. 의도적으로 $REPLY, 색인 및 값 사이의 관계를 보여주고 싶었습니다 .
MestreLion

55

를 사용 dialog하면 명령은 다음과 같습니다.

대화 상자 --clear --backtitle "Backtitle here"--title "Title here"--menu "다음 옵션 중 하나를 선택하십시오."15 40 4 \
1 "옵션 1"
2 "옵션 2"
3 "옵션 3"

여기에 이미지 설명을 입력하십시오

스크립트에 넣는 방법 :

#!/bin/bash

HEIGHT=15
WIDTH=40
CHOICE_HEIGHT=4
BACKTITLE="Backtitle here"
TITLE="Title here"
MENU="Choose one of the following options:"

OPTIONS=(1 "Option 1"
         2 "Option 2"
         3 "Option 3")

CHOICE=$(dialog --clear \
                --backtitle "$BACKTITLE" \
                --title "$TITLE" \
                --menu "$MENU" \
                $HEIGHT $WIDTH $CHOICE_HEIGHT \
                "${OPTIONS[@]}" \
                2>&1 >/dev/tty)

clear
case $CHOICE in
        1)
            echo "You chose Option 1"
            ;;
        2)
            echo "You chose Option 2"
            ;;
        3)
            echo "You chose Option 3"
            ;;
esac

나는 선을 넣어 있습니다 싶습니다 TERMINAL=$(tty)내 스크립트의 상단에 다음에 CHOICE변수 정의 나 변경 2>&1 >/dev/tty하는 2>&1 >$TERMINAL스크립트는 다른 터미널 컨텍스트에서 실행 된 경우 재 지정 문제를 방지 할 수 있습니다.
Shrout1

1
뭐라고합니까 --backtitle매개 변수는 무엇입니까?
TRiG

2
백그라운드에서 블루 스크린의 제목입니다. 스크린 샷의 왼쪽 상단에“여기에서 제목이 있습니다”라고 표시되어 있습니다.
Alaa Ali

14

이 간단한 스크립트를 사용하여 옵션을 만들 수 있습니다

#! / bin / bash
echo "작업을 선택하십시오 ************"
에코 "1) 작업 1"
에코 "2) 작업 2"
에코 "3) 작업 3"
에코 "4) 작업 4" 
읽기 n 사례 $ n 1) echo "옵션 1을 선택했습니다."; 2) echo "선택한 옵션 2";; 3) echo "옵션 3을 선택했습니다";; 4) echo "옵션 4를 선택했습니다"; *) echo "유효하지 않은 옵션";; esac


8

이 답변이 혼합 된 옵션이 하나 더 있지만 좋은 점은 하나의 키만 누르면 스크립트가 계속 -n읽기 옵션 덕분에 계속된다는 것입니다. 이 예제에서는 ANS변수를 사용하여 스크립트를 종료, 재부팅 또는 간단히 종료하라는 메시지가 표시 되며 사용자는 E, R 또는 S 만 누르면됩니다. Enter 키를 누르면 스크립트가 종료되도록 기본값도 설정합니다. 종료됩니다.

read -n 1 -p "Would you like to exit, reboot, or shutdown? (E/r/s) " ans;

case $ans in
    r|R)
        sudo reboot;;
    s|S)
        sudo poweroff;;
    *)
        exit;;
esac

7

이것은 Ubuntu를 대상으로하기 때문에 사용하도록 구성된 백엔드 debconf를 사용해야합니다. 다음과 같이 debconf 백엔드를 찾을 수 있습니다.

sudo -s "echo get debconf/frontend | debconf-communicate"

그것은 "대화"다음 가능성이 사용하는 말한다면 whiptail또는 dialog. Lucid에서는 whiptail입니다.

실패하면 Dennis Williamson의 설명에 따라 bash "select"를 사용하십시오.


3
이것은 아마도 그 질문에 대해서는 과잉 일 것입니다. 그러나 휩 테일과 대화를 언급하면 ​​+1입니다! 나는 그 명령을 몰랐다. .. 매우 달콤하다!
MestreLion

7
#! / bin / sh
show_menu () {
    normal =`에코 "\ 033 [m"`
    menu =`echo "\ 033 [36m"`# 블루
    숫자 =`에코 "\ 033 [33m"`#yellow
    bgred =`에코 "\ 033 [41m"`
    fgred =`에코 "\ 033 [31m"`
    printf "\ n $ {menu} ********************************************* *** $ {normal} \ n "
    printf "$ {menu} ** $ {number} 1) $ {menu} 드롭 박스 마운트 $ {normal} \ n"
    printf "$ {menu} ** $ {number} 2) $ {menu} USB 500 Gig 드라이브 마운트 $ {normal} \ n"
    printf "$ {menu} ** $ {number} 3) $ {menu} Apache $ {normal} \ n"을 다시 시작하십시오.
    printf "$ {menu} ** $ {number} 4) $ {menu} ssh Frost TomCat 서버 $ {normal} \ n"
    printf "$ {menu} ** $ {number} 5) $ {menu} 다른 명령들 {{normal} \ n"
    printf "$ {menu} *********************************************** * $ {normal} \ n "
    printf "메뉴 옵션을 입력하고 종료하려면 $ {fgred} x를 입력하십시오. $ {normal}"
    opt 읽기
}

option_picked () {
    msgcolor =`echo "\ 033 [01; 31m"`# 굵은 빨간색
    normal =`echo "\ 033 [00; 00m"`# 보통 흰색
    message = $ {@ :- "$ {normal} 오류 : 메시지가 전달되지 않았습니다"}
    printf "$ {msgcolor} $ {message} $ {normal} \ n"
}

명확한
show_menu
[$ opt! = ''] 동안
    하다
    [$ opt = ''] 인 경우; 그때
      출구;
    그밖에
      사례 $ opt in
        1) 클리어;
            option_picked "옵션 1 선택";
            printf "sudo mount / dev / sdh1 / mnt / DropBox /; # 3 테라 바이트";
            show_menu;
        ;;
        2) 클리어;
            option_picked "옵션 2 선택";
            printf "sudo mount / dev / sdi1 / mnt / usbDrive; # 500 기가 드라이브";
            show_menu;
        ;;
        3) 클리어;
            option_picked "옵션 3 선택";
            printf "sudo service apache2 restart";
            show_menu;
        ;;
        4) 클리어;
            option_picked "옵션 4 선택";
            printf "ssh lmesser @ -p 2010";
            show_menu;
        ;;
        x) 출구;
        ;;
        \ n) 종료;
        ;;
        *)명확한;
            option_picked "메뉴에서 옵션을 선택하십시오";
            show_menu;
        ;;
      esac
    fi
끝난

2
나는 이것이 오래되었다는 것을 알고 있지만 컴파일하려면 #! / bin / bash를 읽으려면 첫 번째 줄이 필요합니다.
JClar

코드 검토 : 명령문 $$opt변수에서 가 누락되었습니다 while. if문은 중복입니다. 일관되지 않은 들여 쓰기 menu'show_menu . show_menu '가 필요한 곳에서 사용하면 각각 반복되는 대신 루프의 맨 위에 놓을 수 있습니다 case. 일관되지 않은 들여 쓰기 단일 대괄호와 이중 대괄호 사용 혼합. 대신 하드 코딩 된 ANSI 시퀀스를 사용 tput합니다. 모든 대문자 var 이름을 사용하지 않는 것이 좋습니다. FGRED호출되어야합니다 bgred. 대신 백틱을 사용하십시오 $(). 함수 정의는 일관성이 있어야하고 사용하지 않아야합니다.
Dennis Williamson

... 모두 형성됩니다. 터미널 세미콜론은 필요하지 않습니다. 일부 색상 등은 두 번 정의됩니다. 사례 \n는 절대 실행되지 않습니다. 아마도 더.
Dennis Williamson

6

나는 우분투에 항상있는 것처럼 보이는 Zenity를 사용했으며 매우 잘 작동하며 많은 기능을 가지고 있습니다. 가능한 메뉴의 스케치입니다.

#! /bin/bash

selection=$(zenity --list "Option 1" "Option 2" "Option 3" --column="" --text="Text above column(s)" --title="My menu")

case "$selection" in
"Option 1")zenity --info --text="Do something here for No1";;
"Option 2")zenity --info --text="Do something here for No2";;
"Option 3")zenity --info --text="Do something here for No3";;
esac

죄송합니다! 이 스 니펫의 모양, 처음 게시에 대해 유감이며 HTML을 해제해야 할 것 같습니다
LazyEchidna

더 나은 편집에서 '코드 샘플'을 켰습니다. 죄송합니다.
LazyEchidna

5

배쉬 팬시 메뉴

먼저 시도한 다음 자세한 설명을 보려면 내 페이지를 방문하십시오 ... 대화 상자 또는 정력과 같은 외부 라이브러리 또는 프로그램이 필요하지 않습니다 ...

#/bin/bash
# by oToGamez
# www.pro-toolz.net

      E='echo -e';e='echo -en';trap "R;exit" 2
    ESC=$( $e "\e")
   TPUT(){ $e "\e[${1};${2}H";}
  CLEAR(){ $e "\ec";}
  CIVIS(){ $e "\e[?25l";}
   DRAW(){ $e "\e%@\e(0";}
  WRITE(){ $e "\e(B";}
   MARK(){ $e "\e[7m";}
 UNMARK(){ $e "\e[27m";}
      R(){ CLEAR ;stty sane;$e "\ec\e[37;44m\e[J";};
   HEAD(){ DRAW
           for each in $(seq 1 13);do
           $E "   x                                          x"
           done
           WRITE;MARK;TPUT 1 5
           $E "BASH SELECTION MENU                       ";UNMARK;}
           i=0; CLEAR; CIVIS;NULL=/dev/null
   FOOT(){ MARK;TPUT 13 5
           printf "ENTER - SELECT,NEXT                       ";UNMARK;}
  ARROW(){ read -s -n3 key 2>/dev/null >&2
           if [[ $key = $ESC[A ]];then echo up;fi
           if [[ $key = $ESC[B ]];then echo dn;fi;}
     M0(){ TPUT  4 20; $e "Login info";}
     M1(){ TPUT  5 20; $e "Network";}
     M2(){ TPUT  6 20; $e "Disk";}
     M3(){ TPUT  7 20; $e "Routing";}
     M4(){ TPUT  8 20; $e "Time";}
     M5(){ TPUT  9 20; $e "ABOUT  ";}
     M6(){ TPUT 10 20; $e "EXIT   ";}
      LM=6
   MENU(){ for each in $(seq 0 $LM);do M${each};done;}
    POS(){ if [[ $cur == up ]];then ((i--));fi
           if [[ $cur == dn ]];then ((i++));fi
           if [[ $i -lt 0   ]];then i=$LM;fi
           if [[ $i -gt $LM ]];then i=0;fi;}
REFRESH(){ after=$((i+1)); before=$((i-1))
           if [[ $before -lt 0  ]];then before=$LM;fi
           if [[ $after -gt $LM ]];then after=0;fi
           if [[ $j -lt $i      ]];then UNMARK;M$before;else UNMARK;M$after;fi
           if [[ $after -eq 0 ]] || [ $before -eq $LM ];then
           UNMARK; M$before; M$after;fi;j=$i;UNMARK;M$before;M$after;}
   INIT(){ R;HEAD;FOOT;MENU;}
     SC(){ REFRESH;MARK;$S;$b;cur=`ARROW`;}
     ES(){ MARK;$e "ENTER = main menu ";$b;read;INIT;};INIT
  while [[ "$O" != " " ]]; do case $i in
        0) S=M0;SC;if [[ $cur == "" ]];then R;$e "\n$(w        )\n";ES;fi;;
        1) S=M1;SC;if [[ $cur == "" ]];then R;$e "\n$(ifconfig )\n";ES;fi;;
        2) S=M2;SC;if [[ $cur == "" ]];then R;$e "\n$(df -h    )\n";ES;fi;;
        3) S=M3;SC;if [[ $cur == "" ]];then R;$e "\n$(route -n )\n";ES;fi;;
        4) S=M4;SC;if [[ $cur == "" ]];then R;$e "\n$(date     )\n";ES;fi;;
        5) S=M5;SC;if [[ $cur == "" ]];then R;$e "\n$($e by oTo)\n";ES;fi;;
        6) S=M6;SC;if [[ $cur == "" ]];then R;exit 0;fi;;
 esac;POS;done

1
이것은 bash 와만 작동합니다.
Layton Everson

2
좋은! "자세한 설명을 보려면 내 페이지를 방문하십시오"링크가 있습니까?
oak


2

serverfault 에서 이미 같은 질문에 답변했습니다. 이 솔루션은 whiptail을 사용합니다 .


고맙지 만 내 스크립트가 주류 소비를위한 것이므로 추가 의존성이 필요하지 않습니다. 그러나 나는 나중에 사용할 수 있도록 북마크 할 것입니다.
Daniel Rodrigues

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.