issue with my bash script - bash

I'm quite new to bash. I'm trying to achieve a quite easy thing but it looks like extremely complicated...
I'm trying to read variables from a file (ip address, username, password) and connect to firewalls to change them.
The file is around 100 lines and I'm trying to read one by one, chose the IP address of the firewall from the 2nd column, first username and pass from the 3rd and 4th column, second username and pass from the 5th and the 6th column.
The file looks like:
Office1 IP uname1 pass1 uname2 pass2
Office2 IP uname1 pass1 uname2 pass2
All the raws are separated by tabs
The script looks like this one:
#!/bin/bash
office=`awk '{print $1}' TEST.txt`
user1=`awk '{print $3}' TEST.txt`
user2=`awk '{print $5}' TEST.txt`
password1=`awk '{print $4}' TEST.txt`
password2=`awk '{print $6}' TEST.txt`
/usr/bin/expect <<EOF
for p in `cat TEST.txt | awk '{print $2}'`
do
# echo $user1 ", " $password1", " $user2", "$password2"
set timeout 20
spawn telnet $p
expect "login:"
send "cipollone"
send \r
expect "password:"
send "mypass"
send \r
expect ">"
send "set admin user $user1 password $password1 privilege all"
send \r
expect ">"
send "set admin user $user2 password $password2 privilege all"
send \r
expect ">"
send "save"
send \r
expect ">"
send "exit"
send \r
send \r
send \r
# echo $office $p "DONE"
# echo \r
# echo $office $p "DONE" >> results.txt
# echo \r >> results.txt
exit
EOF
done < TEST.txt

The general structure of your script should be:
while read office ip user1 password1 user2 password2
do
/usr/bin/expect <<EOF
...
EOF
done < TEST.txt

Follow #Barmar's advice. Additionally:
for p in `cat TEST.txt | awk '{print $2}'` is not valid expect
when using here-docs, the terminating word EOF must appear without any leading whitespace.

Related

How to generate password for users and send them email?

I have a file with the list of users, I found a way how to generate for each user random password, but didn't know how to select each user their username and password and send them via email (email example username#example.com)
the script sends each user an email with each password from the file, that is, if there are 5 users, then each of them will receive 5 emails and 5 different passwords.
Generate Password
#!/bin/bash
file=$(cat /tmp/user_names.txt)
for line in $file
do
passrand=$(openssl rand -base64 12)
echo -e "$line" "$passrand"
done
Send email
#!/bin/bash
for maillogin in $(cut -f1 -d ' ' /tmp/u_p.txt) || pass=$(cut -f2 -d' ' /tmp/u_p.txt)
do
for pass in $(cut -f2 -d' ' /tmp/u_p.txt)
do
echo "Hello your login is" $maillogin "and password is" $pass | mail -s "Test Mail" "${maillogin}#example.com"
done
done
Your first script is ok.
For the second, you need to read line after line. So, do not use nested loops to read login and password. Use read command and decompose line:
#!/bin/bash
UP_FILE="/tmp/u_p.txt"
while read -r
do
# maillogin <- line without any characters *after first* space (space included)
maillogin="${REPLY%% *}"
# pass <- line without any characters *before first* space (space included)
pass="${REPLY#* }"
echo "Hello your login is $maillogin and password is $pass" | mail -s "Test Mail" "${maillogin}#example.com"
done < "${UP_FILE}"

How to Assign User-Account Name and Directory to Array in Bash/sh?

