shell script to exit out of the script if diskspace is more than 75 - awk

I want a script to exit out of the script if disk space is beyond threshold(ex:75%). Trying below things, But no luck.
df -kh | awk '{if( 0+$5 >= 75 ) exit;}'
Trying above command, its not working. Can anyone help me on this.

This is because your df output is NOT coming in a single line or so, to make this you need to add -P option with it try following once.
df -hP | awk '{if( 0+$5 >= 75 ){print "exiting now..";exit 1}}'
OR
df -hP | awk '$5+0>=75{print "exiting now..";exit 1}'
OR with mount name who is the culprit for breaching threshold.
df -hP | awk '$5+0>=75{print "Mount " $1 " has crossed threshold so exiting now..";exit 1}'
In case you don't have -P option in your box then try following.
df -k | awk '/^ +/ && $4+0>=75{print "Mount " prev" has crossed threshold so exiting now..";exit 1} !/^ +/{prev=$0}'
I am using print statement to make sure exit is working. also -P option was tested on BASH systems.
Since OP told he needs to exit from complete script itself so requesting OP to add following code outside of for loop of his code.(I haven't tested it though but this should work)
if [[ $? -eq 1 ]]
then
echo "Exiting the complete script now..."
exit
else
echo "Looks good so going further in script now.."
fi

If you are using this in a script to exit the script (as opposed to exiting a long awk script) then you need to call exit from the outer script:
if df -kh | awk '{if ($5+0 > 75) exit 1 }'; then echo OK; else echo NOT; fi
Don't forget that df returns one line per mount point, you can do:
if dk -kh /home ....
to check for a particular mount point.

Related

Is there any if statment in awk to compare a word greped from a file?

I am new to any programming and shell scripting.
I am trying to make a if condition in shell script.
I am used of some computed codes for density functional theory (say Quantum espresso).
I want to make the program automatic via a shell script.
My code produce case.data which contains at the end stop (at $2 or we can say at second column).
For example below script should print stop
cat case.data | tail -n 1 | awk '{print $2}'
so if I get stop from above script then then if statement should not produce anything and the rest file should be executed. If I do not get stop from then above script then executable commands in my file should not be executed and a text file containing exit should be executed so that it terminates my job.
What I tried is:
#!bin/bash
# Here I have my other commands that will give us case.data and below is my if statement.
STOP=$(cat $case.dayfile | tail -n 1 | awk '{print $2}')
if [$STOP =="stop"]
then
echo "nil"
else
echo "exit" > exit
chmod u+x exit
./exit
fi
# here after I have other executable that will be executed depending on above if statement
Your whole script should be changed to just this assuming you really do want to print "nil" in the success case for some reason:
awk '{val=$2} END{if (val=="stop") print "nil"; else exit 1}' "${case}.data" || exit
and this otherwise:
awk '{val=$2} END{exit (val=="stop" ? 0 : 1)}' "${case}.data" || exit
I used a ternary in that last script instead of just exit (val!="stop") or similar for clarity given the opposite values of true in a condition and success in an exit status.
Like this?:
if awk 'END{if($2!="stop")exit 1}' case.data
then
echo execute commands here
fi

Exit when the result is ready and do not wait for the rest job?

I want to exit immediately when the result has been provided and do not wait for the rest of the jobs. I provided three examples by different approaches, i.e. awk, head and read. I want to exit after the '1' is shown in the following example without waiting for sleep. But none of the do not work. Is there any guy to help me?
(echo 1; sleep 10; seq 10) | head -n 1
(echo 1; sleep 10; seq 10) | awk -e 'NR==1{print $1;exit}'
(echo 1; sleep 10; seq 10) | ./test.sh
where the test.sh is the following:
while read -r -d $'\n' x
do
echo "$x"
exit
done
Refactor Using Bash Process Substitution
I want to exit after the '1' is shown in the following example without waiting for sleep.
By default, Bash shell pipelines wait for each pipeline segment to complete before processing the next segment of the pipeline. This is usually the expected behavior, because otherwise your commands wouldn't be able to act on the completed output of from each pipeline element. For example, how could sort do its job in a pipeline if it doesn't have all the data available at once?
In this specific case, you can do what you want, but you have to refactor your code so that awk is reading from process substitution rather than a pipe. For example:
$ time awk -e 'NR==1 {print $1; exit}' < <(echo 1; sleep 10; seq 10)
1
real 0m0.004s
user 0m0.001s
sys 0m0.002s
From the timings, you can see that the process exits when awk does. This may not be how you want to do it, but it certainly does what you want to accomplish with a minimum of fuss. Your mileage with non-Bash shells may vary.
Asynchronous Pipelines
Asynchronous pipelines are not really a generic solution, but using one works sufficiently to accomplish your goals for the given use case. The following returns immediately:
$ { echo 1 & sleep 10 & seq 10 & } | awk -e 'NR==1 {print $1; exit}'
1
because the commands in the command list are run asynchronously. When you run commands asynchronously in Bash:
The shell does not wait for the command to finish, and the return status is 0 (true).
However, note that this only appears to do what you want. Your other commands (e.g. sleep and seq) are actually still running in the background. You can validate this with:
$ { echo 1 & sleep 10 & seq 10 & } | awk -e 'NR==1 {print $1; exit}'; pgrep sleep
1
14921
As you can see, this allows awk to process the output of echo without waiting for the entire list of commands to complete, but it doesn't really short-circuit the execution of the command list. Process substitution is still likely to be the right solution, but it's always good to know you have alternatives.

Prepend a # to the first line not already having a #

I have a file with options for a command I run. Whenever I run the command I want it to run with the options defined in the first line which is not commented out. I do this using this bash script:
while read run opt c; do
[[ $run == \#* ]] && continue
./submit.py $opt $run -c "$c"
break
done < to_submit.txt
The file to_submit.txt has entries like this:
#167993 options/optionfile.py long description
167995 options/other_optionfile.py other long description
...
After having run the submit script with the options in the last not commented out line, I want to comment out that line after the command ran successfully.
I can find the line number of the options I used adding this to the while loop:
line=$(grep -n "$run" to_submit.txt | grep "$opt" | grep "$c" | cut -f 1 -d ":")
But I'm not sure how to actually prepend a # to that line now. I could probably use head and tail to save the other lines and process that line separately and combine it all back into the file. But this sounds like it's to complicated, there must be an easier sed or awk solution to this.
$ awk '!f && sub(/^[^#]/,"#&"){f=1} 1' file
#167993 options/optionfile.py long description
#167995 options/other_optionfile.py other long description
...
To overwrite the contents of the original file:
awk '!f && sub(/^[^#]/,"#&"){f=1} 1' file > tmp && mv tmp file
just like with any other UNIX command.
Using GNU sed is probably simplest here:
sed '0,/^[^#]/ s//#&/' file
Add option -i if you want to update file in place.
'0,/^[^#]/ matches all lines up to and including the first one that doesn't start with #
s//#&/ then prepends # to that line.
Note that s//.../ (i.e., an empty regex) reuses the last matching regex in the range, which is /^[^#]/ in this case.
Note that the command doesn't work with BSD/OSX sed, unfortunately, because starting a range with 0 so as to allow the range endpoint to match the very first line also is not supported there. It is possible to make the command work with BSD/OSX sed, but it's more cumbersome.
If the input/output file is not very large, you can do it all in Bash:
optsfile=to_submit.txt
has_run_cmd=0
outputlines=()
while IFS= read -r inputline || [[ -n $inputline ]] ; do
read run opt c <<<"$inputline"
if (( has_run_cmd )) || [[ $run == \#* ]] ; then
outputlines+=( "$inputline" )
elif ./submit.py "$opt" "$run" -c "$c" ; then
has_run_cmd=1
outputlines+=( "#$inputline" )
else
exit $?
fi
done < "$optsfile"
(( has_run_cmd )) && printf '%s\n' "${outputlines[#]}" > "$optsfile"
The lines of the file are put in the outputlines array, with a hash prepended to the line that was used in the ./submit.py command. If the command runs successfully, the file is overwritten with the lines in outputlines.
After some searching around I found that
awk -v run="$run" -v opt="$opt" '{if($1 == run && $2 == opt) {print "#" $0} else print}' to_submit.txt > temp
mv -b -f temp to_submit.txt
seems to solve this (without needing to find the line number first, just comparing $ run and $opt). This assumes that the combination of run and opt is enough to identify a line and the comment is not needed (which happens to be true in my case). Not sure how the comment which is spanning multiple fields in awk would also be taken into account.

while loop only iterates once

I'm writing a unix script which does an awk and pipes to a while loop. For some reason, though, the while loop iterates only once. Can someone point out what I am missing?
awk '{ print $1, $2}' file |
while IFS=" " read A B
do
echo $B
if [ "$B" -eq "16" ];
then
grep -A 1 $A $1 | python unreverse.py
else
grep -A 1 $A
fi
done
"file" looks something like
cheese 2
elephant 5
tiger 16
Solution
The solution is to replace:
grep -A 1 $A
With:
grep -A 1 "$A" filename
Where filename is whatever file you intended grep to read from. Just guessing, maybe you intended:
grep -A 1 "$A" "$1"
I added double-quotes to prevent any possible word-splitting.
Explanation
The problem is that, without the filename, the grep command reads from and consumes all of standard input. It does this on the first run through the loop. Consequently, there is not input left for the second run and read A B fails and the loop terminates.
A Simpler Example
We can see the same issue happening with many fewer statements. Here is a while loop that is given two lines of input but only loops once:
$ { echo 1; echo 2; } | while read n; do grep "$n"; echo "n=$n"; done
n=1
Here, simply by adding a filename to the grep statement, we see that the while loop executes twice, as it should:
$ { echo 1; echo 2; } | while read n; do grep "$n" /dev/null; echo "n=$n"; done
n=1
n=2

Problem with awk and grep

I am using the following script to get the running process to print the id, command..
if [ "`uname`" = "SunOS" ]
then
awk_c="nawk"
ps_d="/usr/ucb/"
time_parameter=7
else
awk_c="awk"
ps_d=""
time_parameter=5
fi
main_class=RiskEngine
connection_string=db.regression
AWK_CMD='BEGIN{printf "%-15s %-6s %-8s %s\n","ID","PID","STIME","Cmd"} {printf "%-15s %-6s %-8s %s %s %s\n","MY_APP",$2,$time_parameter, main_class, connection_string, port}'
while getopts ":pnh" opt; do
case $opt in
p) AWK_CMD='{ print $2 }'
do_print_message=1;;
n) AWK_CMD='{printf "%-15s %-6s %-8s %s %s %s\n","MY_APP",$2,$time_parameter,main_class, connection_string, port}' ;;
h) print "usage : `basename ${0}` {-p} {-n} : Returns details of process running "
print " -p : Returns a list of PIDS"
print " -n : Returns process list without preceding header"
exit 1 ;
esac
done
ps auxwww | grep $main_class | grep 10348 | grep -v grep | ${awk_c} -v main_class=$merlin_main_class -v connection_string=$merlin_connection_
string -v port=10348 -v time_parameter=$time_parameter "$AWK_CMD"
# cat /etc/redhat-release
Red Hat Enterprise Linux AS release 4 (Nahant Update 6)
# uname -a
Linux deapp25v 2.6.9-67.0.4.EL #1 Fri Jan 18 04:49:54 EST 2008 x86_64 x86_64 x86_64 GNU/Linux
When I am executing the following from the script independently or inside script
# ps auxwww | grep $main_class | grep 10348 | grep -v grep | ${awk_c} -v main_class=$merlin_main_class -v connection_string=$merlin_connection_string -v port=10348 -v time_parameter=$time_parameter "$AWK_CMD"
I get two rows on Linux:
ID PID STIME Cmd
MY_APP 6217 2355352 RiskEngine 10348
MY_APP 21874 5316 RiskEngine 10348
I just have one jvm (Java command) running in the background but still I see 2 rows.
I know one of them (Duplicate with pid 21874) comes from awk command that I am executing. It includes again the main class and the port so two rows. Can you please help me to avoid the one that is duplicate row?
Can you please help me?
AWK can do all that grepping for you.
Here is a simple example of how an AWK command can be selective:
ps auxww | awk -v select="$mainclass" '$0 ~ select && /10348/ && ! (/grep/ || /awk/) && {print}'
ps can be made to selectively output fields which will help a little to reduce false positives. However pgrep may be more useful to you since all you're really using is the PID from the result.
pgrep -f "$mainclass.*10348"
I've reformatted the code as code, but you need to learn that the return key is your friend. The monstrously long pipelines should be split over multiple lines - I typically use one line per command in the pipeline. You can also write awk scripts on more than one line. This makes your code more readable.
Then you need to explain to us what you are up to.
However, it is likely that you are using 'awk' as a variant on grep and are finding that the value 10348 (possibly intended as a port number on some command line) is also in the output of ps as one of the arguments to awk (as is the 'main_class' value), so you get the extra information. You'll need to revise the awk script to eliminate (ignore) the line that contains 'awk'.
Note that you could still be bamboozled by a command running your main class on port 9999 (any value other than 10348) if it so happens that it is run by a process with PID or PPID equal to 10348. If you're going to do the job thoroughly, then the 'awk' script needs to analyze only the 'command plus options' part of the line.
You're already using the grep -v grep trick in your code, why not just update it to exclude the awk process as well with grep -v ${awk_c}?
In other words, the last line of your script would be (on one line and with the real command parameters to awk rather than blah blah blah).:
ps auxwww
| grep $main_class
| grep 10348
| grep -v grep
| grep -v ${awk_c}
| ${awk_c} -v blah blah blah
This will ensure the list of processes will not containg any with the word awk in it.
Keep in mind that it's not always a good idea to do it this way (false positives) but, since you're already taking the risk with processes containing grep, you may as well do so with those containing awk as well.
You can add this simple code in front of all your awk args:
'!/awk/ { .... original awk code .... }'
The '!/awk/' will have the effect of telling awk to ignore any line containing the string awk.
You could also remove your 'grep -v' if you extended my awk suggestion into something like:
'!/awk/ && !/grep/ { ... original awk code ... }'.