AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 553022
Accepted
Harold Fischer
Harold Fischer
Asked: 2019-11-20 12:43:04 +0800 CST2019-11-20 12:43:04 +0800 CST 2019-11-20 12:43:04 +0800 CST

如何确保字符串至少包含一个大写字母、一个小写字母、一个数字和一个标点符号?

  • 772

这就是我现在用来完成工作的方法:

#!/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

这是非常低效和冗长的。有一个更好的方法吗?

shell
  • 9 9 个回答
  • 4961 Views

9 个回答

  • Voted
  1. RomanPerekhrest
    2019-11-20T12:56:52+08:002019-11-20T12:56:52+08:00

    灵活的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
    
    • 7
  2. Best Answer
    Stéphane Chazelas
    2019-11-20T14:24:25+08:002019-11-20T14:24:25+08:00

    一次调用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 开头的值-)。

    使用caseshell 构造的该函数的变体:

    has_char_of_each_class() {
      input=$1; shift
      for class do
        case $input in
          (*[[:$class:]]*) ;;
          (*) return 1;;
        esac
      done
    }
    

    但是请注意,在脚本中间更改 shell 的语言环境并不适用于所有sh实现(因此,如果您希望将输入视为已编码,则需要已经在 C 语言环境中调用脚本C 语言环境字符集和字符类仅匹配 POSIX 指定的字符集)。

    • 7
  3. Kusalananda
    2019-11-20T13:57:33+08:002019-11-20T13:57:33+08:00

    以下脚本比您的代码长,但显示了如何根据模式列表测试字符串。该代码检测字符串是否匹配所有模式并打印出结果。

    #!/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 ... esacPOSIX 方法。该变量$pattern在测试中使用不带引号,因此匹配不会作为字符串比较完成。如果字符串与给定的模式不匹配,那么它将匹配,并在设置为*后退出循环。failedtrue

    运行它会产生

    $ 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 的答案(变体)。

    • 3
  4. bxm
    2019-11-20T15:17:00+08:002019-11-20T15:17:00+08:00

    受 RomanPerekhrest 的启发,但做了一些小的改进以取消管道和命令替换:

    if awk '/[[:lower:]]/ && /[[:upper:]]/ && /[[:digit:]]/ && /[[:punct:]]/ {exit 1}' <<< "$string" ; then
      echo "did not match all requirements"
    else
      echo "looks good to me"
    fi
    
    • 3
  5. Harold Fischer
    2019-11-20T16:10:03+08:002019-11-20T16:10:03+08:00

    这是 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
    

    它还通过使用 awk 的退出代码而不是检查 awk 的输出是否为空来借用 bxm 的答案。

    • 3
  6. bu5hman
    2019-11-23T10:57:22+08:002019-11-23T10:57:22+08:00

    从@HaroldFischer @bxm 和@RomanPerekhrest 无耻地窃取纯粹的awk解决方案

    awk -v test="does not meet" '/[a-z]/ && /[A-Z]/ && /[0-9]/ && /[[:punct:]]/ {test="meets"}
        END {print "String "test" your requirements"}' <<<"Aa&0"
    
    • 1
  7. mr.spuratic
    2019-11-23T10:08:27+08:002019-11-23T10:08:27+08:00

    为了完整起见,因为没有其他答案提到 PCRE。BRE/ERE的一个限制是你不能轻易地实现一个逻辑和等效的逻辑“或”与|.

    PCRE 模式允许您使用零宽度断言创建“和”条件:前瞻或后瞻。这些“消耗”没有字符,但限制模式之前或之后的匹配。有很多方法可以使用这些,在这里将前瞻放在前面是有意义的:

    LC_ALL=C pcregrep -q '(?=.*[[:upper:]])(?=.*[[:lower:]])(?=.*[[:digit:]])(?=.*[[:punct:]]).{4,}'
    

    在应用匹配之前,PCRE 对输入应用 4 个“先决条件” .{4,}(4 个或更多字符,随意使其变大;-)。需要注意的一点是“ (?=[[:upper:]])”只会检查单个字符,因此每个条件都以“ .*”开头,因此会检查整个输入。pcregrep还支持通过--locale=C.

    由于 PCRE 中的“P”代表perl:

    perl -wln -e \
      '/(?=.*[[:upper:]])(?=.*[[:lower:]])(?=.*[[:digit:]])(?=.*[[:punct:]]).{4,}/ && exit 0; exit 1;'
    

    对单行输入做同样的事情(它不是“”的一般替代品pcregrep -q)。

    可以在此处找到此类问题的令人头晕目眩的超集:https ://stackoverflow.com/questions/469913/regular-expressions-is-there-an-and-operator


    ¹您可以扩展 ERE 以通过排列来模拟“和”:

    [[:lower:]].*[[:upper:]].*[[:digit:]].*[[:punct:]]|
    [[:lower:]].*[[:upper:]].*[[:punct:]].*[[:digit:]]|
    [[:lower:]].*[[:digit:]].*[[:upper:]].*[[:punct:]]| ... 20 more lines ...
    [[:punct:]].*[[:digit:]].*[[:upper:]].*[[:lower:]]
    

    绝对不会帮助“低效和冗长”。

    • 0
  8. R. van Rijn
    2020-11-05T04:07:27+08:002020-11-05T04:07:27+08:00

    下面根据请求创建一个随机密码。head -c 12您可以通过替换为更大的值来使密码更长。

    while true
    do
      A=$(head /dev/urandom | tr -dc A-Za-z0-9.\'\"$,_! | head -c 12 ; echo '')
    
      [[ ${A} =~ [A-Z] && ${A} =~ [a-z] && ${A} =~ [0-9] && ${A} =~ [.\'\"$,_!] ]] && break
    
    done
    
    • 0

相关问题

  • 这个命令是如何工作的?mkfifo /tmp/f; 猫/tmp/f | /bin/sh -i 2>&1 | 数控 -l 1234 > /tmp/f

  • FreeBSD 的 sh:列出函数

  • 有没有办法让 ls 只显示某些目录的隐藏文件?

  • grep -v grep 有什么作用

  • 如何将带有〜的路径保存到变量中?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve