Assign output of command to environment variable different from original output (bash) - linux

I have encountered a problem with a script I have originally designed.
I am trying to get the number of lines a command displays and if the number is bigger than a value, something should happen.
My problem is that originally this worked fine, now it doesn't.
In my script I am using the following command
NO_LINES=$(ps -ef | grep "sh monitor.sh" | wc -l)
echo $NO_LINES
echo $NO_LINES prints 0 even though it should print 1, the line for the grep command.
If I execute the command separately (not assigning the result to an environment variable) like this
ps -ef | grep "sh monitor.sh" | wc -l
This will print out 1 which is the correct result.
Why is it that by assigning the result to the variable, the value is lower with 1 than the original result?
The bash version of the machine is 4.3.46(1)-release.
Thanks

Related

How to overwrite previous output in bash

I have a bash script, that outputs top most CPU intensive processes every second to the terminal.
tmp=$(ps -e -eo pid,cmd,%mem,%cpu,user --sort=-%cpu | head -n 11)
printf "\n%s\n" "$tmp[pid]"
I know that I can move my cursor to the predeclared position, but that fails every time terminal is not cleared.
I could also just go to the beginning of the line and write over it, but that again makes a problem when current output is shorter that the previous and when the number of lines is not the same as it was at the previous output.
Is there a way to completely erase the previous output and write from there?
Yes, you can clear a part of the screen before each iteration (see https://unix.stackexchange.com/questions/297502/clear-half-of-the-screen-from-the-command-line), but the function watch does it for you. Try:
watch -n 1 "ps -e -eo pid,cmd,%mem,%cpu,user --sort=-%cpu | head -n 11"

Assign a output of jps -vl command to a variable in shell script

I need to assign the output of a command to a variable. The command I tried is:
#!/bin/bash
JAVA_PROCESSES=`jps -vl | grep -v 'sun.tools.jps.Jps' | grep -v 'hudson.remoting.jnlp.Main' | grep -v grep`
NUMBER_OF_JAVA_PROCESSES=`echo $JAVA_PROCESSES | wc -l`
echo $NUMBER_OF_JAVA_PROCESSES
echo $JAVA_PROCESSES
..
When I tried as in above, all java processes grepped are assigned to JAVA_PROCESSES variable in one line. Processes are not separated by new line. Therefore $NUMBER_OF_JAVA_PROCESSES always give 1 for me.
Also $NUMBER_OF_JAVA_PROCESSES show 1 even no processes are assigned to JAVA_PROCESSES due to the empty line in $JAVA_PROCESSES.
Please suggest a way to assign grepped processes separated by new line.
If the main thing you want is to know whether or not you got any at all, you could just test if the variable is empty:
java_procs=$(jps -vl | grep -v 'sun.tools.jps.Jps' | grep -v 'hudson.remoting.jnlp.Main' | grep -v grep)
if [ -z "$java_procs" ]; then
echo "No processes"
fi
Also, we can simplify the grep by using extended regex and just needing a single processes:
java_procs=$(jps -vl | grep -Ev 'sun.tools.jps.Jps|hudson.remoting.jnlp.Main|grep')
Assuming none of the lines output by jps can contain linebreaks themselves, we could get the count after that if we need it:
num_procs=$(printf '%s\n' "$java_procs" | wc -l)
The main problem you were running into is that you weren't quoting your variable, so echo $JAVA_PROCESSES was being expanded and then subject to word splitting, so your newlines were being "eaten" by the shell. You'd always have only one line which would be a space separated list of all the words in your JAVA_PROCESSES variable. To protect from word splitting you can quote the variable, as I did in my code above.
echo will also always add a line break at the end, which is good sometimes, and not so good sometimes, but you should be aware of it happening (that's why you would always get a count of 1 even when there were no processes).

Difference in output when executing system(...) in program and actual command

I have a program written in C that creates an output file with lines of characters. My intention is to count the number of unique lines of characters in this output file (excluding "ABC").
I can do it manually via the Linux command line, using
cat output/output.txt | grep -v "ABC" | sort | uniq -c > uniq_stats/stats.txt
I also put this command into my program so I don't have to do it manually.
memset(command, 0, 500);
sprintf(command, "cat %s | grep -v \"ABC\" | sort | uniq -c > uniq_stats/%s", out_filename, filename);
system(command);
out_filename is output/output.txt and filename is stats.txt
I expect a particular line to be seen 1351 times. The method of using the command line gave this correct value. However, the system(command) method gave only 1349 times. Also, there was another line that was incomplete using the system(command) method, i.e. only a portion of the string was printed out.
Why is it that I got different output from the 2 methods? I have only seen this problem once, as I have tried 4 or 5 other files and both methods gave me the correct results.

How to pipe all the output of "ps" into a shell script for further processing?

When I run this command:
ps aux|awk {'print $1,$2,$3,$11'}
I get a listing of the user, PID, CPU% and the actual command.
I want to pipe all those listings into a shell script to calculate the CPU% and if greater than, say 5, then to kill the process via the PID.
I tried piping it to a simple shell script, i.e.
ps aux|awk {'print $1,$2,$3,$11'} | ./myscript
where the content of my script is:
#!/bin/bash
# testing using positional parameters
echo "$1 $2 $3 $4"
But I get a blank output. Any idea how to do this?
Many thanks!
If you use awk, you don't need an additional bash script. Also, it is a good idea to reduce the output of the ps command so you don't have to deal with extra information:
ps acxho user,pid,%cpu,cmd | awk '$3 > 5 {system("echo kill " $2)}'
Explanation
The extra ps flags I use:
c: command only, no extra arguments
h: no header, good for scripting
o: output format. In this case, only output the user, PID, %CPU, and command
The awk command compare the %CPU, which is the third column, with a threshold (5). If it is over the threshold, then issue the system command to kill that process.
Note the echo in the command. Once you are certain the scripts works the way you like, then remove the word echo from the command to execute it for real.
Your script needs to read its input
#!/bin/bash
while read a b c d; do
echo $a $b
done
I think you can get it using xargs command to pass the AWK output to your script as arguments:
ps aux|awk {'print $1,$2,$3,$11'} | xargs ./myscript
Some extra info about xargs: http://en.wikipedia.org/wiki/Xargs
When piping input from one process to another in Linux (or POSIX-compliant systems) the output is not given as arguments to the receiving process. Instead, the standard output of the first process is piped into the standard input of the other process.
Because of this, your script cannot work. $1...$n accesses variables that have been passed as arguments to it. As there are none it won't display anything. Instead, you have to read the standard input into variables with the read command (as pointed out by William).
The pipe '|' redirects the standard output of the left to the standard input of the right. In this case, the output of the ps goes to the input of awk, then the output of awk goes to the stdin of the script.
Therefore your scripts needs to read its STDIN.
#!/bin/bash
read var1 var2 var3 ...
Then you can do whatever you want with those variables.
More info, type in bash: help read
If I well understood your problem, you want to kill every process that exceeds X% of the CPU (using ps aux).
Here is the solution using AWK:
ps aux | grep -v "%CPU" | awk '{if ($3 > XXX) { print "Killing process with PID "$2", called "$4", consuming "$3"% and launched by "$1; system( "kill -9 " $2 );}}' -
Where XXX is your threshold (% of CPU).
It also prints related info to the killed process, if it is not desired just remove the print statement.
You can add some filters like: do not remove root's process...
Try putting myscript in front like this:
./myscript `ps aux|awk {'print $1,$2,$3,$11'}`

Bash command substitution with a variable

I'm new to bash scripting and I've been learning as I go with a small project I'm taking on. However, I've run into a problem that I cannot seem to get past.
I have a variable that I need to include in a command. When ran directly in the shell (with the variable manually typed), the command returns the expected result. However, I can't get it to work when using a variable.
So, if I manually run this, it correctly returns 0 or 1, depending if it is running or not.
ps -ef | grep -v grep | grep -c ProcessName
However, when I try to embed that into this while clause, it always evaluates to 0 because it's not searching for the correct text.
while [ `ps -ef | grep -v grep | grep -c {$1}` -ne 0 ]
do
sleep 5
done
Is there a way I can accomplish this? I've tried a myriad of different things to no avail. I also tried using the $() syntax for command substitution, but I had no luck with that either.
Thanks!
I think that instead of {$1} you mean "$1". Also, you can just do pgrep -c "$1" instead of the two pipes.
In addition, there's also no need to compare the output of grep -c with 0, since you can just see if the command failed or not. So, a much simplified version might be:
while pgrep "$1" > /dev/null
do
sleep 4
done
You should really use -C with ps rather than the messy pipes if you're using the full process name. If you're interested in substring matching, then your way is the only thing I can think of.

Resources