이것이 내가 지금 작업을 완료하기 위해 사용하고 있는 것입니다:
#!/bin/sh --
string='Aa1!z'
if ! printf '%s\n' "$string" | LC_ALL=C grep -q '[[:upper:]]' || \
! printf '%s\n' "$string" | LC_ALL=C grep -q '[[:lower:]]' || \
! printf '%s\n' "$string" | LC_ALL=C grep -q '[[:digit:]]' || \
! printf '%s\n' "$string" | LC_ALL=C grep -q '[[:punct:]]'; then
printf '%s\n' 'String does not meet your requirements'
else
printf '%s\n' 'String meets your requirements'
fi
이는 매우 비효율적이고 장황합니다. 더 좋은 방법이 있나요?
답변1
한 번의 호출로 awk
파이프 없이:
#! /bin/sh -
string='whatever'
has_char_of_each_class() {
LC_ALL=C awk -- '
BEGIN {
for (i = 2; i < ARGC; i++)
if (ARGV[1] !~ "[[:" ARGV[i] ":]]") exit 1
}' "$@"
}
if has_char_of_each_class "$string" lower upper digit punct; then
echo OK
else
echo not OK
fi
이는 POSIX이지만 mawk
POSIX 문자 클래스는 아직 지원되지 않습니다. --
POSIX 호환 s에는 필요하지 않지만 awk
이전 버전의 busybox에는 필요합니다( awk
s로 시작하는 값을 차단함).$string
-
쉘 구성을 사용하는 이 함수의 변형 case
:
has_char_of_each_class() {
input=$1; shift
for class do
case $input in
(*[[:$class:]]*) ;;
(*) return 1;;
esac
done
}
그러나 스크립트 도중에 쉘의 로케일을 변경하는 것이 모든 sh
구현에서 작동하는 것은 아닙니다. 따라서 입력을 C 로케일로 인코딩된 것으로 처리하려면 C 로케일에서 스크립트를 호출해야 합니다. C 로캘 문자 집합 및 문자 클래스는 POSIX 지정 문자 집합 및 문자 클래스에만 일치합니다.
답변2
유연한 awk
패턴 매칭:
if [[ $(echo "$string" | awk '/[a-z]/ && /[A-Z]/ && /[0-9]/ && /[[:punct:]]/') ]]; then
echo "String meets your requirements"
else
echo "String does not meet your requirements"
fi
답변3
다음 스크립트는 코드보다 길지만 패턴 목록에 대해 문자열을 테스트하는 방법을 보여줍니다. 이 코드는 문자열이 모든 패턴과 일치하는지 확인하고 결과를 인쇄합니다.
#!/bin/sh
string=TestString1
failed=false
for pattern in '*[[:upper:]]*' '*[[:lower:]]*' '*[[:digit:]]*' '*[[:punct:]]*'
do
case $string in
$pattern) ;;
*)
failed=true
break
esac
done
if "$failed"; then
printf '"%s" does not meet the requirements\n' "$string"
else
printf '"%s" is ok\n' "$string"
fi
복합 명령은 case ... esac
일련의 와일드카드 패턴에 대해 문자열을 테스트하는 POSIX 방법입니다. 변수는 $pattern
테스트에서 따옴표 없이 사용되므로 문자열 비교로 일치가 발생하지 않습니다. 문자열이 주어진 패턴과 일치하지 않으면 *
로 설정된 후 일치하고 루프를 종료합니다.failed
true
이것을 실행하면
$ sh script.sh
"TestString1" does not meet the requirements
다음과 같은 함수에서 테스트를 숨길 수 있습니다(코드는 함수를 호출하여 루프에서 여러 문자열을 테스트합니다).
#!/bin/sh
test_string () {
for pattern in '*[[:upper:]]*' '*[[:lower:]]*' '*[[:digit:]]*' '*[[:punct:]]*'
do
case $1 in ($pattern) ;; (*) return 1; esac
done
}
for string in TestString1 Test.String2 TestString-3; do
if ! test_string "$string"; then
printf '"%s" does not meet the requirements\n' "$string"
else
printf '"%s" is ok\n' "$string"
fi
done
LC_ALL=C
함수에서 로컬로 설정 하려면 다음을 작성하십시오.
test_string () (
LC_ALL=C
for pattern in '*[[:upper:]]*' '*[[:lower:]]*' '*[[:digit:]]*' '*[[:punct:]]*'
do
case $1 in ($pattern) ;; (*) return 1; esac
done
)
이제 함수는 서브셸에 있습니다. 따라서 설정은 LC_ALL=C
호출 환경에서 이 변수의 값에 영향을 주지 않습니다.
쉘 함수가 패턴을 인수로 취하도록 하면 기본적으로 다음과 같은 결과를 얻습니다.Stéphane Chazelas의 답변 (변형).
답변4
다음은 mawk와 함께 사용하기 위해 다시 작성된 RomanPerekhrest의 답변입니다.
#!/bin/sh --
string='Aa1!z'
if printf '%s\n' "$string" | LC_ALL=C awk '/[a-z]/ && /[A-Z]/ && /[0-9]/ && /[!-\/:-@[-`{-~]/ {exit 1}'; then
printf '%s\n' 'String does not meet your requirements'
else
printf '%s\n' 'String meets your requirements'
fi
또한 bxm의 답변을 빌려 awk의 출력이 비어 있는지 확인하는 대신 awk의 종료 코드를 사용합니다.