Storing values using if inside while loop BASH - linux

I am working with a new script that reads from a log file and then stores the ips which match one of the two patterns:
Either its a failed attempt or its a failed attempt using ssh.
My code runs good, but the problem is that when the while condition finishes, when I want to call the variable which stores all the IPs it only shows the last one.
#!/bin/bash
while IFS=";" read -r p || [ -n "$p" ]
do
first=$(echo $p | sed -E -e "s/[[:blank:]]+/;/g" | cut -d ";" -f 6)
if [[ $first == "Failed" ]];
then
echo "ADVERTENCIA - ATAC DDOS - !"
x="$(echo $p | sed -E -e "s/[[:blank:]]+/;/g" | awk -F ";" '{print $11}') "
elif [[ $first == "pam_unix(sshd:auth):" ]];
then
echo "ADVERTENCIA - LOGUEIG DE SSH - ! !"
y="$(echo $p | sed -E -e "s/[[:blank:]]+/;/g" | awk -F ";" '{print $15}' | cut -b 7-19)"
fi
done < syslog.txt
(IFS=""; sort <<< "$x") | uniq -c
#This comand only prints the last ip, but I want to print the whole IP list.
My syslog text:
Apr 15 00:00:11 spark sshd[7812]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=218.25.208.92 user=root
Apr 15 11:38:58 spark sshd[13924]: Failed password for root from 183.3.202.111 port 22064 ssh2
Apr 15 11:38:58 spark sshd[13924]: Failed password for root from 183.3.202.111 port 22064 ssh2
Apr 15 00:00:11 spark sshd[7812]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=218.25.208.92 user=root
Current output:
1 183.3.202.111
1 218.25.208.92
What it should be really printing:
2 183.3.202.111
2 218.25.208.92

Each time you assign a value to x you're overwriting the previous version:
x="$(echo $p | sed -E -e "s/[[:blank:]]+/;/g" | awk -F ";" '{print $11}') "
Assuming your intention is to append new ip's onto the end of x, you have a few options, eg:
# use "+=" to append to variable
x+="$(echo $p | sed -E -e "s/[[:blank:]]+/;/g" | awk -F ";" '{print $11}') "
# reference variable in the assignment, eg, x="${x}..."
x="${x}$(echo $p | sed -E -e "s/[[:blank:]]+/;/g" | awk -F ";" '{print $11}') "

Related

Recover network info for each network card in a for loop and do something with it with bash

I created a script that recovers info from a remote VM and generates a second script that creates a new ifcfg-eth* and replaces it in a local copy of the same VM we are migrating (basically the remote VM is in DHCP and we are migrating them in a new location with a static IP and info that we recover from the original VM):
#! /bin/bash
# Version 1.0
# Last review 27/01/2022
# Author Alessandro
# Define variables
RED='\033[0;31m'
NC='\033[0m' # No Color
NCC="\e[0m"
CYAN='\e[96m'
USER='user'
CWD=$(pwd)
# Checks if the IP exists after the script name
if [ -z "$1" ]; then echo -e "${RED}Where is the IP?${NC}" && exit 1; fi
echo -e "${CYAN}Starting...${NCC}"
# Stores the output of of 'script' in the variable $RAW_INPUT
RAW_INPUT=$(ssh -o StrictHostKeyChecking=no $USER#"$1" <<'SCRIPT'
# Script
HOSTNAMEVM=$(hostname -s)
GW=$(/sbin/ip route | awk '/default/ { print $3 }')
INTERFACE1=$(ip link | awk -F: ' $0 !~"lo|vir|wl|^[^1-2]" {print $2;getline}' | awk '{ gsub (" ", "", $0); print}')
IP1=$(ifconfig "${INTERFACE1}" 2>/dev/null|awk '/inet addr:/ {print $2}'|sed 's/addr://')
SUBNET1=$(/sbin/ifconfig "${INTERFACE1}" | grep Mask | cut -d":" -f4)
INTERFACE2=$(ip link | awk -F: ' $0 !~"lo|vir|wl|^[^3-4]" {print $2;getline}' | awk '{ gsub (" ", "", $0); print}')
IP2=$(ifconfig "${INTERFACE2}" 2>/dev/null|awk '/inet addr:/ {print $2}'|sed 's/addr://')
SUBNET2=$(/sbin/ifconfig "${INTERFACE2}" | grep Mask | cut -d":" -f4)
echo "Hostname:${HOSTNAMEVM}"
echo "GW:${GW}"
echo "Network-card1:${INTERFACE1}"
echo "${INTERFACE1}_IP1:${IP1}"
echo "SUBNET_1:${SUBNET1}"
echo "Network-card2:${INTERFACE2}"
echo "${INTERFACE2}_IP2:${IP2}"
echo "SUBNET_2:${SUBNET2}"
echo "ENDECA:${ENDECA}"
SCRIPT
)
# Generate the variables for the pre_failover_script parsing the RAW_INPUT variable
VM_NAME=$( echo "${RAW_INPUT}" | awk -F"Hostname:" '/Hostname:/{print $2}')
# Verifies if the executed pre failover script already exists
if [ -f "${CWD}"/executed_pre_"${VM_NAME}".sh ]; then echo -e "${RED}WARNING${NC}:The pre failover script for${NC} ${CYAN}${VM_NAME}${NC} has been already executed!" && exit 1; fi
JOB_ID=$("${CWD}"/list_jobs.sh | grep "${VM_NAME}" | awk -F"mnt/" '/mnt/{print $2}')
# Checks if a job exists for the vm name
if [ -z "${JOB_ID}" ]; then
echo -e "${RED}The job ID for${NC} ${VM_NAME} ${RED}doesn't exist.${NC}";
exit 1;
fi
IP_GATEWAY=$(echo "${RAW_INPUT}" | awk -F"GW:" '/GW:/{print $2}')
INTERFACE_1=$( echo "${RAW_INPUT}" | awk -F"card1:" '/card1:/{print $2}')
IP_1=$( echo "${RAW_INPUT}" | awk -F"IP1:" '/IP1:/{print $2}')
SUB_1=$( echo "${RAW_INPUT}" | awk -F"NET_1:" '/NET_1:/{print $2}')
INTERFACE_2=$( echo "${RAW_INPUT}" | awk -F"card2:" '/card2:/{print $2}')
IP_2=$( echo "${RAW_INPUT}" | awk -F"IP2:" '/IP2:/{print $2}')
SUB_2=$( echo "${RAW_INPUT}" | awk -F"NET_2:" '/NET_2:/{print $2}')
# Prints the value of ENDECA in case the Symbolic Link has been removed
ENDECA=$( echo "${RAW_INPUT}" | awk -F"ENDECA:" '/ENDECA:/{print $2}')
# Removing existing pre_failover_script file for the ECL2 instance in current directory
rm -rf "${CWD}"/pre_failover_script_"${VM_NAME}".sh
# Define pre_failover_script variable
PRE_FAILOVER_SCRIPT_LOCATION="${CWD}"/pre_failover_script_${VM_NAME}.sh
# Generate ifcfg-eth* file based on information gathered above
echo "#! /bin/bash
PATH_TO_JOB_ID=/opt/dbtk/mnt/${JOB_ID}
PATH_TO_UDEV_NET_RULES_FILES=/etc/udev/rules.d
PATH_TO_GATEWAY_FILE=/etc/sysconfig/network
CLOUD_INIT_LOCAL_STARTUP_SCRIPT=/etc/rc3.d/S50cloud-init-local
CLOUD_INIT_STARTUP_SCRIPT=/etc/rc3.d/S51cloud-init
CLOUD_CONFIG_STARTUP_SCRIPT=/etc/rc3.d/S52cloud-config
CLOUD_FINAL_STARTUP_SCRIPT=/etc/rc3.d/S53cloud-final
PATH_TO_IFCFG_FILES=/etc/sysconfig/network-scripts
rm -f \$PATH_TO_JOB_ID\$PATH_TO_UDEV_NET_RULES_FILES/70-persistent-net.rules*
echo \"GATEWAY=${IP_GATEWAY}\" >> \$PATH_TO_JOB_ID\$PATH_TO_GATEWAY_FILE
rm -f \$PATH_TO_JOB_ID\$CLOUD_INIT_LOCAL_STARTUP_SCRIPT
rm -f \$PATH_TO_JOB_ID\$CLOUD_INIT_STARTUP_SCRIPT
rm -f \$PATH_TO_JOB_ID\$CLOUD_CONFIG_STARTUP_SCRIPT
rm -f \$PATH_TO_JOB_ID\$CLOUD_FINAL_STARTUP_SCRIPT" > "${PRE_FAILOVER_SCRIPT_LOCATION}"
echo "sed '/^BOOTPROTO/d' \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1} > \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.new
{
echo 'NAME=${INTERFACE_1}'
echo 'IPADDR=${IP_1}'
echo 'NETMASK=${SUB_1}'
echo 'BOOTPROTO=static'
} >> \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.new
cp \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1} \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.ori
mv \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.new \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}
rm \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.ori" >> "${PRE_FAILOVER_SCRIPT_LOCATION}"
echo "sed '/^BOOTPROTO/d' \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2} > \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2}.new
{
echo 'NAME=${INTERFACE_2}'
echo 'IPADDR=${IP_2}'
echo 'NETMASK=${SUB_2}'
echo 'BOOTPROTO=static'
} >> \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2}.new
cp \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2} \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2}.ori
mv \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2}.new \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2}
rm \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_2}.ori" >> "${PRE_FAILOVER_SCRIPT_LOCATION}"
The script works perfectly, problem is that now I'm having multiple VMs with 3 or 1 NIC instead of 2 and I was wondering if there was a way to make a for loop that does the same thing but for each Network card. As you can see now I'm generating 'manually' the ifcfg-eth for each NC based on the info collected before:
{
echo 'NAME=${INTERFACE_1}'
echo 'IPADDR=${IP_1}'
echo 'NETMASK=${SUB_1}'
echo 'BOOTPROTO=static'
} >> \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.new
cp \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1} \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.ori
mv \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.new \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}
rm \$PATH_TO_JOB_ID\$PATH_TO_IFCFG_FILES/ifcfg-${INTERFACE_1}.ori" >> "${PRE_FAILOVER_SCRIPT_LOCATION}"```
Now it's no longer convenient but I'm not sure if what I'm trying to do is achievable.