I am attempting to create a bash script for the STIG test with the vulnerability ID V-72017 on a Red Hat Enterprise Linux (RHEL) system. I am tasked with making sure all user permissions have the octal value of 0750 or less
I have the ability to gather the permission octal value a user by using
stat -c "%a" /home/$username
I am trying to create a $username (or directory) array by utilizing the command (outputs name of each user on the system):
eval getent passwd {$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)..$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)} | cut -d: -f1
I plan to map this output to an array, possibly a while loop. Is this a possible solution?
Syntax Error from the following:
(eval getent passwd {$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)..$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)} | cut -d: -f1) | while read -r line
do
myarray+=line
stat -c "%a" /home/$line
done
Desired Output Case 1:
Users:
rob
bob
Exit Fail: bob has permission octal value 0755.
Desired Output Case 2:
Users:
rob
bob
Exit Pass: All users have permission octal value of 0750 or less.
You have found all login users. Regexp can be used to check home dir's permissions.
echo "Users: "
(eval getent passwd {$(awk '/^UID_MIN/ {print $2}' /etc/login.defs)..$(awk '/^UID_MAX/ {print $2}' /etc/login.defs)} | cut -d: -f1) | while read -r line
do
echo $line
perm=$(stat -c "%a" /home/$line)
[[ "$perm" =~ [0-7][0,1,4,5][0] ]] || echo "Exit fail: $line has permission octal value $perm"
done
Maybe you want to adjust the output form.
It is suggested to avoid using eval as much as possible. All the more
if you are investigating the system security status. Please try the
following instead:
#!/bin/bash
perm=0750 # system policy
uid_min=$(sed -n '/^UID_MIN/ s/[^0-9]*\([0-9]\+\).*/\1/p' "/etc/login.defs")
uid_max=$(sed -n '/^UID_MAX/ s/[^0-9]*\([0-9]\+\).*/\1/p' "/etc/login.defs")
# read /etc/passwd and process line by line
while IFS=: read -ra a; do
# now ${a[0]} holds username and ${a[2]} holds uid
if (( ${a[2]} >= uid_min && ${a[2]} <= uid_max )); then
# narrow down the users whose uid is within the range
users+=("${a[0]}")
# check the user's permission
userperm="0$(stat -c "%a" "/home/${a[0]}")"
if (( (~ perm) & userperm )); then
# the user's permission exceeds the limitation $perm
fail+=("$(printf "%s has permission octal value 0%o." "${a[0]}" "$userperm")")
fi
fi
done < "/etc/passwd"
echo "Users:"
for i in "${users[#]}"; do
echo "$i"
done
if (( ${#fail[#]} == 0 )); then
printf "Exit Pass: All users have permission octal value of 0%o or less.\n" "$perm"
else
for i in "${fail[#]}"; do
printf "Exit Fail: %s\n" "$i"
done
fi
Hope this helps.

If statement matching words separated by special char

I'm new to unix. I have a file with an unknown amount of lines in format: "password, username" and I'm trying to make a function that checks this file against user inputted login.
What I have so far:
Accounts file format:
AAA###, firstname.lastname
echo "Please enter Username:"
read username
if cut -d "," -f2 accounts | grep -w -q $username
then
echo "Success"
fi
This function will return Success for inputs "firstname" "lastname" and "firstname.lastname" when I only want it to return for "firstname.lastname"
Any help would be appreciated.
You could go for an exact match, with ^ and $ anchors, like this:
echo "Please enter Username:"
read username
if cut -d "," -f2 accounts | grep -q "^$username$"; then
echo "Success"
fi
While this would work even when the user gives an empty input, you might want to explicitly check for that.
If you loop over the file within the shell, you can use string equality operators instead of regular expressions:
read -rp "enter Username (first.last): " username
shopt -s extglob
found=false
while IFS=, read -r pass uname _othertext; do
# from your question, it looks like the separator is "comma space"
# so we'll remove leading whitespace from the $uname
if [[ "$username" = "${uname##+([[:blank:]])}" ]]; then
echo "Success"
found=true
break
fi
done < accounts
if ! $found; then
echo "$username not found in accounts file"
fi
while read loops in the shell are very slow compared to grep, but depending on the size of the accounts file you may not notice.
Based on your comment, the issue is that the field separator is a comma then a space, not just a comma. cut can't do multi-character delimiters, but awk can. In your code, replace
cut -d "," -f2
with
awk -F ", " '{print $2}'
By the way, there are a few things needed to guard against user input:
# Use "-r" to avoid backslash escapes.
read -rp "Please enter Username:" username
# Always quote variables ("$username").
# Use "grep -F" for fixed-string mode.
# Use "--" to prevent arguments being interpreted as options.
if awk -F ", " '{print $2}' accounts | grep -wqF -- "$username"; then
echo "Success"
fi

Shell Script : Assign the outputs to different variables

In a shell script I need to assign the output of few values to different varialbes, need help please.
cat file1.txt
uid: user1
cn: User One
employeenumber: 1234567
absJobAction: HIRED
I need to assign the value of each attribute to different variables so that I can call them them in script. For example uid should be assigned to a new variable name current_uid and when $current_uid is called it should give user1 and so forth for all other attributes.
And if the output does not contain any of the attributes then that attribute value should be considered as "NULL". Example if the output does not have absJobAction then the value of $absJobAction should be "NULL"
This is what I did with my array
#!/bin/bash
IFS=$'\n'
array=($(cat /tmp/file1.txt | egrep -i '^uid:|^cn:|^employeenumber|^absJobAction'))
current_uid=`echo ${array[0]} | grep -w uid | awk -F ': ' '{print $2}'`
current_cn=`echo ${array[1]} | grep -w cn | awk -F ': ' '{print $2}'`
current_employeenumber=`echo ${array[2]} | grep -w employeenumber | awk -F ': ' '{print $2}'`
current_absJobAction=`echo ${array[3]} | grep -w absJobAction | awk -F ': ' '{print $2}'`
echo $current_uid
echo $current_cn
echo $current_employeenumber
echo $current_absJobAction
Output from sh /tmp/testscript.sh follows:
user1
User One
1234567
HIRED
#!/usr/bin/env bash
# assuming bash 4.0 or newer: create an associative array
declare -A vars=( )
while IFS= read -r line; do ## See http://mywiki.wooledge.org/BashFAQ/001
if [[ $line = *": "* ]]; then ## skip lines not containing ": "
key=${line%%": "*} ## strip everything after ": " for key
value=${line#*": "} ## strip everything before ": " for value
vars[$key]=$value
else
printf 'Skipping unrecognized line: <%s>\n' "$line" >&2
fi
done <file1.txt # or < <(ldapsearch ...)
# print all variables read, just to demonstrate
declare -p vars >&2
# extract and print a single variable by name
echo "Variable uid has value ${vars[uid]}"
Note that this must be run with bash yourscript, not sh yourscript.
By the way -- if you don't have bash 4.0, you might consider a different approach:
while IFS= read -r line; do
if [[ $line = *": "* ]]; then
key=${line%%": "*}
value=${line#*": "}
printf -v "ldap_$key" %s "$value"
fi
done <file1.txt # or < <(ldapsearch ...)
will create separate variables of the form "$ldap_cn" or "$ldap_uid", as opposed to putting everything in a single associative array.
Here's a simple example of what you are trying to do that should get you started. It assumes 1 set of data in the file. Although a tad brute-force, I believe its easy to understand.
Given a file called file.txt in the current directory with the following contents (absJobAction intentionally left out):
$ cat file1.txt
uid: user1
cn: User One
employeenumber: 1234567
$
This script gets each value into a local variable and prints it out:
# Use /bin/bash to run this script
#!/bin/bash
# Make SOURCEFILE a readonly variable. Make it uppercase to show its a constant. This is the file the LDAP values come from.
typeset -r SOURCEFILE=./file1.txt
# Each line sets a variable using awk.
# -F is the field delimiter. It's a colon and a space.
# Next is the value to look for. ^ matches the start of the line.
# When the above is found, return the second field ($2)
current_uid="$(awk -F': ' '/^uid/ {print $2}' ${SOURCEFILE})"
current_cn="$(awk -F': ' '/^cn/ {print $2}' ${SOURCEFILE})"
current_enbr="$(awk -F': ' '/^employeenumber/ {print $2}' ${SOURCEFILE})"
current_absja="$(awk -F': ' '/^absJobAction/ {print $2}' ${SOURCEFILE})"
# Print the contents of the variables. Note since absJobAction was not in the file,
# it's value is NULL.
echo "uid: ${current_uid}"
echo "cn: ${current_cn}"
echo "EmployeeNumber: ${current_enbr}"
echo "absJobAction: ${current_absja}"
~
When run:
$ ./test.sh
uid: user1
cn: User One
EmployeeNumber: 1234567
absJobAction:
$

Bash script for identifying users

I think I may have approached this problem the wrong way and I could really use a hand here.
I'm trying to print a report to the screen using awk. I want to list the users logged in and their full names next to each user. The only way I could figure it out is below, but it only shows my own info. Can I add it into a loop somehow to achieve this or did I go about it completely wrong?
This is what I have:
echo "User name" "|" "Full name"
echo "--------------------------"
echo -n "$USER " ; awk -v user="$USER" -F":" 'user==$1{print$5}' /etc/passwd
The $USER variable just contains your username.
You can pipe the who command to get the list of logged in users.
echo "User name" "|" "Full name"
echo "--------------------------"
who | while read username rest; do
echo -n "$username " ; awk -v user="$username" -F":" 'user==$1{print$5}' /etc/passwd
done
Whenever you find yourself writing a loop in shell to process text, you have the wrong solution.
who | awk '
BEGIN { print "User name|Full name\n--------------------------" }
NR==FNR { name[$1] = $5; next }
{ print $1, name[$1] }
' FS=":" /etc/passwd FS=" " -
Shell is just an environment from which to call tools and it has a language to sequence those calls. The shell tool to process text is awk.

Resources