Dynamic Patch Counter for Shell Script - shell

I am developing a script on a Solaris 10 SPARC machine to calculate how many patches got installed successfully during a patch delivery. I would like to display to the user:
(X) of 33 patches were successfully installed
I would like my script to output dynamically replacing the "X" so the user knows there is activity occurring; sort of like a counter. I am able to show counts, but only on a new line. How can I make the brackets update dynamically as the script performs its checks? Don't worry about the "pass/fail" ... I am mainly concerned with making my output update in the bracket.
for x in `cat ${PATCHLIST}`
do
if ( showrev -p $x | grep $x > /dev/null 2>&1 ); then
touch /tmp/patchcheck/* | echo "pass" >> /tmp/patchcheck/$x
wc /tmp/patchcheck/* | tail -1 | awk '{print $1}'
else
touch /tmp/patchcheck/* | echo "fail" >> /tmp/patchcheck/$x
wc /tmp/patchcheck/* | tail -1 | awk '{print $1}'
fi
done

The usual way to do that is to emit a \r carriage return (CR) at some point and to omit the \n newline or line feed (LF) at the end of the line. Since you're using awk, you can try:
awk '{printf "\r%s", $1} END {print ""}'
For most lines, it outputs a carriage return and the data in field 1 (without a newline at the end). At the end of the input, it prints an empty string followed by a newline.
One other possibility is that you should place the awk script outside your for loop:
for x in `cat ${PATCHLIST}`
do
if ( showrev -p $x | grep $x > /dev/null 2>&1 ); then
touch /tmp/patchcheck/* | echo "pass" >> /tmp/patchcheck/$x
wc /tmp/patchcheck/* | tail -1
else
touch /tmp/patchcheck/* | echo "fail" >> /tmp/patchcheck/$x
wc /tmp/patchcheck/* | tail -1
fi
done | awk '{ printf "\r%s", $1} END { print "" }'
I'm not sure but I think you can apply similar streamlining to the rest of the repetitious code in the script:
for x in `cat ${PATCHLIST}`
do
if showrev -p $x | grep -s $x
then echo "pass"
else echo "fail"
fi >> /tmp/patchcheck/$x
wc /tmp/patchcheck/* | tail -1
done | awk '{ printf "\r%s", $1} END { print "" }'
This eliminates the touch (which doesn't seem to do much), and especially not when the empty output of touch is piped to echo which ignores its standard input. It eliminates the sub-shell in the if line; it uses the -s option of grep to keep it quiet.
I'm still a bit dubious about the wc line. I think you're looking to count the number of files, in effect, since each file should contain one line (pass or fail), unless you listed some patch twice in the file identified by ${PATCHLIST}. In which case, I'd probably use:
for x in `cat ${PATCHLIST}`
do
if showrev -p $x | grep -s $x
then echo "pass"
else echo "fail"
fi >> /tmp/patchcheck/$x
ls /tmp/patchcheck | wc -l
done | awk '{ printf "\r%s", $1} END { print "" }'
This lists the files in /tmp/patchcheck and counts the number of lines output. It means you could simply print $0 in the awk script since $0 and $1 are the same. To the extent efficiency matters (not a lot), this is more efficient because ls only scans a directory, rather than having wc open each file. But it is more particularly a more accurate description of what you are trying to do. If you later want to count the passes, you can use:
for x in `cat ${PATCHLIST}`
do
if showrev -p $x | grep -s $x
then echo "pass"
else echo "fail"
fi >> /tmp/patchcheck/$x
grep '^pass$' /tmp/patchcheck/* | wc -l
done | awk '{ printf "\r%s", $1} END { print "" }'
Of course, this goes back to reading each file, but you're getting more refined information out of it now (and that's the penalty for the more refined information).

Here is how I got my patch installation script working the way I wanted:
while read pkgline
do
patchadd -d ${pkgline} >> /var/log/patch_install.log 2>&1
# Create audit file for progress indicator
for x in ${pkgline}
do
if ( showrev -p ${x} | grep -i ${x} > /dev/null 2>&1 ); then
echo "${x}" >> /tmp/pass
else
echo "${x}" >> /tmp/fail
fi
done
# Progress indicator
for y in `wc -l /tmp/pass | awk '{print $1}'`
do
printf "\r${y} out of `wc -l /patchdir/master | awk '{print $1}'` packages installed for `hostname`. Last patch installed: (${pkgline})"
done
done < /patchdir/master

Related

System Variable set in bash not sticking after i go to an IF statement

apacherelease=$(curl -s "https://httpd.apache.org" | grep Released | awk '{print $4}' | perl -p -e 's/2.4.54/2.4.54-1/g') &&
apacheinstallversion=$(dnf list installed | grep httpd.x86_64|awk '{print $2}') &&
echo $apacherelease
echo $apacheinstallversion
if test "$apacheinstallversion" = "$apacherelease"; then
: variables are the same
else
: variables are different
fi
`
If I run the commands to set variable directly from the command line instead of a script the variables stick however in the script they disappear the moment I move to the if statement.
Any input would extremely help!
Corrected version:
apacherelease=$(curl -s "https://httpd.apache.org" | grep Released | awk '{print $4}' | perl -p -e 's/2.4.54/2.4.54-1/g') &&
apacheinstallversion=$(dnf list installed | grep httpd.x86_64 | awk '{print $2}')
echo "$apacherelease"
echo "$apacheinstallversion"
if [[ $apacheinstallversion == $apacherelease ]]; then
echo "variables are the same"
else
echo "variables are different" >&2
fi
use the full featured bash test [[
use == instead of =

Would it be possible to print the file used to redirect STDERR?

Would it be possible to print the filename used to redirect STDERR, given the sample command below:
command.sh 2>file.err
Code in command.sh:
#!/bin/sh
ls -l non_existing_file.txt
echo "STDERR file is: $stderrFilename" # variable should print file.err
It's a little risky, but you could try parsing AIX's procfiles output. It involves capturing the major and minor numbers of the stderr device, along with the inode number, then looking for the corresponding device, its mountpoint, and then using find to look for the file with the given inode number:
#!/bin/sh
dev=$(procfiles $$ | awk '$1 == "2:" { print substr($4, 5) }')
inode=$(procfiles $$ | awk '$1 == "2:" { print substr($5, 5) }')
major=${dev%%,*}
minor=${dev##*,}
if [ "$major}" -eq 0 ]
then
echo I give up, the major number is zero
exit 1
fi
for file in /dev/*
do
[ -b "$file" ] || continue
if istat "$file" | grep -q "^Major Device ${major}.*Minor Device ${minor}$"
then
break
fi
done
fs=$(mount | awk '$1 == "'"${file}"'" { print $2 }')
stderrFilename=$(find "$fs" -inum "$inode")
I made a solution using history. Not sure if there is an easier way to do this ( or a proper one).
#!/bin/sh
stderrfname=`history | tail -1 | awk '{ print $3 }' | sed "s/.*>//"`
echo "STDERR file is: $stderrfname"

How to pass a variable string to a file txt at the biginig of test?

I have a problem
I Have a program general like this gene.sh
that for all file (es file: geneX.csv) make a directory with the name of gene (example: Genex/geneX.csv) next this program compile an other program inside gene.sh but this progrm need a varieble and I dont know how do it.
this is the program gene.sh
#!/bin/bash
# Create a dictory for each file *.xls and *.csv
for fname in *.xlsx *csv
do
dname=${fname%.*}
[[ -d $dname ]] || mkdir "$dname"
mv "$fname" "$dname"
done
# For each gene go inside the directory and compile the programs getChromosomicPositions.sh to have the positions, and getHapolotipeStings.sh to have the variants
for geni in */; do
cd $geni
z=$(tail -n 1 *.csv | tr ';' "\n" | wc -l)
cd ..
cp getChromosomicPositions.sh $geni --->
cp getHaplotypeStrings.sh $geni
cd $geni
export z
./getChromosomicPositions.sh *.csv
export z
./getHaplotypeStrings.sh *.csv
cd ..
done
This is the program getChromosomichPositions.sh:
rm chrPosRs.txt
grep '^Haplotype\ ID' $1 | cut -d ";" -f 4-61 | tr ";" "\n" | awk '{print "select chrom,chromStart,chromEnd,name from snp147 where name=\""$1"\";"}' > listOfQuery.txt
while read l; do
echo $l > query.txt
mysql -h genome-mysql.cse.ucsc.edu -u genome -A -D hg38 --skip-column-names < query.txt > queryResult.txt
if [[ "$(cat queryResult.txt)" == "" ]];
then
cat query.txt |
while read line; do
echo $line | awk '$6 ~/rs/ {print $6}' > temp.txt;
if [[ "$(cat temp.txt)" != "" ]];
then cat temp.txt | awk -F'name="' '{print $2}' | sed -e 's/";//g' > temp.txt;
./getHGSVposHG19.sh temp.txt ---> Hear the problem--->
else
echo $line | awk '{num=sub(/.*:g\./,"");num+=sub(/\".*/,"");if(num==2){print};num=""}' > temp2.txt
fi
done
cat query.txt >> varianti.txt
echo "Missing Data" >> chrPosRs.txt
else
cat queryResult.txt >> chrPosRs.txt
fi
done < listOfQuery.txt
rm query*
hear the problem:
I need to enter in the file temp.txt and put automatically at the beginning of the file the variable $geni of the program gene.sh
How can I do that?
Why not pass "$geni" as say the first argument when invoking your script, and treating the rest of the arguments as your expected .csv files.
./getChromosomicPositions.sh "$geni" *.csv
Alternatively, you can set it as environment variable for the script, so that it can be used there (or just export it).
geni="$geni" ./getChromosomicPositions.sh *.csv
In any case, once you have it available in the second script, you can do
if passed as the first argument:
echo "${1}:$(cat temp.txt | awk -F'name="' '{print $2}' | sed -e 's/";//g')
or if passed as environment variable:
echo "${geni}:$(cat temp.txt | awk -F'name="' '{print $2}' | sed -e 's/";//g')