Shell script printf not printing variables

i'm new in stackoverflow, I’m trying to print something like that.
From: email#domain.com
To: rcpt#domain.com
Date: date
LOG_ID: ID
I’m reading the Postfix logs, which contains the following text:
Oct 28 10:42:48 host postfix/pickup[8779]: 4B0782A016E: uid=1000 from=<from#from.com>
Oct 28 10:42:48 host postfix/cleanup[14607]: 4B0782A016E: message-id=<20211028134248.4B0782A016E#mail.democloud.com>
Oct 28 10:42:48 host postfix/qmgr[2656]: 4B0782A016E: from=<from#from.com>, size=400, nrcpt=1 (queue active)
Oct 28 10:42:50 host postfix/smtp[14610]: 4B0782A016E: to=<rcpt#rcpt.com>, relay=gmail-smtp-in.l.google.com[172.217.192.26]:25, delay=1.7, delays=0/0/1.1/0.64, dsn=2.0.0, status=sent (250 2.0.0 OK 1635428569 jl7si2828702qvb.144 - gsmtp)
Oct 28 10:42:50 host postfix/qmgr[2656]: 4B0782A016E: removed
To perform the task I created the following shell script:
#!/bin/bash
if [ $# -ne 1 ] ; then
echo "
Ingresar cuenta de correo
Ej.: $0 rcpt#rcpt.com
"
exit 1
fi
RCPT="$1"
MAILOG="/var/log/mail.log"
STATS_DIR="/tmp"
echo -e "\n==== Detalle Total de Envios a <$1> ==== "
echo
QUEUEID=`grep -w "to=<${RCPT}>" ${MAILOG} | grep -E "status=(sent|deferred|bounced)" | awk -F"smtp" '{print $2}' | cut -d":" -f2 | sed -e s'/ //'g | sort | uniq`
for QID in ${QUEUEID} ; do
EMAIL=`grep ${QID} /var/log/mail.log | sed -r 's/^([^;]*;)[^;]*;/\1/' | awk 'BEGIN{FS=OFS=" "} {print $7}' | grep -w "from=" | tr -d , | grep -E -o "\b[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b"`
RCPT=`grep ${QID} /var/log/mail.log | sed -r 's/^([^;]*;)[^;]*;/\1/' | awk 'BEGIN{FS=OFS=" "} {print $7}' | grep -w "to=" | tr -d , | grep -E -o "\b[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b"`
DATE=`grep ${QID} /var/log/mail.log | sed -r 's/^([^;]*;)[^;]*;/\1/' | awk 'BEGIN{FS=OFS=" "} {print $1, $2,$3}'`
printf "From: $s\nDestinatario: $s\nFecha: $s\nLOG_ID: $s\n\n" "${EMAIL}" "${RCPT}" "${DATE}" "${QID}"
done
exit 0
My problem is running printf, the script output is empty.
What’s the problem? Can you help me improve the script, thank you.
Regards,

How to direct linux bash script output to one line per host

I'd like to change my script(s) so that the command output is separated by a comma and is all on one line per host. Any ideas on how I can achieve this:
1st Script:
#!/bin/bash
for i in `cat ~/hostnames.txt`
do
ssh -q $i 'bash -s' < server_info.sh
done
2nd Script (server_info.sh):
#!/bin/bash
echo -n "Name: "
uname -n
echo -n "CPUs: "
cat /proc/cpuinfo* | grep processor | wc -l
echo -n "Memory (kb): "
cat /proc/meminfo | grep MemTotal: | awk '{print $2}'
echo -n "Current Kernel: "
uname -a | awk '{print $3}'
echo -n "IP: "
hostname -i
echo -e
Changing your 1st script:
#!/bin/bash
for i in cat ~/hostnames.txt
do
ssh -q $i 'bash -s' < server_info.sh
done | awk -v RS= '{$1=$1}1'
Note: Your server_info.sh can be a lot more optimized.For example:
cat /proc/meminfo | grep MemTotal: | awk '{print $2}'
could be changed to:
awk '/MemTotal:/{print $2}' /proc/meminfo

awk field separator using ":" and ignore spaces within ":"

I'm trying to automate the user creation(Bulk Users) in Linux. So, I've created a text file with all the needed parameters for user's creation.
The Text file contains the below content:
tom:tom1:588:0:test user1:/home/test:/bin/false
harry:harry1:589:0:test test2:/hom/test2:/bin/nologin
Trying to use awk command to print the column's in for loop and create the users with the information's from /tmp/user.txt
for userdetails in $(cat /tmp/user.txt)
do
user=`echo $userdetails | awk -v FS=: '{print $1}'`
passwd=`echo $userdetails | awk -v FS=: '{print $2}'`
uid=`echo $userdetails | awk -v FS=: '{print $3}'`
gid=`echo $userdetails | awk -v FS=: '{print $4}'`
comment=`echo $userdetails | awk -v FS=: '{print $5}'`
home_dir=`echo $userdetails | awk -v FS=: '{print $6}'`
user_shell=`echo $userdetails | awk -v FS=: '{print $7}'`
useradd -d "$home_dir" -c "$comment" -s "$user_shell" -u "$uid" -g "$gid" "$user"
echo "$passwd" | passwd "$user" --stdin;
done
Actual Output:
useradd: invalid home directory ''
passwd: Unknown user name 'tom'.
useradd: invalid home directory ''
passwd: Unknown user name 'user1'.
useradd: invalid home directory ''
passwd: Unknown user name 'harry'.
useradd: invalid home directory ''
passwd: Unknown user name 'test2'.
What I'm doing wrong ?
P.S : I am aware there is a command called newusers in Linux, but i need to check the same via script to create bulk users in Linux.
idk how you got started on that track but you're off-base. Just use a shell loop:
while IFS=':' read -r user passwd uid gid comment home_dir user_shell; do
useradd -d "$home_dir" -c "$comment" -s "$user_shell" -u "$uid" -g "$gid" "$user"
echo "$passwd" | passwd "$user" --stdin;
done < /tmp/user.txt
The above is just showing how to read the file contents into variables, it assumes you know what you're doing with the "useradd" and "passwd" lines.
I would use while for read a file by line so the entire line will be stored in the variable you choose( in my case: line) . Then you could echo variable and awk will do the rest of the work. I prefer $() than `` so I use both for now. For this much data you could create a loop into the while to make code sorter and better manageable.
cat /tmp/user.txt | while read line
do
user=`echo $line | awk -F ":" '{print $1}'`
passwd=$(echo $line | awk -F ":" '{print $2}')
uid=$(echo $line | awk -F ":" '{print $3}')
gid=$(echo $line | awk -F ":" '{print $4}')
comment=$(echo $line | awk -F ":" '{print $5}')
home_dir=$(echo $line | awk -F ":" '{print $6}')
user_shell=$(echo $line | awk -F ":" '{print $7}')
useradd -d "$home_dir" -c "$comment" -s "$user_shell" -u "$uid" -g "$gid" "$user"
echo "$passwd" | passwd "$user" --stdin;
done
If you are stuck with awk, you can use Awk Global substitution:
awk '{gsub(/:/,"-")} 1' your_file
This is also feasible using sed with the g flag ( Sed Global substitution ):
sed "s/:/-/g" your_file
In both casesn your edited lines would be printed to the screen.

how to cut substring from linux command "who"

command who returns list of users logged to server
[admin#DB01ATK ~]$ who
adm_drodmann pts/3 2015-07-01 08:57 (10.129.12.77)
adm_ssmith pts/4 2015-07-01 02:11 (10.129.12.76)
adm_kholdman pts/2 2015-06-30 23:08 (10.129.12.45)
the point is to assign to variable, value of username($1) where terminal($2) is result from command
ps aux | grep screen
question asked question answered:
PTS=$(awk '{print $7}' <<< $(ps aux | grep screen) )
who | while read CMD;
do
res=$(awk '{print $2}' <<< "$CMD")
if [ "$res" = "$PTS" ]
then
echo "logged as $(awk '{print $1}' <<< "$CMD")"
fi
done;
:-)
As per your comment I expand my solution. You want to say:
ptw=$(ps aux | awk '/screen/ {print $7}')
while IFS=read -r user res _;
do
[ "$res" = "$PTS" ] && echo "logged as $user"
done < <(who)

Resources