Got it working, here is the code: https://github.com/MNFaust/Linux_Memory_Mining/blob/master/subScripts/gdb_mine.sh
THANK YOU!
I am working on a subscript that is being called in a program to grab data from Memory Addresses and I am trying to automate the Stack and Heap mem dumps with GDB. Everything is working except for the memory address variable calls. Can someone please let me know if they see where I am going wrong with this:
#!/bin/bash
#========================================
#GDB processes: Gaining data
#By: Joshua Faust
#========================================
echo '------------------------------'
echo 'Process ID Data Required: '
echo '------------------------------'
echo -n "What is the PID you are inquiring about: "; read PID
awk '{print $1,$6}' /proc/$PID/maps > "${PID}_maps"
# Grab the heap memory address for the PID
heapAddress=`cat /proc/$PID/maps | grep heap | awk '{print $1}'` #Grab the HEAP address in memory
heapAddressRemove=${heapAddress/-/" 0x"} #reove the -, add 0x to denote a hex address for GDB
heapAddressFinal= echo "$heapAddressRemove" | awk '$0="0x"$0' #adds a 0x to the from of the mem address to denote a hex address
# Grab the stack memory address for the PID
stackAddress=`cat /proc/$PID/maps | grep stack | awk '{print $1}'` #Grab the STASCK address in memory
stackAddressRemove=${stackAddress/-/" 0x"} #remove the - in the memory address for GDB
stackAddressFinal= echo "$stackAddressRemove" | awk '$0="0x"$0' # adds a 0x to the front of the memory address to denote a hex addess
clear #Clear data on screen
echo '---------------------------'
echo 'Starting Mining Processes '
echo '---------------------------'
#
# Ask user if you would like to view the memory map segments of the processes they chose.
#
echo -n "Would you like to see PID $PID memory map? (y or n) "; read pidAnswer
if [ $pidAnswer == "y" ] || [ $pidAnswer == "Y" ]
then
echo
cat "${PID}_maps"
else
echo 'continuing to GDB session...'
echo
fi
#
# Start of the GDB session:
#
echo -n "Are you ready to start a GDB session on $PID? (y or n) "; read mineAnswer
if [ $mineAnswer == "y" ] || [ $mineAnswer == "Y" ]
then
echo '--------------------------------------------------------------------------------'
echo ' Getting Register Information '
echo ''
echo 'To dump: dump [memory,binary,tekhex, verilog] FileLocation Memory Segment Range '
echo 'To quit: quit'
echo Heap Address: $heapAddressFinal
echo Stack Address: $stackAddressFinal
echo '--------------------------------------------------------------------------------'
echo
echo -n 'Would you like to dump the stack or the heap? '; read dump
if [ $dump == "heap" ] || [ $dump == "Heap" ]
then
gdb --pid $PID -ex 'dump memory HeapDump.hex $heapAddressFinal' # to add another command -ex command
echo 'Data dumped to HeapDump.hex'
elif [ $dump == "stack" ] || [ $dump == "Stack" ]
then
gdb --pid $PID -ex 'dump memory StackDump.hex $stackAddressFinal'
echo 'Data dumped to StackDump.hex'
fi
else
echo
echo 'closing program....'
exit 1
fi
rm -rf PID.txt #remove the temp file created to hold the PID
Here is the output where I am having issues. When I echo and call the variable stackAddressFinal and heapAddressFinal, they do not populate on console. However, in a test script I build they do.
Are you ready to start a GDB session on 21505? (y or n) y
--------------------------------------------------------------------------------
Getting Register Information
To dump: dump [memory,binary,tekhex, verilog] FileLocation Memory Segment Range
To quit: quit
Heap Address:
Stack Address:
--------------------------------------------------------------------------------
Would you like to dump the stack or the heap?
You need to add quotes, like heapAddressFinal=`echo "$heapAddressRemove" | awk '$0="0x"$0'`. Hope this helps.
Related
I am currently writing the following script that logs into a remote server and runs couple of commands to verify the performance of the server and prints a message based on the output of those commands .But the ssh doesn't work and returns the stats of the server that hosts the script instead .
Script
#!/bin/bash
#######################
#Function to add hosts to the array
#the following function takes the ip addresses provided while the script is run and stores them in an array
#######################
Host_storing_func () {
HOST_array=()
for i in $# ;do
HOST_array+=(${i});
done
#echo ${HOST_array[*]}
}
#######################
#Calling above function
#######################
Host_storing_func "$#"
############################################################
#Collect Stats of Ping,memory,iowait time test function
############################################################
b=`expr ${#HOST_array[*]} - 1 `
for i in `seq 0 $b` ;do
sshpass -f /root/scripts/passwordFile.txt /usr/bin/ssh student35#${HOST_array[${i}]} << HERE
echo `hostname`
iowaittm=`sar 2 2|awk '/^Average/{print $5};'`
if [ $iowaittm > 10 ];then
echo "IO ==> BAD"
else
echo "IO ==> GOOD"
fi
memoryy=`free -m |grep Swap|awk '{if($2 == 0) print 0;else print (($4 / $2 ) * 100)}'`
if [ ${memoryy} < '10' ] ;then
echo "memory ==> good"
elif [[ "${memory}" -ge 0 ]] && [[ "${memory}" -le 10 ]];then
echo "No Swap"
else
echo "memory ==> bad"`enter code here`
fi
ping -w2 -c2 `hostname` | grep "packet loss"|awk -F, '{print $3}'|awk -F% '{print $1}'|sed 's/^ *//'|awk '{if ($1 == 0) print "Yes" ;else print "No"}'
HERE
done
Output : oc5610517603.XXX.com is the name of the source server
[root#oc5610517603 scripts]# ./big_exercise.sh 9.XXX.XXX.XXX 9.XXX.XXX.XXX
Pseudo-terminal will not be allocated because stdin is not a terminal.
oc5610517603.XXX.com
IO ==> GOOD
No Swap
ping: oc5610517603.ibm.com: Name or service not known
Pseudo-terminal will not be allocated because stdin is not a terminal.
oc5610517603.XXX.com
IO ==> GOOD
No Swap
ping: oc5610517603.XXX.com: Name or service not known
thanks for checking the script , I figured out a way to solve the problem
It is the sshpass command that is causing issue , you just have to put the opening HERE in single quotes if you want to use variables with in the HEREdoc but if the variables are calculated before ssh then you don't have to put opening HERE in single quotes
sshpass -f /root/scripts/passwordFile.txt /usr/bin/ssh -T student35#${i} << 'HERE'
after I changed the sshpass command as above my script worked
I have modified your script a bit.
As suggested by #chepner, I am not using the Host_storing_func.
Heredocs for sshpaas are somewhat tricky. You have to escape every back-tick and $ sign in the heredoc.
Notice the - before the heredoc start, it allows you to indent the heredoc body. Also, try to avoid back-ticks when you can. use $(command) instead.
Hope it helps.
#!/bin/bash
#######################
#Function to add hosts to the array
#the following function takes the ip addresses provided while the script is run and stores them in an array
#######################
array=( "$#" )
user="student35"
############################################################
#Collect Stats of Ping,memory,iowait time test function
############################################################
for host in ${array[#]}; do
sshpass -f /root/scripts/passwordFile.txt /usr/bin/ssh -l ${user} ${host} <<-HERE
thishost=\$(hostname)
echo "Current Host -> \$thishost";
iowaittm=\`sar 2 2|awk '/^Average/{print \$5}'\`
if [ \$iowaittm > 10 ]; then
echo "IO ==> BAD"
else
echo "IO ==> GOOD"
fi
memory=\$(free -m | grep Swap | awk '{if(\$2 == 0) print 0;else print ((\$4 / \$2 ) * 100)}')
if [ \${memory} < '10' ] ;then
echo "memory ==> good"
elif [[ "\${memory}" -ge 0 ]] && [[ "\${memory}" -le 10 ]]; then
echo "No Swap"
else
echo "memory ==> bad"\`enter code here\`
fi
ping -w2 -c2 \`hostname\` | grep "packet loss"|awk -F, '{print \$3}'|awk -F% '{print \$1}'|sed 's/^ *//'|awk '{if (\$1 == 0) print "Yes" ;else print "No"}'
HERE
done
I'm attempting to sweep an IP block totaling about 65,000 addresses. We've been instructed to use specifically ICMP packets with bash and find a way to parallelize it. Here's what I've come up with:
#!/bin/bash
ping() {
if ping -c 1 -W 5 131.212.$i.$j >/dev/null
then
((++s))
echo -n "*"
else
((++f))
echo -n "."
fi
((++j))
#if j has reached 255, set it to zero and increment i
if [ $j -gt 255 ]; then
j=0
((++i))
echo "Pinging 131.212.$i.xx IP Block...\n"
fi
}
s=0 #number of responses recieved
f=0 #number of failures recieved
i=0 #IP increment 1
j=0 #IP increment 2
curProcs=$(ps | wc -l)
maxProcs=$(getconf OPEN_MAX)
while [ $i -lt 256 ]; do
curProcs=$(ps | wc -l)
if [ $curProcs -lt $maxProcs ]; then
ping &
else
sleep 10
fi
done
echo "Found "$s" responses and "$f" timeouts."
echo /usr/bin/time -l
done
However, I've been running into the following error (on macOS):
redirection error: cannot duplicate fd: Too many open files
My understanding is I'm going over a resource limit, which I've attempted to rectify by only starting new ping processes if the existing processes count is less than the specified max, but this does not solve the issue.
Thank you for your time and suggestions.
EDIT:
There are a lot of good suggestions below for doing this with preexisting tools. Since I was limited by academic requirements, I ended up splitting the ping loops into a different process for each 12.34.x.x blocks, which although ugly did the trick in under 5 minutes. This code has a lot of problems, but it might be a good starting point for someone in the future:
#!/bin/bash
#############################
# Ping Subfunction #
#############################
# blocks with more responses will complete first since worst-case scenerio
# is O(n) if no IPs generate a response
pingSubnet() {
for ((j = 0 ; j <= 255 ; j++)); do
# send a single ping with a timeout of 5 sec, piping output to the bitbucket
if ping -c 1 -W 1 131.212."$i"."$j" >/dev/null
then
((++s))
else
((++f))
fi
done
#echo "Recieved $s responses with $f timeouts in block $i..."
# output number of success results to the pipe opened in at the start
echo "$s" >"$pipe"
exit 0
}
#############################
# Variable Declaration #
#############################
start=$(date +%s) #start of execution time
startMem=$(vm_stat | awk '/Pages free/ {print $3}' | awk 'BEGIN { FS = "\." }; {print ($1*0.004092)}' | sed 's/\..*$//');
startCPU=$(top -l 1 | grep "CPU usage" | awk '{print 100-$7;}' | sed 's/\..*$//')
s=0 #number of responses recieved
f=0 #number of failures recieved
i=0 #IP increment 1
j=0 #IP increment 2
#############################
# Pipe Initialization #
#############################
# create a pipe for child procs to write to
# child procs inherit runtime environment of parent proc, but cannot
# write back to it (like passing by value in C, but the whole env)
# hence, they need somewhere else to write back to that the parent
# proc can read back in
pipe=/tmp/pingpipe
trap 'rm -f $pipe' EXIT
if [[ ! -p $pipe ]]; then
mkfifo $pipe
exec 3<> $pipe
fi
#############################
# IP Block Iteration #
#############################
# adding an ampersand to the end forks the command to a separate, backgrounded
# child process. this allows for parellel computation but adds logistical
# challenges since children can't write the parent's variables
echo "Initiating scan processes..."
while [ $i -lt 256 ]; do
#echo "Beginning 131.212.$i.x block scan..."
#ping subnet asynchronously
pingSubnet &
((++i))
done
echo "Waiting for scans to complete (this may take up to 5 minutes)..."
peakMem=$(vm_stat | awk '/Pages free/ {print $3}' | awk 'BEGIN { FS = "\." }; {print ($1*0.004092)}' | sed 's/\..*$//')
peakCPU=$(top -l 1 | grep "CPU usage" | awk '{print 100-$7;}' | sed 's/\..*$//')
wait
echo -e "done" >$pipe
#############################
# Concat Pipe Outputs #
#############################
# read each line from the pipe we created earlier, adding the number
# of successes up in a variable
success=0
echo "Tallying responses..."
while read -r line <$pipe; do
if [[ "$line" == 'done' ]]; then
break
fi
success=$((line+success))
done
#############################
# Output Statistics #
#############################
echo "Gathering Statistics..."
fail=$((65535-success))
#output program statistics
averageMem=$((peakMem-startMem))
averageCPU=$((peakCPU-startCPU))
end=$(date +%s) #end of execution time
runtime=$((end-start))
echo "Scan completed in $runtime seconds."
echo "Found $success active servers and $fail nonresponsive addresses with a timeout of 1."
echo "Estimated memory usage was $averageMem MB."
echo "Estimated CPU utilization was $averageCPU %"
This should give you some ideas with GNU Parallel
parallel --dry-run -j 64 -k ping 131.212.{1}.{2} ::: $(seq 1 3) ::: $(seq 11 13)
ping 131.212.1.11
ping 131.212.1.12
ping 131.212.1.13
ping 131.212.2.11
ping 131.212.2.12
ping 131.212.2.13
ping 131.212.3.11
ping 131.212.3.12
ping 131.212.3.13
-j64 executes 64 pings in parallel at a time
-dry-run means do nothing but show what it would do
-k means keep the output in order - (just so you can understand it)
The ::: introduces the arguments and I have repeated them with different numbers (1 through 3, and then 11 through 13) so you can distinguish the two counters and see that all permutations and combinations are generated.
Don't do that.
Use fping instead. It will probe far more efficiently than your program will.
$ brew install fping
will make it available, thanks to the magic of brew.
Of course it's not as optimal as you are trying to build above, but you could start the maximum allow number of processes on the background, wait for them to end and start the next batch, something like this (except I'm using sleep 1s):
for i in {1..20} # iterate some
do
sleep 1 & # start in the background
if ! ((i % 5)) # after every 5th (using mod to detect)
then
wait %1 %2 %3 %4 %5 # wait for all jobs to finish
fi
done
I have a shell script that runs on Debian. It contains lots of functions and conditions. There is a menu and the user may choose different options and different outputs will be displayed. The user can always go back to the main menu and choose again some options then another output will be displayed. Of course each time the screen is cleared with "clear".
However when then output contains too many lines, I will be able to scroll up a little bit, but it will stop and I won't be able to scroll all the way to the first line I need to see. Being able to scroll up with the mousse wheel is the king of behavior I would like...
It looks like the problem comes from the xterm window, because it is fine with the normal terminal. However xterm is nice because I can setup the height and the width, as well as changing the colors...
Is there a way to increase this limitation from the script itself as I won't have the permission to change anything in the Debian environment...
I read that some people actually pipe the entire script to "less", I tried that, the problem is that I can't use the menu anymore...
Please find below the first script that is used to run the main one:
xterm -fg ivory -bg darkblue -fn 8x13bold -geometry 76x110+1700+0 -T "QC CHECK" -e /tests/SCRIPTS/QC/qc.sh
Below is a little sample of my script, but, it contains much more:
#!/bin/sh
stty erase ^H
function water
{
clear
echo -e "Current Survey : ${proj}"
echo -e "Current Sequence : ${seq}"
echo -e ""
echo -e " [ Trace QC Water Column ]"
echo -e ""
if [ $mincable -eq $maxcable ]
then
echo -e " Cable checked : $maxcable"
else
echo -e " Cables checked : ${mincable}-${maxcable}"
fi
echo -e " Max noise level : ${maxnoise}uB"
if [ ${skiptrace} -eq 0 ]
echo -e " Traces skipped : ${skiptrace}"
else
echo -e " Traces skipped : 1-${skiptrace}"
fi
echo -e ""
#############
water=`awk --field-separator=";" '($4>'$maxnoise') {print int(a=(($1-1)/'$nb_traces')+1) " " ($1-((int(a)-1)*'$nb_traces')) " " $4}' ${seq}_TraceAverages.txt | grep -v "USER_AVRMS_WC1" | grep -v "R32" | awk '{printf $1 " " $2 " " ("%*.*f\n"), 1, 2, $3}' | awk '($2>'$skiptrace')&&($1>='$mincable')&&($1<='$maxcable') {print $1 " - " $2 " - " $3}' | awk '{printf("%16s%6s%8s%6s%10s\n"), $1, $2, $3, $4, $5}' | awk '(NR>1) && (old != $1) {printf("%65s\n"), "'$sep_cable'"} {print; old=$1}'`
#############
count_water=`awk --field-separator=";" '($4>'$maxnoise') {print int(a=(($1-1)/'$nb_traces')+1) " " (b=($1-((int(a)-1)*'$nb_traces'))) " " $3}' ${seq}_TraceAverages.txt | grep -v "USER_AVRMS_WC1" | grep -v "R32" | awk '($2>'$skiptrace')&&($1>='$mincable')&&($1<='$maxcable') {print $3}' | wc -l`
#############
echo -e " ------------------------------------------------------"
echo -e ""
echo -e " Cable - Trace - RMS_WC"
echo -e ""
echo -e " ------------------------------------------------------"
echo -e ""
if [ $count_water -ge 1 ]
then
echo -e "$water"
else
setterm -term linux -back red -fore white
echo -e " Wow! No traces? Maybe decrease your values..."
setterm -term linux -default
fi
echo -e ""
setterm -term linux -back blue -fore white
echo -e " RMS_WC > ${maxnoise}uB = $count_water"
setterm -term linux -default
echo -e ""
echo -e " ------------------------------------------------------"
echo -e ""
}
# check for latest project in /tests
proj_temp
# if config file is missing go to config menu
if [ ! -e /tests/$proj/SCRIPTS/QC/config ]
then
config
fi
# force choice=1 and config_ok=1 to return to main menu when loop has run once (no problem when more than one)
choice=1
config_ok=1
while :
do
# do it all
if [ ${choice} -eq 1 2>/dev/null ]
then
choice=X
config_ok=1
# read configuration file
readconfig
main
# config menu and help
if [ ${seq} = "c" ]
then
config
elif [ ${seq} = "h" ]
then
help
elif [ ${seq} = "q" ]
then
clear
setterm -term linux -back magenta -fore white
echo ""
echo -e "\t Try me next time :*"
sleep 0.65
exit
fi
# config_ok=1 when configuration is done, meaning user returns to main menu after exiting config menu
if [ ${config_ok} -eq 1 ]
then
cd $input_dir
# check if file for requested sequence is valid
testline
# this function updates the awk script for signal QC check
awkscript
doall
choice
fi
# let the user choose what QC is wanted
elif [ ${choice} -eq 2 2>/dev/null ]
then
choice=X
# initialize values so that user can choose its own
init
menu
option
fi
case ${menu} in
1) deep
choice ;;
2) water
choice ;;
3) awkscript
signal
choice ;;
4) readconfig
awkscript
doall
choice ;;
esac
if [ ${choice} = "q" 2>/dev/null ]
then
exit
fi
done
As you can see I have many functions and many variables that are called is some "echo" which makes it hard for me to scroll up when there is too many lines, and, the user got also to scroll up and down to see everything and to choose and action.
Pipe the output thru less or more. There are options (hot keys) to go forth, back, search etc.
I could not find a way to scroll through the length of my output so what I did is a loop that is gradually increasing by increments of 0.1 the value of ${maxnoise} (with a condition on the number of line output) because this variable is actually the one conditioning how big is the output. It works fine this way so I consider my question answered.
Sorry for the trouble.. here is the code what works for a single server. I need help to loop it to multiple servers. Thanks in advance, Please help me out .
I need to know information of multiple servers like their:
Operating system info
Hostname and dns info
Network info
Who is online
Last logged in users and so on
logic is to pass the server names from a text file and display the same info for all the server in the file and write output to other file
Below are the different fucntions which fetch the details of a server. The same should be iterated for multiple servers..
#!/bin/bash
# grabsysinfo.sh - A simple menu driven shell script to to get information about your
# Linux server / desktop.
# Define variables
LSB=/usr/bin/lsb_release
# Purpose: Display pause prompt
# $1-> Message (optional)
function pause(){
local message="$#"
[ -z $message ] && message="Press [Enter] key to continue..."
read -p "$message" readEnterKey
}
# Purpose - Display a menu on screen
function show_menu(){
date
echo "---------------------------"
echo " Main Menu"
echo "---------------------------"
echo "1. Operating system info"
echo "2. Hostname and dns info"
echo "3. Network info"
echo "4. Who is online"
echo "5. Last logged in users"
echo "6. Free and used memory info"
echo "7. exit"
}
# Purpose - Display header message
# $1 - message
function write_header(){
local h="$#"
echo "---------------------------------------------------------------"
echo " ${h}"
echo "---------------------------------------------------------------"
}
# Purpose - Get info about your operating system
function os_info(){
write_header " System information "
echo "Operating system : $(uname)"
[ -x $LSB ] && $LSB -a || echo "$LSB command is not insalled (set \$LSB variable)"
#pause "Press [Enter] key to continue..."
pause
}
# Purpose - Get info about host such as dns, IP, and hostname
local dnsips=$(sed -e '/^$/d' /etc/resolv.conf | awk '{if (tolower($1)=="nameserver") print $2}')
write_header " Hostname and DNS information "
echo "Hostname : $(hostname -s)"
echo "DNS domain : $(hostname -d)"
echo "Fully qualified domain name : $(hostname -f)"
echo "Network address (IP) : $(hostname -i)"
echo "DNS name servers (DNS IP) : ${dnsips}"
pause
}
# Purpose - Network inferface and routing info
function net_info(){
devices=$(netstat -i | cut -d" " -f1 | egrep -v "^Kernel|Iface|lo")
write_header " Network information "
echo "Total network interfaces found : $(wc -w <<<${devices})"
echo "*** IP Addresses Information ***"
ip -4 address show
echo "***********************"
echo "*** Network routing ***"
echo "***********************"
netstat -nr
echo "**************************************"
echo "*** Interface traffic information ***"
echo "**************************************"
netstat -i
pause
}
# Purpose - Display a list of users currently logged on
# display a list of receltly loggged in users
function user_info(){
local cmd="$1"
case "$cmd" in
who) write_header " Who is online "; who -H; pause ;;
last) write_header " List of last logged in users "; last ; pause ;;
esac
}
# Purpose - Display used and free memory info
function mem_info(){
write_header " Free and used memory "
free -m
echo "*********************************"
echo "*** Virtual memory statistics ***"
echo "*********************************"
vmstat
echo "***********************************"
echo "*** Top 5 memory eating process ***"
echo "***********************************"
ps auxf | sort -nr -k 4 | head -5
pause
}
# Purpose - Get input via the keyboard and make a decision using case..esac
function read_input(){
local c
read -p "Enter your choice [ 1 - 7 ] " c
case $c in
1) os_info ;;
2) host_info ;;
3) net_info ;;
4) user_info "who" ;;
5) user_info "last" ;;
6) mem_info ;;
7) echo "Bye!"; exit 0 ;;
*)
echo "Please select between 1 to 7 choice only."
pause
esac
}
# ignore CTRL+C, CTRL+Z and quit singles using the trap
trap '' SIGINT SIGQUIT SIGTSTP
First you need Key-Based SSH login to your remote servers (https://help.ubuntu.com/community/SSH/OpenSSH/Keys#Key-Based_SSH_Logins)
After that you use a loop like this:
function read_input(){
read -p "Enter your choice [ 1 - 7 ] " c
for server in $(cat your_server_file);
do
case $c in
1) os_info $server;;
2) host_info $server;;
3) net_info $server;;
4) user_info "who" $server;;
5) user_info "last" $server;;
6) mem_info $server;;
7) echo "Bye!"; exit 0 ;;
*)
echo "Please select between 1 to 7 choice only."
pause
esac
done
}
And in example your mem_info function
function mem_info(){
server=$1
write_header " Free and used memory "
ssh ssh_remote_user#server free -m
echo "*********************************"
echo "*** Virtual memory statistics ***"
echo "*********************************"
ssh ssh_remote_user#server vmstat
echo "***********************************"
echo "*** Top 5 memory eating process ***"
echo "***********************************"
ssh ssh_remote_user#server ps auxf | sort -nr -k 4 | head -5
pause
}
UPDATE
If you use sshpass you have to change a little bit.
Your your_server_file should look like this:
user1#password1|user1#server1
user2#password2|user2#server2
...
Your main function
function read_input(){
read -p "Enter your choice [ 1 - 7 ] " c
for line in $(cat your_server_file);
do
user_pass=$(echo $line | sed -e 's/\(.*\)|\(.*\)/\1/')
server=$(echo $line | sed -e 's/\(.*\)|\(.*\)/\2/')
case $c in
1) os_info $server $user;;
2) host_info $server $user;;
3) net_info $server $user;;
4) user_info "who" $server $user;;
5) user_info "last" $server $user;;
6) mem_info $server $user;;
7) echo "Bye!"; exit 0 ;;
*)
echo "Please select between 1 to 7 choice only."
pause
esac
done
}
And the mem_info function
function mem_info(){
server=$1
user_pass=$2
write_header " Free and used memory "
sshpass -p $user_pass ssh $server free -m
echo "*********************************"
echo "*** Virtual memory statistics ***"
echo "*********************************"
sshpass -p $user_pass ssh $server vmstat
echo "***********************************"
echo "*** Top 5 memory eating process ***"
echo "***********************************"
sshpass -p $user_pass ssh $server ps auxf | sort -nr -k 4 | head -5
pause
}
I did not quote anything. Please take care about special characters
I'm not used to writing code in bash but I'm self teaching myself. I'm trying to create a script that will query info from the process list. I've done that but I want to take it further and make it so:
The script runs with one set of commands if A OS is present.
The script runs with a different set of commands if B OS is present.
Here's what I have so far. It works on my Centos distro but won't work on my Ubuntu. Any help is greatly appreciated.
#!/bin/bash
pid=$(ps -eo pmem,pid | sort -nr -k 1 | cut -d " " -f 2 | head -1)
howmany=$(lsof -l -n -p $pid | wc -l)
nameofprocess=$(ps -eo pmem,fname | sort -nr -k 1 | cut -d " " -f 2 | head -1)
percent=$(ps -eo pmem,pid,fname | sort -k 1 -nr | head -1 | cut -d " " -f 1)
lsof -l -n -p $pid > ~/`date "+%Y-%m-%d-%H%M"`.process.log 2>&1
echo " "
echo "$nameofprocess has $howmany files open, and is using $percent"%" of memory."
echo "-----------------------------------"
echo "A log has been created in your home directory"
echo "-----------------------------------"
echo " "
echo ""$USER", do you want to terminate? (y/n)"
read yn
case $yn in
[yY] | [yY][Ee][Ss] )
kill -15 $pid
;;
[nN] | [n|N][O|o] )
echo "Not killing. Powering down."
echo "......."
sleep 2
;;
*) echo "Does not compute"
;;
esac
Here's my version of your script. It works with Ubuntu and Debian. It's probably safer than yours in some regards (I clearly had a bug in yours when a process takes more than 10% of memory, due to your awkward cut). Moreover, your ps are not "atomic", so things can change between different calls of ps.
#!/bin/bash
read percent pid nameofprocess < <(ps -eo pmem,pid,fname --sort=-pmem h)
mapfile -t openfiles < <(lsof -l -n -p $pid)
howmany=${#openfiles[#]}
printf '%s\n' "${openfiles[#]}" > ~/$(date "+%Y-%m-%d-%H%M.process.log")
cat <<EOF
$nameofprocess has $howmany files open, and is using $percent% of memory.
-----------------------------------
A log has been created in your home directory
-----------------------------------
EOF
read -p "$USER, do you want to terminate? (y/n) "
case $REPLY in
[yY] | [yY][Ee][Ss] )
kill -15 $pid
;;
[nN] | [n|N][O|o] )
echo "Not killing. Powering down."
echo "......."
sleep 2
;;
*) echo "Does not compute"
;;
esac
First, check that your version of ps has the --sort flag and the h option:
--sort=-pmem tells ps to sort wrt decreasing pmem
h tells ps to not show any header
All this is given to the read bash builtin, which reads space-separated fields, here the fields pmem, pid, fname and puts these values in the corresponding variables percent, pid and nameofprocess.
The mapfile command reads standard input (here the output of the lsof command) and puts each line in an array field. The size of this array is computed by the line howmany=${#openfiles[#]}. The output of lsof, as stored in the array openfiles is output to the corresponing file.
Then, instead of the many echos, we use a cat <<EOF, and then the read is use with the -p (prompt) option.
I don't know if this really answers your question, but at least, you have a well-written bash script, with less multiple useless command calls (until your case statement, you called 16 processes, I only called 4). Moreover, after the first ps call, things can change in your script (even though it's very unlikely to happen), not in mine.
You might also like the following which doesn't put the output of lsof in an array, but uses an extra wc command:
#!/bin/bash
read percent pid nameofprocess < <(ps -eo pmem,pid,fname --sort=-pmem h)
logfilename="~/$(date "+%Y-%m-%d-%H%M.process.log")
lsof -l -n -p $pid > "$logfilename"
howmany=$(wc -l < "$logfilename")
cat <<EOF
$nameofprocess has $howmany files open, and is using $percent% of memory.
-----------------------------------
A log has been created in your home directory ($logfilename)
-----------------------------------
EOF
read -p "$USER, do you want to terminate? (y/n) "
case $REPLY in
[yY] | [yY][Ee][Ss] )
kill -15 $pid
;;
[nN] | [n|N][O|o] )
echo "Not killing. Powering down."
echo "......."
sleep 2
;;
*) echo "Does not compute"
;;
esac
You could achieve this for example by (update)
#!/bin/bash
# place distribution independent code here
# dist=$(lsb_release -is)
if [[ -f /etc/redheat-release ]];
then # this is a RedHead based distribution like centos, fedora, ...
dist="redhead"
elif [[ -f /etc/issue.net ]];
then
# dist=$(cat /etc/issue.net | cut -d' ' -f1) # debian, ubuntu, ...
dist="ubuntu"
else
dist="unknown"
fi
if [[ $dist == "ubuntu" ]];
then
# use your ubuntu command set
elif [[ $dist == "redhead" ]];
then
# use your centos command set
else
# do some magic here
fi
# place distribution independent code here