Tail -Fn0 and variable

This follows on from Faulty tail syntax or grep command? but I'm reading a live log entries for given conditions and when they're met continuing the execution of the rest of the script. I'm using this:
tail -Fn0 /var/log/messages | grep -q "CPU utilization" | grep -q "exceeded threshold"
FPC=$(echo $line | awk 'END { print substr($8,1,1) }')
PIC=$(echo $line | awk 'END { print substr($11,1,1) }')
echo FPC $FPC
echo PIC $PIC
echo "Running information gathering"...and rest of script.
Which works perfectly for the conditions detection and further execution, but I don't have the log entry to test for the FPC and PIC variables. I've tried wrapping the tail statement thus:
line=$(tail -Fn0 /var/log/messages | grep -q "CPU utilization" | grep -q "exceeded threshold")
but grep -q exits silently and the $line variable is blank. I've tried:
line=$(tail -Fn0 /var/log/messages | grep -m1 "CPU utilization" | grep -m1 "exceeded threshold")
which doesn't work until I attempt to CONTROL-C out of the script. Then it works fine and continues perfectly. Can someone help please?
I need the variables FPC and PIC later in the script.
Assuming that you don't need these variables later on, you could do something like this:
tail -Fn0 /var/log/messages | \
awk '/CPU utilization/ && /exceeded threshold/ {
print "FPC", substr($8,1,1); print "PIC", substr($11,1,1); exit }'
When the line matches both patterns, print the two parts of it that you are interested in and exit.
If you do need the variables, you could do something like this instead:
line=$(tail -Fn0 /var/log/messages | awk '/CPU utilization/&&/exceeded threshold/{print;exit}')
FPC=$(echo "$line" | awk '{ print substr($8,1,1) }')
PIC=$(echo "$line" | awk '{ print substr($11,1,1) }')

