bash script: commands after the loop are not executed if the loop condition is false - bash

I have a strange behavior in my script bash. when the while condition is true the script behaves correctly but if it's false the commands after the loop aren't executed at all and the script stops. There is no break in my commands after the loop.
I cannot see where is the problem! Any help is welcome :)
Thanks in advance.
while [ expression1 ] || [ expression2 ]
do
echo in the loop
if [ expression3 ] && [ expression4 ] ;
then
commands..
break;
fi
commands..
done
commands..
echo out from the loop
Real code:
start_t=`grep Start_t $job_template | awk -F= '{print $2}'`
current_date=`date +%s`
progress_t=`expr $current_date - $start_t`
exec_t=`grep Exec_t $job_template | awk -F= '{print $2}'`
running_state="r"
req_state $job_id # get the state
xml_state=` grep "job_id=$job_id" $list_job_file | awk '{print $4}'`
while [ $state = $running_state ] || [ $xml_state = "stoped" ]
do
echo in the loop
if [ "$xml_state" = "running" ] && [ $progress_t -gt $exec_t ] ;
then
kill_job $job_id
update_status $job_template "killed"
echo The job is killed
break;
fi
sleep $sleeping_t
$req_state $job_id # to update the state
echo state $state
xml_state=` grep "job_id=$job_id" $list_job_file | awk '{print $4}' `
echo xml_state $xml_state
start_t=`grep Start_t $job_template | awk -F= '{print $2}'`
current_date=`date +%s`
progress_t=`expr $current_date - $start_t`
done
echo out from the loop
commands..

There are many mistakes in this script :
state is not initialised
as test are done with single right bracket, the variables should be double quoted to avoid shell expansion
seems req_state is a function or a command, so there must not be preceded by a $
useless use of grep with awk : grep Start_t $job_template | awk -F= '{print $2}' and awk -F= '/Start_t/{print $2}' $job_template will do the same thing.

Related

shell script : comma in the beginning instead of end

This is a part of my shell script.
for line in `cat $1`
do
startNum=`echo $line | awk -F "," '{print $1}'`
endNum=`echo $line | awk -F "," '{print $2}'`
operator=`echo $line | awk -F "," '{print $3}'`
termPrefix=`echo $line | awk -F "," '{print $4}'`
if [[ "$endNum" == 81* ]] || [[ "$endNum" == 33* ]] || [[ "$endNum" == 55* ]]
then
areaCode="${endNum:0:2}"
series="${endNum:2:4}"
startCLI="${startNum:6:4}"
endCLI="${endNum:6:4}"
else
areaCode="${endNum:0:3}"
series="${endNum:3:3}"
startCLI="${startNum:6:4}"
endCLI="${endNum:6:4}"
fi
echo "Add,${areaCode},${series},${startCLI},${endCLI},${termPrefix},"
#>> ${File}
done
input is csv contains below many rows :
5557017101,5557017101,102,1694
5515585614,5515585614,102,084
Output od shell script :
,dd,55,5701,7101,7101,1694
,dd,55,1558,5614,5614,0848
Not sure why comma is coming in startign of output, instead as per shell script it should come in the end.
please help
Here is a suggested awk command that should replace all of your shell+awk code. This awk also takes care of trailing \r:
awk -v RS=$'\r' 'BEGIN{FS=OFS=","} NF>3{
startNum=$1; endNum=$2; termPrefix=$4;
if (endNum ~ /^(81|33|55)/) {
areaCode=substr(endNum,1,2); series=substr(endNum,3,4)
}
else {
areaCode=substr(endNum,1,3); series=substr(endNum,4,3)
}
startCLI=substr(startNum,7,4); endCLI=substr(endNum,7,4);
print "Add", areaCode, series, startCLI, endCLI, termPrefix
}' file
Add,55,5701,7101,7101,1694
Add,55,1558,8561,5614,084

awk command variable NF not working on NULL input

I run my safe shell script to make sure a binary is running
to check a binary is running I do following command
pidof prog.bin | awk '{print NF}'
is some system it gives me 0 when binary not running
and
in some systems it gives me NULL(nothing)
I can check the NULL using -z option but why awk command acting this way ??
Instead of pidof you can use:
pgrep -qf prog.bin
And check its exit status.
As per man pgrep:
-f Match against full argument lists. The default is to match against process names.
-q Do not write anything to standard output.
You can use this,
if [ `pidof 'NetworkManager'` ]; then
echo "Running"
else
echo "Not Running"
fi
One way to handle this sort of thing (undefined variables) in awk is like this:
echo hi | awk '{print a}'
compared with:
echo hi | awk '{print a || 0}'
0
One Liner for If else
[[ $(pidof 'NetworkManager') ]] && echo "Running" || echo "Not Running"
Try this:
pidof prog.bin | awk '{ if (NF!=0) print NF }'
Here's some tests with awk and NF:
$ # regular line of input
$ echo foo | awk '{print NF}'
1
$ # empty line
$ echo | awk '{print NF}'
0
$ # a word on input with no newline
$ printf "%s" nonewline | awk '{print NF}'
1
$ # no input, not even a newline
$ printf %s | awk '{print NF}'
# no output from awk
I suspect the pidof case is the last: not even a newline. To force a newline:
echo $(pidof prog) | ...
printf "%s\n" "$(pidof prog)" | ...

