[SOLVED] Function goes into infinite loop when reading from a file
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
My script is working fine when I take details for each user one by one and supply them to my function adduser1
But when I take input from a file (reading line by line), somehow it goes to infinite loop
Please suggest how to take inputs from a file (reading line by line) and supply it to my function adduser1.
Below is my script.
Code:
#!/bin/bash
adduser1 ()
{
userdata="$1"
name=`echo "$userdata" | awk '{print $1}'`
gname=`echo "$userdata" | awk '{print $3}'`
while :
do
echo
echo "1. Add user on all servers"
echo "2. Add user on selected servers"
echo "3. Exit"
echo -e -n "\nPlease enter option [1 - 3] :- \c"
read ans
case $ans in
1) echo -e -n "\nAdding user on all servers\n"
for server in `cat $DIR/serverlist.txt`
do
ssh $server "/usr/sbin/useradd -g $gname $name"
done
;;
2) echo "Enter IP or hostname separated by spaces from all the alive servers"
read IPlist
for server in $IPlist
do
ssh $server "/usr/sbin/useradd -g $gname $name"
done
;;
3) break;;
*) echo "$ans is an invaild option. Please select option 1,2 or 3 only"
echo "Press [enter] key to continue. . ."
read enterKey;;
esac
done
}
################################ MAIN
DIR=`pwd`
while :
do
echo
echo "1. Add multiple users from a file"
echo "2. Add users one by one"
echo "3. Exit"
echo -e -n "Please enter option [1 - 3] :- \c"
read ans
case $ans in
1) echo -e -n "\nYou have to enter all three fields in the file in the below order only.....\t\nUsername\tPassword\tGroupname\n\n"
sleep 2
cat /dev/null > $DIR/usertobeadded.txt
vim $DIR/usertobeadded.txt
if [ `wc -l $DIR/usertobeadded.txt | awk '{print $1}'` -ne 0 ]
then
cat $DIR/usertobeadded.txt | while read userdata
do
adduser1 "$userdata"
done
else
echo -e -n "File cannot be empty"
break
fi
;;
2) echo -e -n "\nEnter Username, Password and User's primary group separated by spaces (like vikas vikaspasswd l1admin):-\n"
read userdata
adduser1 "$userdata"
;;
3) break;;
*) echo "$ans is an invaild option. Please select option 1,2 or 3 only"
echo "Press [enter] key to continue. . ."; read enterKey;;
esac
done
This is the output when it works fine (Option 2).
Code:
[root@box1 tmp]# ./test.sh
1. Add multiple users from a file
2. Add users one by one
3. Exit
Please enter option [1 - 3] :- 2
Enter Username, Password and User's primary group separated by spaces (like vikas vikaspasswd l1admin):-
user1 user1passwd group1
1. Add user on all servers
2. Add user on selected servers
3. Exit
Please enter option [1 - 3] :- 1
Adding user on all servers
root@192.168.2.20's password:
1. Add user on all servers
2. Add user on selected servers
3. Exit
Please enter option [1 - 3] :- 3
1. Add multiple users from a file
2. Add users one by one
3. Exit
Please enter option [1 - 3] :- 3
[root@box1 tmp]#
Below is the output when it goes into infinite loop (Option 1).
Code:
[root@box1 tmp]# ./test.sh
1. Add multiple users from a file
2. Add users one by one
3. Exit
Please enter option [1 - 3] :- 1
You have to enter all three fields in the file in the below order only.....
Username Password Groupname
1. Add user on all servers
2. Add user on selected servers
3. Exit
Please enter option [1 - 3] :- is an invaild option. Please select option 1,2 or 3 only
Press [enter] key to continue. . .
1. Add user on all servers
2. Add user on selected servers
3. Exit
Please enter option [1 - 3] :- is an invaild option. Please select option 1,2 or 3 only
Press [enter] key to continue. . .
1. Add user on all servers
2. Add user on selected servers
3. Exit
Please enter option [1 - 3] :- is an invaild option. Please select option 1,2 or 3 only
Press [enter] key to continue. . .
1. Add user on all servers
2. Add user on selected servers
3. Exit
.......... -------------- This goes infinitely, I have trimmed the output to make it readable.
I have to press Ctrl+C to get out of it.
Please enter option [1 - 3] :-
1. Add multiple users from a file
2. Add users one by one
3. Exit
Please enter option [1 - 3] :- 3
[root@box1 tmp]#
That break in case 3 is not breaking out of the loop as expected.
Try setting up a flag variable, say "STOP". Set it to 0 or blank. The while loop should check the STOP for 1 or not blank. The in case 3, set the STOP variable to 1 or T or whatever you want to indicate that option 3 was selected. When the while loop wraps around, it will check STOP and exit.
That break in case 3 is not breaking out of the loop as expected.
Try setting up a flag variable, say "STOP". Set it to 0 or blank. The while loop should check the STOP for 1 or not blank. The in case 3, set the STOP variable to 1 or T or whatever you want to indicate that option 3 was selected. When the while loop wraps around, it will check STOP and exit.
Hi mjones,
I tried setting STOP flag at few places, but nothing worked for me.
Could you please set the flags for me. I have been struggling for this for quite some time now.
This is the latest what I have tried.
This is how I placed, STOP flag.
Code:
#!/bin/bash
adduser1 ()
{
userdata="$1"
name=`echo "$userdata" | awk '{print $1}'`
gname=`echo "$userdata" | awk '{print $3}'`
while :
do
if [ $STOP -eq 0 ]
then
echo
echo "1. Add user on all servers"
echo "2. Add user on selected servers"
echo "3. Exit"
echo -e -n "\nPlease enter option [1 - 3] :- \c"
read ans
case $ans in
1) echo -e -n "\nAdding user on all servers\n"
for server in `cat $DIR/serverlist.txt`
do
ssh $server "/usr/sbin/useradd -g $gname $name"
done
;;
2) echo "Enter IP or hostname separated by spaces from all the alive servers"
read IPlist
for server in $IPlist
do
ssh $server "/usr/sbin/useradd -g $gname $name"
done
;;
3) break;;
*)
echo "$ans is an invaild option. Please select option 1,2 or 3 only"
STOP=1
echo "Press [enter] key to continue. . ."
read enterKey;;
esac
fi
done
}
################################ MAIN
DIR=`pwd`
STOP=0
while :
do
echo
echo "1. Add multiple users from a file"
echo "2. Add users one by one"
echo "3. Exit"
echo -e -n "Please enter option [1 - 3] :- \c"
read ans
case $ans in
1) echo -e -n "\nYou have to enter all three fields in the file in the below order only.....\t\nUsername\tPassword\tGroupname\n\n"
sleep 2
#cat /dev/null > $DIR/usertobeadded.txt
vim $DIR/usertobeadded.txt
if [ `wc -l $DIR/usertobeadded.txt | awk '{print $1}'` -ne 0 ]
then
cat $DIR/usertobeadded.txt | while read userdata
do
adduser1 "$userdata"
done
else
echo -e -n "File cannot be empty"
break
fi
;;
2) echo -e -n "\nEnter Username, Password and User's primary group separated by spaces (like vikas vikaspasswd l1admin):-\n"
read userdata
adduser1 "$userdata"
;;
3) break;;
*) echo "$ans is an invaild option. Please select option 1,2 or 3 only"
echo "Press [enter] key to continue. . ."; read enterKey;;
esac
done
This is the output of the script.
Quote:
[root@box1 tmp]# ./test.sh
1. Add multiple users from a file
2. Add users one by one
3. Exit
Please enter option [1 - 3] :- 1
You have to enter all three fields in the file in the below order only.....
Username Password Groupname
1. Add user on all servers
2. Add user on selected servers
3. Exit
Please enter option [1 - 3] :- is an invaild option. Please select option 1,2 or 3 only
Press [enter] key to continue. . .
Infinite loop stopped, but now it hangs here and does not accepts/identifies any options.
Some options tried by me...
2
2
1
3
Then I had press Ctrl+C
1. Add multiple users from a file
2. Add users one by one
3. Exit
Please enter option [1 - 3] :- 3
[root@box1 tmp]#
Right above your while statements, set the STOP flag to 0. The while statements should check the STOP flag. In case 3 of your switch code, set the STOP flag to 1. Like this:
Code:
STOP=0
while [ $STOP -eq 0 ] :
do
.
.
.
case $ans in
.
.
.
3) STOP=1
break;;
.
.
.
esac
done
Your issue is not the flags or how you terminate your loops (although personally I try to steer clear of creating infinite loops as you have for this exact reason),
but rather that the read in the function has no scope and hence is reading from the same file as the read in your first while loop.
The clue is in your output:
Code:
1. Add user on all servers
2. Add user on selected servers
3. Exit
Please enter option [1 - 3] :- is an invaild option. Please select option 1,2 or 3 only
Press [enter] key to continue. . .
The red portion is your own code which shows you that the case statement has been entered and invalid input has been given.
Try adding an echo to the default clause of the case:
Code:
echo "$ans"
This will show you what data is being captured and if I am not mistaken it will be from your file.
I would suggest setting up some file descriptors and using read's -u option to read from the appropriate ones.
Right above your while statements, set the STOP flag to 0. The while statements should check the STOP flag. In case 3 of your switch code, set the STOP flag to 1. Like this:
Code:
STOP=0
while [ $STOP -eq 0 ] :
do
.
.
.
case $ans in
.
.
.
3) STOP=1
break;;
.
.
.
esac
done
Thanks mjones, for your help. But this did not helped.
I must say, I myself would never have thought there was something wrong for my while loop to read the file line by line, as I have used the same loop in many of my scripts.
I am marking this thread as solved.
Below is my working program, it might be useful for others.
Code:
#!/bin/bash
adduser1 ()
{
userdata="$1"
name=`echo "$userdata" | awk '{print $1}'`
gname=`echo "$userdata" | awk '{print $3}'`
echo -e -n "\n\n$userdata\n\n"
#STOP=0
while : #[ $STOP -eq 0 ]
do
echo
echo "1. Add user on all servers"
echo "2. Add user on selected servers"
echo "3. Exit"
echo -e -n "\nPlease enter option [1 - 3] :- \c"
read ans
echo -e -n "\n\nValue of ans is $ans\n\n"
case $ans in
1) echo -e -n "\nAdding user $name on all servers\n"
for server in `cat $DIR/serverlist.txt`
do
ssh $server "/usr/sbin/useradd -g $gname $name"
done
;;
2) echo "Enter IP or hostname separated by spaces from all the alive servers"
read IPlist
for server in $IPlist
do
ssh $server "/usr/sbin/useradd -g $gname $name"
done
;;
3) break;;
*)
echo "$ans is an invaild option. Please select option 1,2 or 3 only"
echo "Press [enter] key to continue. . ."
read enterKey;;
esac
done
}
################################ MAIN
DIR=`pwd`
while :
do
echo
echo "1. Add multiple users from a file"
echo "2. Add users one by one"
echo "3. Exit"
echo -e -n "Please enter option [1 - 3] :- \c"
read ans1
case $ans1 in
1) echo -e -n "\nYou have to enter all three fields in the file in the below order only.....\t\nUsername\tPassword\tGroupname\n\n"
#sleep 2
#cat /dev/null > $DIR/usertobeadded.txt
vim $DIR/usertobeadded.txt
if [ `wc -l $DIR/usertobeadded.txt | awk '{print $1}'` -ne 0 ]
then
exec 3<$DIR/usertobeadded.txt
while read -u 3 -r userdata
do
adduser1 "$userdata"
done
else
echo -e -n "File cannot be empty"
break
fi
;;
2) echo -e -n "\nEnter Username, Password and User's primary group separated by spaces (like vikas vikaspasswd l1admin):-\n"
read userdata
adduser1 "$userdata"
;;
3) break;;
*) echo "$ans1 is an invaild option. Please select option 1,2 or 3 only"
echo "Press [enter] key to continue. . ."; read enterKey;;
esac
done
Using file descriptors with "read -u" option helped me out, but I could not really get how this worked because both the loops yield same output.
Quote:
This worked well for me.
[root@box2 tmp]# cat abc.sh
#!/bin/bash
FILE=/tmp/passwd
exec 3<$FILE
while read -u 3 -r line
do
echo $line
done
[root@box2 tmp]#
This did NOT.
[root@box2 tmp]# cat xyz.sh
FILE=/tmp/passwd
cat $FILE | while read line
do
echo $line
done
[root@box2 tmp]#
The issue is not about yielding the same results. At a basic level both loops will read the file line by line.
The issue is that you have a second and more read commands in your function and as bash does not scope the read to only
work inside the function it can pull data from any open file descriptor that it knows about. By default all read commands
use the default stdin so irrelevant of where in your code you say the command 'read blah' it will read from stdin and
place whatever it receives into the variable.
If you would like to see the difference with your current examples, you can do the following:
Code:
one_time_read()
{
read
}
FILE=/tmp/passwd
cat $FILE | while read line
do
echo $line
one_time_read
done
Add the same to your other script and see the difference.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.