I want to create a template for password entries and the conditions are:

  1. Password must be at least 12 characters long;
  2. There must be at least one lower case, one upper case, one number, and one special character;
  3. Specific special characters such as <>`"'~ are not allowed;
  4. Order does not matter, as long as the 3 previous conditions are met.

I have utilized negative lookahead to create a pattern, but that doesn't work. My pattern:


OS: Ubuntu 20.04

  • 7
    If you have any influence on these requirements, please push back against them. Forbidding some special characters in passwords is a sign that something is broken in your infrastructure: either it can be exploited by something other than a password, or passwords are being passed around to places that can't keep them secret. Requiring special characters in passwords is widespread, but misguided advice that pushes people to choose weaker passwords. security.stackexchange.com/q/131056 security.stackexchange.com/questions/16455 security.stackexchange.com/questions/6095 Dec 3 '21 at 11:48
  • 2
    Even just the fact that you're doing password validation in bash is a red flag. It's possible, but hard to verify that the password won't leak due to a bug in another part of the script, or that it won't leak through a side channel in the password validation code, or that it won't end up in a log somewhere. Dec 3 '21 at 11:50
  • 2
    @Gilles'SO-stopbeingevil' fully agree with your suggestion, but the bug in question is in Wordpress which doesn't accept these characters in the password when passed through Wp-CLI.
    – malthus
    Dec 3 '21 at 13:58

Your question does not mention how you try to use your expression. Regular expressions in the bash shell are POSIX extended regular expressions. These do not support positive or negative lookaheads or look-behinds.

Instead of using your expression to try to fit something more into it, I'm suggesting breaking the test down into several separate tests.

It's easiest to understand the implementation and to keep it maintained if each test is done independently, without trying to craft a monster expression to do it all.


pw_is_valid () {
        local avoid='[<>`"'"'"'~]'

        [ "${#1}" -ge 12 ] &&
        [[ $1 =~ [[:upper:]] ]] &&
        [[ $1 =~ [[:lower:]] ]] &&
        [[ $1 =~ [[:digit:]] ]] &&
        [[ $1 =~ [[:punct:]] ]] &&
        [[ ! $1 =~ $avoid ]]

        IFS= read -r -s -p 'Enter password: ' pw
        pw_is_valid "$pw"
        echo 'Try again' >&2

I'm using a variable, avoid, to hold the regular expression [<>`"'~] to reduce the quoting needed to represent it.

In the POSIX aka C locale, the POSIX [:punct:] character class contains the characters in the following set:


In regular locales, it would typically contain a lot more¹.

I'm using it here as a stand-in for "special characters".

¹ for example, on Ubuntu 20.04 and in the en_GB.UTF-8 locale, it includes 147495 different characters. YMMV depending on the system, version thereof and locale.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.