Parsing in bash - extract content from brackets

The input to my bash script can be of the form [fec1::1]:80 or []:80. The second input implies that there's no IP address given. My bash script is to split the input into IP and port. With the said second input, the script should 'understand' that no IP was given.
The following logic seems to solve my problem, on the bash prompt:
$ ip=[]:78
$ echo $ip
[]:78
$ temp=(`echo $ip | awk -F'[][]' '{print $2}'`)
$ echo $temp
$
When I try to do the same thing from within a script, the result is different:
local_endpoint="$1"
printf 'local_endpoint: %s\n' "$local_endpoint"
IN="$local_endpoint"
local_ip=$(echo "$IN" | awk -F'[][]' '{print $2}')
if [ -z "$local_ip" ] ; then
local_ip=$(echo "$IN" | awk -F':' '{print $1}')
local_port=$(echo "$IN" | awk -F':' '{print $2}')
else
local_port=$(echo "$IN" | awk -F'[][]' '{print $3}' | awk -F':' '{print $2}')
fi
printf 'IP: %s\n' $local_ip
printf 'port: %d\n' $local_port
if [ -z "$local_port" -a -z "$local_ip" ] ; then
printf 'No port and IP was given\n'
elif [ -z "$local_ip" ] ; then
printf 'No IP was given\n'
elif [ -z "$local_port" ] ; then
printf 'No port was given\n'
fi
exit 2
Output:
# ./temp.sh []:829
local_endpoint: []:829
IP: []
port: 829
Any idea on what's happening? Also, why do I see the extra comma (,) at the end of the output?
Your script is missing quoting at many places and there are stray commas too in printf. This script should work:
local_endpoint="$1"
printf 'local_endpoint: %s\n' "$local_endpoint"
IN="$local_endpoint"
if [[ "$IN" == "["* ]] ; then
local_ip=$(echo "$IN" | awk -F'[][]' '{print $2}')
local_port=$(echo "$IN" | awk -F'[][]' '{print $3}' | awk -F':' '{print $2}')
else
local_ip=$(echo "$IN" | awk -F':' '{print $1}')
local_port=$(echo "$IN" | awk -F':' '{print $2}')
fi
printf 'IP: <%s>\n' "$local_ip"
printf 'port: <%d>\n' "$local_port"
if [ -z "$local_port" -a -z "$local_ip" ] ; then
printf 'No port and IP was given\n'
elif [ -z "$local_ip" ] ; then
printf 'No IP was given\n'
elif [ -z "$local_port" ] ; then
printf 'No port was given\n'
fi
exit 2
Process substitution is:
var=$(command ...)
not
var=(command ...)

Bash - Memory usage

