这就是我现在用来完成工作的方法:
#!/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
这是非常低效和冗长的。有一个更好的方法吗?
灵活的
awk
模式匹配:一次调用
awk
和不使用管道:那是 POSIX,但请注意
mawk
还不支持 POSIX 字符类。与 POSIX 兼容的s--
不需要 ,awk
但会在旧版本的 busybox 中awk
(这会阻塞以$string
开头的值-
)。使用
case
shell 构造的该函数的变体:但是请注意,在脚本中间更改 shell 的语言环境并不适用于所有
sh
实现(因此,如果您希望将输入视为已编码,则需要已经在 C 语言环境中调用脚本C 语言环境字符集和字符类仅匹配 POSIX 指定的字符集)。以下脚本比您的代码长,但显示了如何根据模式列表测试字符串。该代码检测字符串是否匹配所有模式并打印出结果。
复合命令是针对一组通配模式测试字符串的
case ... esac
POSIX 方法。该变量$pattern
在测试中使用不带引号,因此匹配不会作为字符串比较完成。如果字符串与给定的模式不匹配,那么它将匹配,并在设置为*
后退出循环。failed
true
运行它会产生
您可以将测试隐藏在这样的函数中(代码在循环中测试多个字符串,调用该函数):
如果要
LC_ALL=C
在函数中本地设置,写成请注意,函数的主体现在位于子 shell 中。因此,设置
LC_ALL=C
不会影响调用环境中此变量的值。获取 shell 函数以将模式也作为参数,您基本上会得到Stéphane Chazelas 的答案(变体)。
受 RomanPerekhrest 的启发,但做了一些小的改进以取消管道和命令替换:
这是 RomanPerekhrest 为与 mawk 一起工作而改写的答案:
它还通过使用 awk 的退出代码而不是检查 awk 的输出是否为空来借用 bxm 的答案。
从@HaroldFischer @bxm 和@RomanPerekhrest 无耻地窃取纯粹的
awk
解决方案为了完整起见,因为没有其他答案提到 PCRE。BRE/ERE的一个限制是你不能轻易地实现一个逻辑和等效的逻辑“或”与
|
.PCRE 模式允许您使用零宽度断言创建“和”条件:前瞻或后瞻。这些“消耗”没有字符,但限制模式之前或之后的匹配。有很多方法可以使用这些,在这里将前瞻放在前面是有意义的:
在应用匹配之前,PCRE 对输入应用 4 个“先决条件”
.{4,}
(4 个或更多字符,随意使其变大;-)。需要注意的一点是“(?=[[:upper:]])
”只会检查单个字符,因此每个条件都以“.*
”开头,因此会检查整个输入。pcregrep
还支持通过--locale=C
.由于 PCRE 中的“P”代表
perl
:对单行输入做同样的事情(它不是“”的一般替代品
pcregrep -q
)。可以在此处找到此类问题的令人头晕目眩的超集:https ://stackoverflow.com/questions/469913/regular-expressions-is-there-an-and-operator
¹您可以扩展 ERE 以通过排列来模拟“和”:
绝对不会帮助“低效和冗长”。
下面根据请求创建一个随机密码。
head -c 12
您可以通过替换为更大的值来使密码更长。