1846148 Members
4797 Online
110254 Solutions
New Discussion

Re: scripting

 
SOLVED
Go to solution
Ravinder Singh Gill
Regular Advisor

scripting

In a script what does the following while loop translate to:

while [ x$1 != "x" ]

I have not done scripting before and do not understand the arguments inside the while loop (brackets) as above.

16 REPLIES 16
RAC_1
Honored Contributor

Re: scripting

It means while x$1 not eqal to x
$1=varibale defined.
There is no substitute to HARDWORK
Rajesh SB
Esteemed Contributor
Solution

Re: scripting

Hi,

while [ x$1 != "x" ]

$1 means command line parameter for your script. Suppose you script name is "run.sh"

# run.sh Gill

here $1=Gill
x$1=xGill

If # run.sh
Here $1= null
x$1=x

This while loop says
x$1 is not equal to x.

Regards,
Rajesh
john korterman
Honored Contributor

Re: scripting

Hi,

it is a kind of standard trick for detecting if a variable has been set.
In this case it checks if the script in which it is used has been activated with a parameter(=$1) or not: if $1 is non-exisiting, then x$1 is equal to x

regards,
John K.
it would be nice if you always got a second chance
James R. Ferguson
Acclaimed Contributor

Re: scripting

Hi:

The expression will evaluate to true if "$1" isn't empty, otherwise it will evaluate to false.

You can easily see this by doing:

cat /tmp/debug
#!/usr/bin/sh
if [ x$1 != "x" ]; then
echo ok
fi

# sh -x /tmp/debug #...no arguments
# sh -x /tmp/debug arg #...any argument

Regards!

...JRF...
Ravinder Singh Gill
Regular Advisor

Re: scripting

Thanks guys
Ravinder Singh Gill
Regular Advisor

Re: scripting

But what is x?

The script is as follows:

echo "Please enter the User's Surname and first TWO Christian Names only, "
echo "separated by spaces. Please use only lower case. CTRL-C to exit."
echo
echo "For example \"jones derick richard\" for Derick Richard Jones."
echo
echo " User's name (lower case only): \c"
read NAME
unset CHR1 CHR2 SURNAME EXIST USERCHECK
COUNT=0
set $NAME
while [ x$1 != "x" ]
do
COUNT=`expr $COUNT + 1`
case $COUNT in
1) SURNAME=$1 ;;
2) CHR1=$1 ;;
3) CHR2=$1 ;;
esac
shift 1
done
INIT1=`echo $CHR1 | cut -c1`
INIT2=`echo $CHR2 | cut -c1`
if [ x$INIT2 = "x" ]
then
INIT2=a
fi
SNAME=`echo $SURNAME | cut -c1-5`

