bash 처리 후 매개변수에서 옵션을 제거하는 방법

bash 처리 후 매개변수에서 옵션을 제거하는 방법

위치 매개변수 목록을 가져 와서 반복하고, 매개변수가 있는 플래그와 옵션을 구문 분석하고, 구문 분석 후 이를 제거하여 기본 매개변수만 남겨두는 bash스크립트를 어디 선가 본 기억이 납니다. 그런 다음 스크립트에 의해 처리됩니다.caseshift

예를 들어, 명령줄을 구문 분석할 때 cp -R file1 -t /mybackup file2 -f먼저 매개변수를 반복하고, 사용자가 디렉토리에 드롭을 요청했는지 식별하고 -R, 대상을 지정 -t /mybackup하고 강제로 복사한 다음 매개변수 목록에서 해당 매개변수를 제거하고 프로그램을 다음과 같이 -f남겨둡니다. file1 file2처리할 나머지 매개변수.

하지만 매번 보는 스크립트를 기억하거나 알아낼 수 없는 것 같습니다. 나는 단지 그렇게 할 수 있기를 원합니다. 나는 다양한 사이트를 인터넷 검색하고 내가 확인한 관련 페이지 목록을 첨부했습니다.

사이트의 한 질문은 "순서 독립적 옵션"에 대해 구체적으로 질문했지만 단일 답변이나 복제된 질문에 대한 답변은 옵션이 일반적인 인수와 혼합되는 위와 같은 상황을 고려하지 않습니다. 이 사람의 경우 특별 언급 이유순서와 상관없이옵션.

bash내장 함수는 옵션이 아닌 첫 번째 인수에서 멈추는 것 같아서 getopts해결책으로는 충분하지 않은 것 같습니다. 이것이 Wooledge BashFAQ 페이지(아래 참조)에서 매개변수를 재정렬하는 방법을 설명하는 이유입니다. 하지만 매개변수 목록이 매우 긴 경우에는 여러 배열을 만드는 것을 피하고 싶습니다.

shift매개변수 목록 중간에서 단일 매개변수를 표시하는 것은 지원되지 않으므로 내가 원하는 것을 달성하는 간단한 방법이 무엇인지 잘 모르겠습니다 .

완전히 새로운 배열을 만들지 않고도 매개변수 목록의 중간에서 매개변수를 제거할 수 있는 솔루션이 있는지 듣고 싶습니다.

이미 본 페이지:

답변1

POSIXly, 옵션 구문 분석은 --첫 번째 비옵션(또는 비옵션 매개변수) 인수 중 먼저 오는 것에서 중지되어야 합니다. 그래서

cp -R file1 -t /mybackup file2 -f

file1에 있으므로 cp모든 file1, -t및 가 디렉터리 에 /mybackup반복적으로 복사되어야 합니다 .file2-f

그러나 GNU getopt(3)( 옵션을 구문 분석하는 데 사용됨(여기서는 GNU 특정 옵션을 사용하기 때문에 cpGNU를 사용함 ))는 환경 변수가 설정되지 않은 한 인수 뒤에 옵션을 허용합니다. 따라서 실제로 POSIX 옵션 스타일 구문 분석과 동일합니다.cp-t$POSIXLY_CORRECT

cp -R -t /mybackup -f -- file1 file2

getoptsGNU 쉘( )에서도 내장 쉘은 bashPOSIX 버전만 처리합니다. 또한 긴 옵션이나 선택적 매개변수가 있는 옵션도 지원하지 않습니다.

GNU와 같은 옵션을 구문 분석하려면 cpGNU API를 사용해야 합니다 getopt(3). 이렇게 하려면 Linux의 경우 다음을 사용할 수 있습니다 getopt(util-linux이 명령 getopt의 향상된 버전또한 다른 Unices에도 포팅되었습니다.FreeBSD).

이렇게 하면 getopt옵션이 정식 방식으로 재정렬되어 루프를 통해 간단히 구문 분석할 수 있습니다 while/case.

$ getopt -n "$0" -o t:Rf -- -Rf file1 -t/mybackup file2
 -R -f -t '/mybackup' -- 'file1' 'file2'

일반적으로 다음과 같이 사용합니다.

parsed_options=$(
  getopt -n "$0" -o t:Rf -- "$@"
) || exit
eval "set -- $parsed_options"
while [ "$#" -gt 0 ]; do
  case $1 in
    (-[Rf]) shift;;
    (-t) shift 2;;
    (--) shift; break;;
    (*) exit 1 # should never be reached.
  esac
done
echo "Now, the arguments are $*"

