다양한 옵션을 허용하는 쉘 스크립트를 작성하려고 하는데 getopts
옵션과 인수의 가변 순서를 처리할 수 있기 때문에 좋은 솔루션인 것 같습니다.
나는 다음과 같이 각 옵션에 해당 값이 필요한 짧은 옵션만 사용하겠습니다. ./command.sh -a arga -g argg -b argb
그러나 옵션을 비특정 순서로 입력할 수 있도록 허용하고 싶습니다. 이는 대부분의 사람들이 셸 명령을 사용하는 데 익숙합니다.
case
또 다른 점은 옵션 매개변수 값을 직접, 가급적이면 명세서 에서 확인하고 싶다는 점이다 . 그 이유는 진술 :)
에 대한 나의 테스트가 일관성 없는 결과를 낳았기 때문입니다(아마도 나의 이해 부족 때문일 것입니다). 예를 들어:case
#!/bin/bash
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
;;
u)
MYSQL_USER=$OPTARG
;;
p)
MYSQL_PASS=$OPTARG
;;
d)
BACKUP_DIR=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
무슨 일이 일어나는지는 이것이 실패한다는 것입니다... -d에 인수가 필요하지만 내가 얻는 값이 내가 필요한 값이 아니라는 ./command.sh -d -h
플래그를 지정하고 싶을 때입니다 .-d=-h
그래서 각 옵션이 설정되어 있고 한 번만 설정되었는지 확인하기 위해 Case 문에서 자체 유효성 검사를 실행하는 것이 더 쉬울 것이라고 생각했습니다.
다음을 수행하려고 하는데 내 if [ ! "$MYSQL_HOST" ]; then
블록이 실행되지 않습니다.
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
if [ ! "$MYSQL_HOST" ]; then
echo "host not set"
exit 2
fi
;;
u)
MYSQL_USER=$OPTARG
if [ ! "$MYSQL_USER" ]; then
echo "username not set"
exit 2
fi
;;
p)
MYSQL_PASS=$OPTARG
if [ ! "$MYSQL_PASS" ]; then
echo "password not set"
exit 2
fi
;;
d)
BACKUP_DIR=$OPTARG
if [ ! "$BACKUP_DIR" ]; then
echo "backup dir not set"
exit 2
fi
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
#:)
# echo "Option -$opt requires an argument" >&2
# exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
OPTARG
an 안의 길이가 0인지 확인할 수 있는 방법이 없나요 getopts ... while ... case
?
외부에서 매개변수 검증을 수행하는 것에 의존하고 싶지 않습니다. 그러면 etc getopts
에 대한 매개변수 값을 얻고 누락된 옵션을 캡처하지 못할 수도 있습니다.:)
while ... case ... esac
-d
답변1
getoptit
다음을 사용하여 두 번째 스크립트(내가 로 저장한) 를 호출할 때 :
getoptit -d -h
그러면 다음이 인쇄됩니다:
MYSQL_HOST='' MYSQL_USER='' MYSQL_PASS='' BACKUP_DIR='-h' Additionals:
if [ ! "$BACKUP_DIR" ]; then
이렇게 BACKUP_DIR이 설정되어 있지 않은 경우에는 테스트해 보시기 바랍니다. 그러면 내부의 코드가 실행되지 않는 것이 정상입니다.
각 옵션이 한 번 설정되었는지 테스트하려면 $OPTARG 값을 기준으로 할당하기 전에 이 작업을 수행해야 합니다. 또한 할당하기 전에 $OPTARG가 a로 시작하는지 확인해야 합니다 '-'
( -d -h
오류의 경우).
...
d)
if [ ! -z "$BACKUP_DIR" ]; then
echo "backup dir already set"
exit 2
fi
if [ z"${OPTARG:0:1}" == "z-" ]; then
echo "backup dir starts with option string"
exit 2
fi
BACKUP_DIR=$OPTARG
;;
...
답변2
다른 답변은 실제로 귀하의 질문에 많은 답변을 제공하지 않기 때문에 이는 예상되는 동작이며 방법에 따라POSIX해석 중:
옵션에 옵션 인수가 필요한 경우 getopts 유틸리티는 이를 쉘 변수 OPTARG에 배치해야 합니다. 옵션이 없거나 옵션 매개변수 없이 옵션이 발견되면 OPTARG를 설정 해제해야 합니다. )
그것이 명시된 전부이며, (너무 길지 않은) 이야기를 간략하게 말하면: getopts
보이는 옵션을 마술처럼 알지 못하는 완벽하게 타당한 주장입니다.필요하다실제로 옵션 인수가 아닌 또 다른 옵션인 인수가 있습니다. 옵션에 유효한 인수 -h
로 전달하는 것을 막을 수 있는 방법은 없습니다 -d
. 이는 실제로 getopts
인수가 필요한 옵션이 명령줄 끝에 나타나는 경우에만 오류가 발생한다는 것을 의미합니다. 예를 들면 다음과 같습니다.
test.sh
:
#!/bin/sh
while getopts ":xy:" o; do
case "${o}" in
:) echo "${OPTARG} requires an argument"; exit 1;
esac
done
예:
$ ./test.sh -y -x
$
$ ./test.sh -x -y
y requires an argument
두 번째 접근 방식이 작동하지 않는 이유는 구문 분석이 getopts
여전히 동일하기 때문에 루프에 들어가면 "손상"이 이미 완료되었기 때문입니다.
이 기능을 절대적으로 비활성화하려면 Benubird가 지적했듯이 옵션 인수를 직접 확인하고 유효한 옵션과 같으면 오류를 발생시켜야 합니다.
답변3
계속 사용할 예정이지만 getopts
나중에 몇 가지 추가 확인을 수행합니다. (테스트되지 않음)
errs=0
declare -A option=(
[MYSQL_HOST]="-h"
[MYSQL_USER]="-u"
[MYSQL_PASS]="-p"
[BACKUP_DIR]="-d"
)
for var in "${!option[@]}"; do
if [[ -z "${!var}" ]]; then
echo "error: specify a value for $var with ${option[var]}"
((errs++))
fi
done
((errs > 0)) && exit 1
Bash 버전 4 필요
답변4
먼저 를 사용해야 합니다 if [ -z "$MYSQL_USER" ]
.
둘째, 할당이 필요하지 않습니다. if [ -z "$OPTARG" ]
잘 작동합니다.
셋째, 나는 그것이 당신이 정말로 원하는 것이라고 생각합니다 if [ ${#OPTARG} = 0 ]
. ${#x}는 문자열 $x의 길이를 반환하는 bash입니다(자세히 보기여기).
넷째, 직접 확인하는 경우에는 getopts 대신 getopt를 사용하는 것이 더 유연하므로 사용하는 것이 좋습니다.
마지막으로 첫 번째 질문에 답하기 위해 다음과 같이 플래그 목록을 맨 위에 배치하여 플래그가 인수로 전달되는 시기를 감지할 수 있습니다.
args=( -h -u -p -d )
그런 다음 제공된 인수가 다음과 같은 옵션인지 확인하려는 옵션을 확인하십시오.
d)
BACKUP_DIR=$OPTARG
if [ "$(echo ${args[@]/$OPTARG/})" = "$(echo ${args[@]})" ]
then
echo "Argument is an option! Error!"
fi
완벽한 답변은 아니지만 효과가 있습니다! 문제는 "-h"가 완벽하게 유효한 인수이고 유효한 플래그가 아닌 인수를 찾고 있다는 사실을 쉘에 알릴 수 없다는 것입니다.