LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 09-20-2018, 05:48 AM   #1
Honest Abe
Member
 
Registered: May 2018
Distribution: CentOS 7, OpenSUSE 15
Posts: 420
Blog Entries: 1

Rep: Reputation: 202Reputation: 202Reputation: 202
Question Help with bash/expect


Hi Folks,

I have made a bash script which ssh's to a number of servers (IPs are read from a text file) and adds users with a specified username and userid. The user details are also read from a text file. So I am basically running nested for loops (once for Host IP and once for each line in user_list).

The script is working as expected, only letdown being the password prompts. For each IP the password is prompted twice (first time for ssh and second time for sudo).

I would very much like to use an expect script to automate the credentials part. I have no prior experience with expect and I could not find examples of nested loop or string manipulation (when read from a file) in expect.

Here's my working bash script [some details have been omitted]
Code:
#!/bin/bash

SSH_USER="username"

HOST_IPs=/path/to/ip_list

users=/path/to/user_list


for ip in $(cat $HOST_IPs)

do

        for i in $(cat $users)

        do

        #echo $i
        USERS=`echo $i|awk -F: '{print $1}'`
        USRID=`echo $i|awk -F: '{print $2}'`
#echo $USERS

        echo "adding $USERS with ID $USRID to $ip"

        ssh $SSH_USER@$ip  " sudo  /usr/sbin/useradd -M -u $USRID -c 'description' -G secondary_GRP $USERS ;sudo chage -M -1 $USERS "
###
                if [ $? -eq 0 ]; then
                        echo "User $USERS has been created successfully on $ip"
                else
                        echo "Please check the log or verify it manually."
                fi
        done