I have a problem that I can't solve, so I've come to you.
I need to write a program that will read all processes and a program must sort them by users and for each user it must display how much of a memory is used.
For example:
user1: 120MB
user2: 300MB
user3: 50MB
total: 470MB
I was thinking to do this with ps aux command and then get out pid and user with awk command. Then with pmap I just need to get total memory usage of a process.
it's just a little update, users are automatically selected
#!/bin/bash
function mem_per_user {
# take username as only parameter
local user=$1
# get all pid's of a specific user
# you may elaborate the if statement in awk obey your own rules
pids=`ps aux | awk -v username=$user '{if ($1 == username) {print $2}}'`
local totalmem=0
for pid in $pids
do
mem=`pmap $pid | tail -1 | \
awk '{pos = match($2, /([0-9]*)K/, mem); if (pos > 0) print mem[1]}'`
# when variable properly set
if [ ! -z $mem ]
then
totalmem=$(( totalmem + $mem))
fi
done
echo $totalmem
}
total_mem=0
for username in `ps aux | awk '{ print $1 }' | tail -n +2 | sort | uniq`
do
per_user_memory=0
per_user_memory=$(mem_per_user $username)
if [ "$per_user_memory" -gt 0 ]
then
total_mem=$(( $total_mem + $per_user_memory))
echo "$username: $per_user_memory KB"
fi
done
echo "Total: $total_mem KB"
Try this script, which may solve your problem:
#!/bin/bash
function mem_per_user {
# take username as only parameter
local user=$1
# get all pid's of a specific user
# you may elaborate the if statement in awk obey your own rules
pids=`ps aux | awk -v username=$user '{if ($1 == username) {print $2}}'`
local totalmem=0
for pid in $pids
do
mem=`pmap $pid | tail -1 | \
awk '{pos = match($2, /([0-9]*)K/, mem); if (pos > 0) print mem[1]}'`
# when variable properly set
if [ ! -z $mem ]
then
totalmem=$(( totalmem + $mem))
fi
done
echo $totalmem
}
total_mem=0
for i in `seq 1 $#`
do
per_user_memory=0
eval username=\$$i
per_user_memory=$(mem_per_user $username)
total_mem=$(( $total_mem + $per_user_memory))
echo "$username: $per_user_memory KB"
done
echo "Total: $total_mem KB"
Best regards!
You can access the shell commands in python using the subprocess module. It allows you to spawn subprocesses and connect to the out/in/error. You can execute the ps -aux command and parse the output in python.
check out the docs here
Here is my version. I think that Tim's version is not working correctly, the values in KB are too large. I think the RSS column from pmap -x command should be used to give more accurate value. But do note that you can't always get correct values because processes can share memmory. Read this A way to determine a process's "real" memory usage, i.e. private dirty RSS?
#!/bin/bash
if [ "$(id -u)" != "0" ]; then
echo "WARNING: you have to run as root if you want to see all users"
fi
echo "Printing only users that current memmory usage > 0 Kilobytes "
all=0
for username in `ps aux | awk '{ print $1 }' | tail -n +2 | sort | uniq`
do
pids=`ps aux | grep $username | awk -F" " '{print $2}'`
total_memory=0
for pid in $pids
do
process_mem=`pmap -x $pid | tail -1 | awk -F" " '{print $4}'`
if [ ! -z $process_mem ]
then #don't try to add if string has no length
total_memory=$((total_memory+$process_mem))
fi
done
#print only those that use any memmory
if [ $total_memory -gt 0 ]
then
total_memory=$((total_memory/(1024)))
echo "$username : $total_memory MB"
all=$((all+$total_memory))
fi
done
echo "----------------------------------------"
echo "Total: $all MB"
echo "WARNING: Use at your own risk"

bourne shell single-quote, doublequote & backquote question

/!bin/sh
if [ "`echo $desc $status | awk -F"," '{print $3}' | awk -F" " '{print $1}' | sed '/^$/d'`" != "OK" ]; then
echo "howdy dody"
fi
echo $desc $status | awk -F"," '{print $3}' | awk -F" " '{print $1}' | sed '/^$/d'
First if-condition won't run, im guessing it's because of improper quotation, but i can't figure it out.
Thanks in advance for any help.
You can also use single quotes around the argument to the -F option as you have around other arguments:
if [ "`echo $desc $status | awk -F',' '{print $3}' | awk -F' ' '{print $1}' | sed '/^$/d'`" != "OK" ]; then
It is much easier to write your test if you wrap it in a function:
mytest()
{
echo "$1 $2" \
| awk -F"," -v arg3="$3" '{print arg3}' \
| awk -F" " -v arg1="$1" '{print arg1}' \
| sed '/^$/d'
}
This way, you can verify that it works correctly. Once you gained this confidence
if [ "$(mytest "$desc" "$status")" != "OK" ]; then
echo "howdy doody"
fi
or
if mytest "$desc" "$status" | grep -q -v '^OK$'; then
echo "howdy doody"
fi
If you're using Bash, I'd recommend $(...) instead of back-quotes. What error messages do you get? My guess is that the -F"," option to awk is not being quoted properly. Trying inserting \ to escape the quotation marks.
At first glance, you might want to try escaping some of the double quotes:
if [ "`echo $desc $status | awk -F"," '{print $3}' | awk -F" " '{print $1}' | sed '/^$/d'`" != "OK" ]; then
echo "howdy dody"
fi
to
if [ "`echo $desc $status | awk -F\",\" '{print $3}' | awk -F\" \" '{print $1}' | sed '/^$/d'`" != "OK" ]; then
echo "howdy doody"
fi
Escaping the double quotes is certainly a good idea, but it looks like the $3 and the $1 are intended to be interpreted by awk. They are being interpreted by your shell instead. You probably want to escape the '$'s. (It is possible that you have meaningful values for $1 and $3 in the shell, but not likely.)

Resources