Variable loss in redirected bash while loop

I have the following code
for ip in $(ifconfig | awk -F ":" '/inet addr/{split($2,a," ");print a[1]}')
do
bytesin=0; bytesout=0;
while read line
do
if [[ $(echo ${line} | awk '{print $1}') == ${ip} ]]
then
increment=$(echo ${line} | awk '{print $4}')
bytesout=$((${bytesout} + ${increment}))
else
increment=$(echo ${line} | awk '{print $4}')
bytesin=$((${bytesin} + ${increment}))
fi
done < <(pmacct -s | grep ${ip})
echo "${ip} ${bytesin} ${bytesout}" >> /tmp/bwacct.txt
done
Which I would like to print the incremented values to bwacct.txt, but instead the file is full of zeroes:
91.227.223.66 0 0
91.227.221.126 0 0
127.0.0.1 0 0
My understanding of Bash is that a redirected for loop should preserve variables. What am I doing wrong?
First of all, simplify your script! Usually there are many better ways in bash. Also most of the time you can rely on pure bash solutions instead of running awk or other tools.
Then add some debbuging!
Here is a bit refactored script with debugging
#!/bin/bash
for ip in "$(ifconfig | grep -oP 'inet addr:\K[0-9.]+')"
do
bytesin=0
bytesout=0
while read -r line
do
read -r subIp _ _ increment _ <<< "$line"
if [[ $subIp == "$ip" ]]
then
((bytesout+=increment))
else
((bytesin+=increment))
fi
# some debugging
echo "line: $line"
echo "subIp: $subIp"
echo "bytesin: $bytesin"
echo "bytesout: $bytesout"
done <<< "$(pmacct -s | grep "$ip")"
echo "$ip $bytesin $bytesout" >> /tmp/bwacct.txt
done
Much clearer now, huh? :)

Resources