done
Could I get some pointers on --
A. How may I use nested for loops in expect ?
B. How may I use string manipulation from file ? [my user details are kept on a text file in USERNAME:USERID format. I would like to know if I may use something like awk -F: '{print $1/$2}' FILENAME]
C. Is there any way I can call an expect script to this position (###) and provide the passwords ?

Note:
1. I am not looking for a solution with sshpass /ssh keypair.
2. My environment is a CentOS 6.9 VM. Openssh -> 5.3p1-123.el6_9, bash -> 4.1.2-48.el6, expect -> 5.44.1.15-5.el6_4
 
Old 09-20-2018, 12:26 PM   #2
michaelk
Moderator
 
Registered: Aug 2002
Posts: 25,702

Rep: Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895
expect is an extension of Tcl, you can use any of its built in commands as well as calling an external command like awk.

Tcl does have a split function.

You can use nested loops in Tcl similar to any other language.

I have not tried using sudo in an expect script. You can write your entire script in Tcl/expect, you can execute via expect -c "cmds" or use a heredoc as bash.

Some documentation.
https://www.tcl.tk/doc/
https://www.tcl.tk/man/expect5.31/expect.1.html
 
Old 09-20-2018, 02:32 PM   #3
MadeInGermany
Senior Member
 
Registered: Dec 2011
Location: Simplicity
Posts: 2,792

Rep: Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201Reputation: 1201
There are some mistakes in the script:

The for loops take a list; they are not good in reading lines. A space in the user_list can spoil everything.
A while-read loop is much better.

The exit status of ssh is not related to the exit status of the remotely run commands.

The following proposal also tries to combine the remote comands into one ssh command; would be more sufficient.

Code:
#!/bin/bash

SSH_USER="username"

HOST_IPs=host_ips

users=user_list

while read ip
do

    # construct the remote script
    while IFS=":" read USR USRID others
    do

        echo "
echo 'adding $USR with ID $USRID to $ip'
sudo /usr/sbin/useradd -M -u '$USRID' -c 'description' -G secondary_GRP '$USR' &&
sudo chage -M -1 '$USR' &&
echo 'User $USR has been created successfully on $ip' ||
echo 'Please check the log or verify it manually.'
"
###
    done < $users |
    # and feed the output into a remote /bin/sh
    ssh -x $SSH_USER@$ip /bin/sh
done < $HOST_IPs
You can give the remote script a better shape with
Code:
echo 'adding $USR with ID $USRID to $ip'
if
  sudo /usr/sbin/useradd -M -u '$USRID' -c 'description' -G secondary_GRP '$USR' &&
  sudo chage -M -1 '$USR'
then
  echo 'User $USR has been created successfully on $ip'
else
  echo 'Please check the log or verify it manually.'
fi
But be aware that this is all within an
Code:
echo "
"
on the calling system, so the $variables are substituted, but you may not use further " signs unless you \escape them.
--
Regarding the password setting,
you should use the chpasswd command, that reads it from stdin by default. Like this:
Code:
echo "user:password" | chpasswd

Last edited by MadeInGermany; 09-20-2018 at 02:56 PM.
 
Old 09-20-2018, 11:57 PM   #4
Honest Abe
Member
 
Registered: May 2018
Distribution: CentOS 7, OpenSUSE 15
Posts: 420

Original Poster
Blog Entries: 1

Rep: Reputation: 202Reputation: 202Reputation: 202
Thanks for your attention.

@MadeInGermany,

I actually do have a sed snippet to handle leading/trailing whitespaces {sed -e 's/^[ \t]*//' -e 's/[ \t]*$//'}. I could not post the whole script for workplace reasons, but I provided a bare-bone version to show my efforts.
I shall try out your recommendation with while loop.

@michaelk,

Thanks for the links, but documentation and manual pages were the first thing where I checked. Hope there were more examples for expect (much like nmcli-examples, wishful thinking ).
 
Old 09-21-2018, 03:35 PM   #5
michaelk
Moderator
 
Registered: Aug 2002
Posts: 25,702

Rep: Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895Reputation: 5895
Here is a quick expect heredoc that you can run within bash that shows how to login via ssh and run multiple commands using sudo.

Code:
#!/bin/bash
host=myhost
user=myuser

expect  << EOF
spawn ssh $user@$host
expect "*password: "
send "myuser_password\r"
expect "$ "
send "sudo bash -c 'ls -l /var'; whoami\r"
expect "*$user:"
send "myuser_password\r"
expect "$ "
EOF
As one ssh command. The first password is to login via ssh and the second is the sudo password.
Code:
host=myhost
user=myuser

expect  << EOF
spawn ssh -t $user@$host "sudo bash -c 'ls -l /var'; whoami"
expect "*password: "
send "mypassword\r"
expect "*$user:"
send "mypassword\r"
expect "$ "
EOF
Do not indent the heredoc.
 
1 members found this post helpful.
Old 09-22-2018, 05:09 AM   #6
Honest Abe
Member
 
Registered: May 2018
Distribution: CentOS 7, OpenSUSE 15
Posts: 420

Original Poster
Blog Entries: 1

Rep: Reputation: 202Reputation: 202Reputation: 202
FIXED

Thanks @michaelk, This was very helpful. I have added the EOF block with minor modifications to the ### section and it now works flawlessly (albeit with a time delay, so I guess I have to use the set timeout).

Few links for future visitors to this thread in hopes that they find the resources useful -

link 1
link 2
link 3
link 4

Last edited by Honest Abe; 09-22-2018 at 05:18 AM.
 
Old 10-01-2018, 06:23 AM   #7
Honest Abe
Member
 
Registered: May 2018
Distribution: CentOS 7, OpenSUSE 15
Posts: 420

Original Poster
Blog Entries: 1

Rep: Reputation: 202Reputation: 202Reputation: 202
Stuck again on passing bash non-command directives via expect.
I am trying to send something similar to -

Code:
#! /bin/bash
SERVER=/test/ab/serverlist
USER=someuser

#login to each server -
for ip in $(cat $SERVER)
do
/usr/bin/expect <<iHERESTRING
set timeout 100
spawn ssh -o StrictHostKeyChecking=no -t $USER@$ip
expect "*password: "
send "Mypassword\r"
expect "$ "
# taking backups of files--
send "sudo cp -p /path/to/file1 /path/to/file1.`date +"%d-%m-%Y"`; sudo cp -p /path/to/file1 /path/to/file1.`date +"%d-%m-%Y"`"
expect "*$USER:"
send "Mypassword\r"
expect "$ "
#Generate userlist that may be removed
send " sudo cat /etc/sssd/sssd.conf | grep -i simple_allow_users | fmt -1| grep -Ev \"simple\|=\"|sed -e s/,//g>/home/someuser/user_list \r"
expect "$ "
#remove users
send "for j in \$(cat /home/someuser/user_list); do if ![[ \"\$j\" =~ ^(AAAAAAA|BBBBBBB|CCCCCCC) ]] && [[ \$j != n* ]] ; then echo "deleting \$j" ; fi; done;/r"
iHERESTRING
done
I have tried the highlighted part this way as well -

Code:
send "for j in \$(sudo grep -iP simple_allow_users /etc/sssd/sssd.conf | fmt -1 | grep -Ev "simple|="| sed -e s/,//g); do if ![[ "\$j" =~ ^(AAAAAAA|BBBBBBB|CCCCCCC) ]] && [[ \$j != n* ]] ; then echo "deleting \$j" ; fi; done;/r"
expect "$ "
The backup files are created successfully but I am getting the following error code - (similar for both attempts)
Code:
extra characters after close-quote
    while executing
"send " for j in $(cat /home/someuser/user_list); do if ![[ \"$j\" =~ ^
I had hoped that this was a syntactical error and I escaped double quotes using a backslash, but that doesn't help. (Either I am not not escaping enough characters, or the error is not known to me.)

Code:
send "for j in \$(cat /home/someuser/user_list); do if ![[ \"\$j\" =~ ^(AAAAAAA|BBBBBBB|CCCCCCC) ]] && [[ \$j != n* ]] ; then echo \"deleting \$j\" ; fi; done;/r"
May I get a pointer as to how I may use this for loop with expect ?

Environments -
A. 2.6.32-696.10.3.el6.x86_64/bash 4.1.2(1)/expect 5.44.1.15-5 (running from)
B. 3.10.0-862.11.6.el7.x86_64/bash 4.2.46(2) (target machine)

Last edited by Honest Abe; 10-01-2018 at 09:02 PM.
 
Old 10-05-2018, 02:33 AM   #8
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,862
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
Please don't do this... there is newusers(8) for adding multiple users
 
1 members found this post helpful.
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
[root@fugo trace]# sh expect.sh expect.sh: line 9: expect: command not found sivaloga Linux - Kernel 1 08-22-2013 04:29 AM
[SOLVED] Expect Function Help Using Bash metallica1973 Programming 15 03-13-2013 09:59 AM
Need help with BASH-scripts and EXPECT ElFredrico Linux - General 17 05-17-2012 11:51 PM
[SOLVED] /usr/bin/expect : Script to check server load using both expect and bash Soji Antony Programming 1 07-27-2010 11:27 PM
expect/bash sk8guitar Programming 5 10-06-2009 12:00 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 10:56 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration