这就是我现在用来完成工作的方法:
#!/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
的值而阻塞)。$string
-
使用 shell 构造的该函数的变体case
:
has_char_of_each_class() {
input=$1; shift
for class do
case $input in
(*[[:$class:]]*) ;;
(*) return 1;;
esac
done
}
但请注意,在脚本中间更改 shell 的语言环境并不适用于所有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
)
请注意,函数体现在位于子 shell 中。因此,设置LC_ALL=C
不会影响调用环境中该变量的值。
让 shell 函数也将模式作为参数,你基本上得到Stéphane Chazelas 的回答(变体)。
答案4
这是 RomanPerekhrest 的答案,重写为与 mawk 一起使用:
#!/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 的输出是否为空。