while [ ${#SNAME} -lt 5 ]
do
SNAME=$SNAME"a"
done

UNAME=`echo $SNAME$INIT1$INIT2`

USERCHECK=`echo "$UNAME:"`
EXIST=`grep $USERCHECK /etc/passwd | awk -F: {'print $1'}`

if [ x$EXIST != "x" ]
then
COUNT=1
for i in $EXIST
do
COUNT=`expr $COUNT + 1`
done
UNAME=`echo $UNAME$COUNT`
fi

HOMEDIR="/home/gvts"
if [ $INIT2 = a ]
then
COMMENTS="STAFF $CHR1 $SURNAME"
else
COMMENTS="STAFF $CHR1 $CHR2 $SURNAME"
fi

clear
echo
echo " GVTS User creation utility"
echo " ~~~~~~~~~~~~~~~~~~~~~~~~~~"
echo
echo "The user account will be created as follows:"
echo
echo " User ID: $UNAME"
# echo " User ID2: $UNAME"t""
echo " Home directory: $HOMEDIR"
echo " Comments: $COMMENTS"
sleep 3

echo "useradd -g gvts -s /bin/false -d $HOMEDIR -c \"$COMMENTS\" $UNAME" > u
# echo "useradd -g gvts -s /bin/false -d $HOMEDIR -c \"$COMMENTS\" $UNAME"t"" >> u
echo "echo "Enter password for $UNAME"" >> u
echo "passwd $UNAME" >> u
# echo "echo "Enter password for $UNAME"t""" >> u
# echo "passwd $UNAME"t"" >> u
sh u
rm u
done
john korterman
Honored Contributor

Re: scripting

Hi again,

x is simply a random letter in this case.
It is not important which letter is used, in the script however, x is used:

while [ x$1 != "x" ]

and

if [ x$INIT2 = "x" ]


you could as well have used:

while [ y$1 != "y" ]

and

if [ z$INIT2 = "z" ]

the idea is simply to test if the accompanying variable, $1 and $INIT2, respectively, have been set, i.e. do they have a value or are they equal to nothing.
If equal to nothing, the letter chosen is equal to the letter chosen!

In you script the idea is to repeat the question until the user supplies input, which then is considered $1 by the script.

regards,
John K.
it would be nice if you always got a second chance
Howard Marshall
Regular Advisor

Re: scripting

In your script, x is nothing more than a error prevention device.

The statement while[x$1 != "x" ] breaks down like this

$1 is the command line parameter. As stated above if your script is run.sh the command line parameters would be Smith and William and the first ($1) would be Smith

#run.sh Smith William

if you just execute the script with no parameters $1 is a null and the test program ([) will return an error like value expected. However, if instead of testing for just $1, you append something to the front of $1, for instance x, then you get x$1 which translates to xSmith in the example above but only to x if you provide no parameters. Now your have something to test with that will not return the value expected, sense it will always have at least the value of x.

thatâ s why the test is for "x" and not "" if no parameter is provided.

In the case of your script, what its doing is, if there is no parameter (in this case names) sent then it asks for them in the script, if there is a parameter, (the name) it has the name and doesnâ t need to prompt for it inside the script.

Hope that helps

H
Gary L. Paveza, Jr.
Trusted Contributor

Re: scripting

In your script "x" is a constant. It is simply the letter "x". It can be any other value you want (I would say away from metacharacters).
Ravinder Singh Gill
Regular Advisor

Re: scripting

Can you guys offer some explanation of the rest of the script. I am aware of what it does on a general level, but do not understand some of the commands/technical bits etc
Jean-Yves Picard
Trusted Contributor

Re: scripting


echo
echo " User's name (lower case only): \c"
read NAME
unset CHR1 CHR2 SURNAME EXIST USERCHECK
COUNT=0
set $NAME

set name as first parameter (aka $1)

while [ x$1 != "x" ]
do
COUNT=`expr $COUNT + 1`
case $COUNT in
1) SURNAME=$1 ;;
2) CHR1=$1 ;;
3) CHR2=$1 ;;
esac
shift 1
done

assign NAME to SURNAME, then CHR1 then CHR2

INIT1=`echo $CHR1 | cut -c1`
INIT2=`echo $CHR2 | cut -c1`

make INIT1 initial of CHR1, INIT2 liekwise

if [ x$INIT2 = "x" ]
then
INIT2=a
fi

if no INIT2, then assing aritrary a to INIT2

SNAME=`echo $SURNAME | cut -c1-5`

SNAME is first 5 letter of surname ...

