What I want is exactly shown below (I modified the former image and it looks like clearer.)
And with some guys' help, I made it. My script is below:
#!/bin/bash
# name: loginname_validate
# purpose: to validate the loginname which is used to add a new user
i=0
while [ $i -ne 1 ]
do
read -p "Please enter a new loginname: " loginname
if [ -z "$loginname" ]
then
echo "You should enter a nonempty loginname."
continue 1
elif [ ${#loginname} -lt 8 -o ${#loginname} -gt 16 ]
then
echo "The length of loginname must between 8 and 16 characters long."
continue 1
elif echo "$loginname" | awk '/^[-a-Z]/ && /[a-z]/ && /[A-Z]/ && /[0-9]/ && /\W/ { X=1 } END { exit(!X) }'
then
echo "You have entered a valid loginname."
break
else
echo "The loginname, which you entered, must start with a underscore or an alphabet, and it must contain at least one lowercase and one uppercase and one digit and one punctuation."
continue 1
fi
done
Of course it's not a homework problem.
I'm writing a script to create a Linux user automatically. And I want to determine whether the loginname already exists(condition 1) and its length is more than 8-chars(condition 2) and it contains uppercase/lowercase/numeric/punctuation(condition 3). If it meets all conditions, that means it is a valid loginname.
That's OK? And will you not prevent others helping me, will you?
# Create a variable i
typeset i=value
# Does i meet condition 1
if [ i == whatever condition 1 ]
then
# Does i meet condition 2
if [ i == whatever condition 2 ]
then
# Does i meet condition 3
if [ i == whatever condition 3 ]
then
# Action if all conditions are met
else
# Action if i does not meet condition 3
fi
else
# Action if i does not meet condition 2
fi
else
# Action if i does not meet condition 1
fi
Hi, Yoda,
Thanks. However, it doesn't meet my need.
For instance, if the variable i doesn't meet the condition 3, I want to recreate the variable i. How can I return the code
Be careful - if you don't modify the creation of the variable, you're stuck in an infinite loop. So you better not define i to be a constant. However, depending on your shell (bash in my case), this may work for you:
until typeset i=J; [ "$i" cond1 -a "$i" cond2 -a "$i" cond3 ]; do :; done
I understand the usage of until. But it doesn't work well here.
I'm so sad because if I use your code, I will not get the error indication when the variable i doesn't meet any condition.
And thanks for your advice. In fact, the variable i will be assigned the value by the command read.
e.g.
read -p "Please enter something: " i
and what I want is to check user's input whether to meet all the three conditions. If not, reenter again!
Thank you.
---------- Post updated at 05:39 PM ---------- Previous update was at 05:37 PM ----------
I don't create a variable. I just want to assign the value to the variable by using command read.
like this:
what if I want to print different indications?
e.g.:
if variable i equals to 1, I will get the standard output "hello!"
if variable i equals to 2, I will get the standard output "nice!"
if variable i equals to 3, I will get the standard output "good!"
Going back to the original posting in this thread, the following might do what you were trying to do:
#!/bin/bash
# name: loginname_validate
# purpose: to validate the loginname which is used to add a new user
i=0
while [ $i -ne 1 ]
do
#for ksh: if ! IFS="" read -r loginname?"Please enter a new loginname: "
#for bash:if ! IFS="" read -r -p"Please enter a new loginname: " loginname
if printf "Please enter a new loginname: " &&
! IFS="" read -r loginname
then printf '\nUnexpected EOF or error reading login name.\n' >&2
exit 2
fi
if [ -z "$loginname" ]
then echo 'Loginname must not be empty.'
elif [ ${#loginname} -lt 8 -o ${#loginname} -gt 16 ]
then echo 'Loginname must be between 8 and 16 characters long.'
elif [ "$loginname" == "${loginname#[_[:alpha:]]}" ]
then echo 'Loginname 1st character must be underscore or alphabetic.'
elif [ "$loginname" == "${loginname#*[[:lower:]]}" ]
then echo 'Loginname must contain a lowercase alphabetic character.'
elif [ "$loginname" == "${loginname#*[[:upper:]]}" ]
then echo 'Loginname must contain an uppercase alphabetic character.'
elif [ "$loginname" == "${loginname#*[[:digit:]]}" ]
then echo 'Loginname must contain a numeric character.'
elif [ "$loginname" == "${loginname#*[[:punct:]]}" ]
then echo 'Loginname must contain a punctuation character.'
elif [ "$loginname" != "${loginname#*[![:alnum:][:punct:]]}" ]
then echo "Loginname can only contain alphanumeric and punctuation characters."
elif [ -d ~"$loginname" ]
then printf "Loginname already exists: %s\n" "$loginname"
else echo 'You have entered a valid loginname.'
i=1
continue
fi
echo 'Loginname must meet following criteria:
1. 8 <= number of characters in name <= 16,
2. 1st character in name must be an alphabetic character or an underscore,
3. name must contain at least one lowercase letter,
4. name must contain at least one uppercase letter,
5. name must contain at least one numeric character,
6. name must contain at least one punctuation character, and
7. name must not already be assigned.'
done
printf "Loop terminated with loginname set to \"%s\"\n" "$loginname"
However, I find some things VERY strange about this problem. I've never seen a Linux or UNIX system that wanted login names to contain punctuation characters like ` , ~ , ' , " , ! , # , $ , ^ , & , * , ( , ) , { , } , \ , / , < , and > . The requirement to have punctuation characters sounds more like a validation check for passwords than for login names.
Notes:
This script is written to be portable for use with any POSIX conforming shell. The way the ksh and bash read built-ins specify prompts are incompatible with each other. The portable if statement in brown above can be removed along with the comment at the start of one of the two lines above it for use with ksh only or with bash only.
The code in orange above prevents characters in the space class and the control character class from being accepted. For login names, I would think this would be a useful addition. For passwords, I would think this code should be removed.
Your original script didn't mention it, but I would think any validation program for a new user name would need to verify that the name being checked is not already in use as a login name. Unfortunately, I don't know of any portable way to test it. (A grep on /etc/passwd would work on some systems, but certainly not all.) The code in blue above tries to check this by looking for the home directory for given login name. If the shell on your system finds the home directory for the given name, it seems safe to assume that the name is already in use. If for some reason some users' directories are not accessible, some duplicate names might not be caught by this test. If this test isn't sufficient on your system, I assume that you'll be able to replace it with something that works in your environment. If you don't want a test like this at all, just remove the code in blue.
You are right! Loginname doesn't look so complex. I just took a little practice.
I have a question: what if I want the loginname to start with an underscore or an alphabet and not to contain any spaces? What should I do?
Write down your exact set of requirements as a list of tests that need to be performed. You have the first two done:
Is the 1st character is _ or alpha class?
Are there any spaces? Although I imagine you might want: Are there any characters in class space (rather than just space characters to exclude tabs, newlines, vertical tabs, etc. in addition to the space character itself).
Do you have any other requirements about what characters are allowed or forbidden, minimum or maximum length restrictions, etc?
With your list of requirements and the code I supplied as a sample, try creating your own script. If it doesn't work, show us the complete set of requirements and show us what you tried. Then we'll try to help you fix the script to meet your requirements.
As you get more experience programming, you'll quickly learn it is MUCH easier to write a program if you write a complete set of requirements first.
PS Note that the script I provided doesn't allow any space characters in the login name.
What confused me just now was why I couldn't replace [a-Z0-9_@.-] with [\w@.-].
---------- Post updated at 03:59 PM ---------- Previous update was at 03:27 PM ----------
I have another question:Why did I get an error with the first code? And it worked well with the second code
first code:
...
if grep -q "^${loginname}:" /etc/passwd && echo "The loginname already exists." && return 1
elif [ -z "${loginname}" ] && echo "You should enter a nonempty loginname." && return 1
...
Error:
second code:
...
if grep -q "^${loginname}:" /etc/passwd
then
echo "The loginname already exists."
return 1
elif [ -z "${loginname}" ]
then
echo "You should enter a nonempty loginname."
return 1
...
According to the standards, \w in a bracket expression in a Basic Regular Expression (as used by grep when the -E and -F options are not active) matches the two characters \ and w .
What I found surprising is that I hadn't seen anything indicating that the login names you wanted to verify were:
stored in a file,
that your wanted to include the line number in the file where they were stored as part of your list of valid names,
nor that the only character you wanted for the first and last character was "_".
Note that the a-Z in a range expression is treated as an empty set or as an error on most systems I've seen because "a" comes after "Z" in collating order in the C/POSIX locale. Looking more closely at the standards, it is ambiguous as to whether a backwards range should be accepted. If it is accepted it would contain the characters Z , [ , \ , ] , ^ , _ , ` , and a ; not lowercase and uppercase letters.
You also did not mention that your code checking valid login names was going to be included in a function. But, the return statement is only vaiid inside a function.
Whether it is in a function or not, there are two ways to code the equivalent of an if statement (ignoring elif clauses):
if compound-list
then
compound-list of then clause commands
else
compound-list of else clause commands
fi
which matches what you have shown above as second code , and equivalently
compound-list &&
compound-list of then clause commands ||
compound-list of else clause commands
What you are showing above as first code is a cross between the two that is a syntax error; the if keyword requires matching then and fi keywords.
I had expected that you were going to try to pattern your updated code based on the code I had supplied earlier. I thought it was working for you, but you had changed the requirements concerning what was to be considered a valid login name.
What you have here is a completely different concept. And, with the short snippets of code you have shown, I no longer have any idea what you're trying to do.