또한 getoptGNU와 동일한 방식으로 옵션을 구문 분석한다는 점에 유의하세요 cp. 특히 GNU 와 마찬가지로 긴 옵션을 지원하고(축약된 형식으로 입력) $POSIXLY_CORRECT환경 변수를 존중합니다(설정 시 인수 후 옵션에 대한 지원이 비활성화됨) cp.

gdb를 사용하고 수신된 매개변수를 인쇄하면 getopt_long()매개변수를 작성하는 데 도움이 될 수 있습니다 getopt(1).

(gdb) bt
#0  getopt_long (argc=2, argv=0x7fffffffdae8, options=0x4171a6 "abdfHilLnprst:uvxPRS:T", long_options=0x417c20, opt_index=0x0) at getopt1.c:64
(gdb) set print pretty on
(gdb) p *long_options@40
$10 = {{
    name = 0x4171fb "archive",
    has_arg = 0,
    flag = 0x0,
    val = 97
  }, {
    name = 0x417203 "attributes-only",
[...]

getopt그런 다음 다음과 같이 사용할 수 있습니다 .

getopt -n cp -o abdfHilLnprst:uvxPRS:T -l archive... -- "$@"

cpGNU에서 지원하는 옵션 목록은 버전마다 변경될 수 있으며 옵션에 합법적인 값을 전달하는지 getopt확인할 방법이 없다는 점을 명심하세요 .--sparse

답변2

따라서 인수가 처리될 때마다 처리해야 하는 인수 목록의 다음 숫자로 쉘 변수를 설정하고 0이 아닌 값을 반환할 getopts것으로 예상하지 않습니다 . 값을 1로 설정 $OPTIND하면 POSIX는 새 매개변수 목록이 허용되도록 지정합니다. 따라서 이는 단지 반환을 관찰하고, 실패한 반환에 대한 증가 카운터 더하기 기호를 저장하고 , 실패한 매개변수를 제거하고, 실패한 각 시도를 재설정합니다. 루프를 사용자 정의하거나 변수 에 저장하고 해당 부분을 .$OPTINDgetoptsgetopts$OPTIND$OPTINDopts "$@"caseeval $case

opts() while getopts :Rt:f opt || {
             until command shift "$OPTIND" || return 0
                   args=$args' "${'"$((a=${a:-0}+$OPTIND))"'}"'
                   [ -n "${1#"${1#-}"}" ]
             do OPTIND=1; done 2>/dev/null; continue
};     do case "$opt"  in
             R) Rflag=1      ;;
             t) tflag=1      ;
                targ=$OPTARG ;;
             f) fflag=1      ;;
       esac; done

$args런타임 시 처리되지 않은 모든 인수로 설정됩니다 getopts... 그래서...

set -- -R file1 -t /mybackup file2 -f
args= opts "$@"; eval "set -- $args"
printf %s\\n "$args"
printf %s\\n "$@"         
printf %s\\n "$Rflag" "$tflag" "$fflag" "$targ"

산출

 "${2}" "${5}"
file1
file2
1
1
1
/mybackup

이것은 bash, dash, zsh, ksh93, mksh...에 작동합니다. 글쎄, 나는 그 시점에서 시도를 포기했습니다. 각 셸에는 $[Rtf]flag및 도 포함되어 있습니다 $targ. 중요한 점은 getopts처리하고 싶지 않은 매개변수의 모든 숫자가 유지된다는 것입니다.

옵션 스타일을 변경해도 아무런 차이가 없습니다. 이는 -Rf -t/mybackup또는 와 유사하게 작동합니다 -R -f -t /mybackup. 목록 중간, 목록 끝 또는 목록 시작 부분에서 작동합니다.

그럼에도 불구하고,가장 좋은 방법매개변수 목록 끝에 for 옵션을 붙여넣고 --처리 shift "$(($OPTIND-1))"실행이 끝나면 실행하세요 getopts. 이 방법으로 처리된 모든 매개변수를 제거할 수 있습니다.그리고매개변수 목록의 끝을 유지합니다.

제가 좋아하는 것 중 하나는 장기 옵션을 단기 옵션으로 전환하는 것입니다. 저는 이 작업을 매우 비슷한 방식으로 수행하므로 이 답변을 쉽게 얻을 수 있습니다.앞으로난 달린다 getopts.

i=0
until [ "$((i=$i+1))" -gt "$#" ]
do case "$1"                   in
--Recursive) set -- "$@" "-R"  ;;
--file)      set -- "$@" "-f"  ;;
--target)    set -- "$@" "-t"  ;;
*)           set -- "$@" "$1"  ;;
esac; shift; done

관련 정보