while [ ${#SNAME} -lt 5 ]
do
SNAME=$SNAME"a"
done

...padded with "a"


UNAME=`echo $SNAME$INIT1$INIT2`

now Joe Doe gave UNAME=doeaaja
and Joe Mike Smithson gave smithjm
wow this gave a nice 7 letter login name !!!


USERCHECK=`echo "$UNAME:"`
EXIST=`grep $USERCHECK /etc/passwd | awk -F: {'print $1'}`

what about if name is alread on :etc/passwd ?

if [ x$EXIST != "x" ]
then
COUNT=1
for i in $EXIST
do
COUNT=`expr $COUNT + 1`
done
UNAME=`echo $UNAME$COUNT`
fi

count number of occurence (and prolly a syntax error here, one should write
if [ "x$EXIST" != "x" ]
instead of
if [ x$EXIST != "x" ]
besides, if there is more than 9 matches, resulting name might be 9 letter long, a problem on sone unixes ...)

HOMEDIR="/home/gvts"
if [ $INIT2 = a ]
then
COMMENTS="STAFF $CHR1 $SURNAME"
else
COMMENTS="STAFF $CHR1 $CHR2 $SURNAME"
fi

prepare Gcos variable in /etc/passwd (5th filed) with real name.

clear
echo
echo " GVTS User creation utility"
echo " ~~~~~~~~~~~~~~~~~~~~~~~~~~"
echo
echo "The user account will be created as follows:"
echo
echo " User ID: $UNAME"
# echo " User ID2: $UNAME"t""
echo " Home directory: $HOMEDIR"
echo " Comments: $COMMENTS"
sleep 3

tell us about the creation of unix account (by the way where is HOMEDIR defined ?)

echo "useradd -g gvts -s /bin/false -d $HOMEDIR -c \"$COMMENTS\" $UNAME" > u
# echo "useradd -g gvts -s /bin/false -d $HOMEDIR -c \"$COMMENTS\" $UNAME"t"" >> u
echo "echo "Enter password for $UNAME"" >> u
echo "passwd $UNAME" >> u
# echo "echo "Enter password for $UNAME"t""" >> u
# echo "passwd $UNAME"t"" >> u
sh u
rm u
done

where that's done comming from ? did you copy paste it all ?

--
are you having trouble with this script ?
does it run well ?

Jean-Yves Picard
john korterman
Honored Contributor

Re: scripting

Hi again,

To add a little detail to the part where the user's input is read in:

After presenting the instructions about how the user is supposed to enter his input:

read NAME
...assigns all input made by the user into a single variable: if the user wrote:
skov ib bo
these three names will then all be kept in NAME

set $NAME
...has the effect that the content of NAME is broken up into space separated items, i.e. positional parameters, that can be addressed individually; now $1 contains the word skov, $2 contains the word ib, and $3 the word bo.
This is important to understand as the script seems to lose interes in the NAME variable!


The script now chews its way through these positional parameters:

while [ x$1 != "x" ]
...until positional parameter no. 1 is "empty" do this;

COUNT=`expr $COUNT + 1`
...add one to COUNT (first time COUNT is "1")

case $COUNT in
...if COUNT is 1, 2, or 3, do the requested action for the value in question:

1) SURNAME=$1 ;;
...COUNT is 1, assign the current $1 value, skov, to the variable SURNAME
...the script can select only a single option of the case-structure; as "1" was used the script then jumps to the end of the case-structure:
esac

...at this moment there are still three positional parameters:
skov ib bo

...but this is now changed by
shift 1
...which removes the previous $1 value, skov, leaving only
ib bo
...Notice that $1 is now ib

...Then the loop is evaluated again:
while [ x$1 != "x" ]
...$1 is not empty, as it now contains
ib

...COUNT is incremented by one
COUNT=`expr $COUNT + 1`
...And is now 2

...Therefore the
case $COUNT in
...now enters
2) CHR1=$1 ;;
...Which assigns ib to CHAR1


Shift 1
...Changes $1 to
bo

...and the third time we enter the loop bo is assigned to CHR2

...after the third
shift 1
there is no longer any $1 and the evaluation
while [ x$1 != "x" ]
...is now true, e.g.
x is equal to x and the script jumps to the first line after done..

A long explanation for a small part, but the way NAME is handled is a little tricky.

regards,
John K.
it would be nice if you always got a second chance
Bill Hassell
Honored Contributor

Re: scripting

And just to add some expertise to your shell scripting, the tecnique:

[ x$1 != "x" ]

is now replaced with the more readable technique:

[ "$1" != "" ]

By placing the parameter ($1 in this case) within double quotes, the test for a missing or null value makes much more sense. If $1 is null or a zero-length string the test (the square brackets) will be correctly evaluated.


Bill Hassell, sysadmin
Mark Ellzey
Valued Contributor

Re: scripting

Ravinder,

This construct is quite old. See Bill's answer for the current correct syntax.

Since this is an interactive script, it really needs to do a lot of error-checking. Typically, when I write an interactive script, I'll test all the expected parameters with something like this:

if [ -z $1 ]
then
echo "Missing parameter."
exit 1
fi

The -z means if the value of the string $1 is null, evaluate to true. The test(1m) man page will give you a much more detailed description.

-or-

if [ $# -ne 3 ]
then
echo "Missing parameters."
exit 1
fi

The $# is the number (#) of parameters from the command line. This code just says that if the number of parameters is not equal to 3 , then stop.

You should always let the user know when something is wrong and stops the script. This can be extremely important if the scripts does anything 'dangerous', like removing files.

Regards,
Mark
Ralph Grothe
Honored Contributor

Re: scripting

Wonder why all the scripting experts that correctly explained the odd [ x$1 != "x" ] shell idiom to you haven't mentioned the double square brackets test "operator" yet.
If you are using any contempory Bourne shell compatible like ksh, bash, or HP-UX sh,
the the odd idiom isn't needed and obsolete
because the [[ ]] test command prevents word splitting and pathname globbing but does all the variable, parameter, command and arithmetic expansions.
This lets you write more tangibly

while [[ $1 != "" ]]; do

or better yet

while [[ -n $1 ]]; do

or even as arithmetic expression,
but probably more obfuscated

while (( ${#1} )); do
Madness, thy name is system administration
Ravinder Singh Gill
Regular Advisor

Re: scripting

cheers for your help guys