Unable to get the absolute value of command output - linux

So I wanted to make a simple script to keep checking the CPU temperature of my RasPi, which is stored in /sys/class/thermal/thermal_zone0/temp , and hence cat /sys/class/thermal/thermal_zone0/temp would give the temp, but like this :
cat /sys/class/thermal/thermal_zone0/temp
38459
which essentially means 38.459 degree Celsius.
I was unable to format the output to get 38.594 °C
My code:
tempT="$(cat /sys/class/thermal/thermal_zone0/temp)"
tempC=$($tempT / 1000)
echo "$tempC °C"
The error I get:
-bash: 38459: command not found
°C
Thanks

The simplest would be to use awk.
awk '{print $1/1000}' /sys/class/thermal/thermal_zone0/temp
or with some more control with printf
awk '{printf "%.3f\n", $1/1000}' /sys/class/thermal/thermal_zone0/temp
The error you are seeing comes from that you used $( ...), which is a command substitution and tries to run the command inside. So when you do:
$($tempT / 1000)
First $tempT expands to 38459 and then shell tries to run a command named 38459 with two arguments / and 1000. So you see the message 38459: Command not found. Use $((...)) for arithmetic expansion, but shells do not implement floating point arithmetic so you have to use other tools like awk or bc.

I'd use bc if it is available on your system.
$ CELSIUS=$(bc -l <<< $(cat /sys/class/thermal/thermal_zone0/temp)/1000)
$ echo $CELSIUS
25.00000000000000000000

TempC=$($tempT / 1000);
Resolves to:
TempC=$(38459 / 1000);
And bash treats $(...) as a command to be passed into a subshell, so it tries to run the executable 38455, which it can't find, and hence complains.
I would use bc, as #kinezan suggested, though I personally prefer the following convention:
TempC=$(echo "scale=3; $tempT / 1000" | bc)
which outputs 38.459

Related

More convenient way to do arithmetic with program output at the shell?

I usually need to run programs to do some file checking, like say use wc to count the lines of a file and then do some arithmetic with it. Usually the way I do this is just getting the output and then doing the arithmetic by opening a python terminal or whichever software can be used to do so.
If I have to do it many times, then this gets a bit annoying, and I'd like to have some method to take the output directly and do the arithmetic that I want. For instance, one that I like is using perl in the following way, assuming I have to take the output of wc and divide it by 12:
perl -e 'print `wc -l file`/12'
This can be useful but gets annoying after a while. Since this is probably something people need to do all the time, I'd like to know what better faster methods people use to do this fast. I've seen expr might be even better, but I get a syntax error when passing it the output of something bound in ``, like above. So basically the shortest, most efficient way one can do this simple arithmetic in linux terminals from file outputs.
Double parentheses ((...)) perform arithmetic, and with a dollar sign $((...)) you can get the result as a string.
echo $((`wc -l < file` / 12))
echo $(($(wc -l < file) / 12))
You can use variables and they don't need dollar signs. Both var and $var are acceptable:
lines=$(wc -l < file)
echo $((lines / 12))
if ((lines * 42 + 17 > 630)); then
...
fi
So basically I have tested with a code on my bash:
Multiline code:
a=$(echo "hi" | wc -l)
echo $a
b=`expr $a + 2`
echo $b
Which I have changed to one line:
echo `expr $(echo "hi" | wc -l) + 20`
echo "hi" | wc -l is calculating no of lines and is within $() which makes it as one variable and evaluate its value
Then expr takes two arguements here and make sure you to use space before and after the operator and use a backtic(`) to evaluate thi and doing echo finally

Need help in this unix command

I am trying to fetch the latest file in a directory and get the time since it is modified. Using the below command but getting error. Can someone tell me what I am doing wrong here?
And is there any simplified version for this?
NOW=`date +%s`;
FILE=`ls -lpt /tmp/app/test/*.txt | head -n 1 | awk '{print $9}'`;
Time=`stat -c %Y ${FILE}`;
DIFF=`${NOW} - ${Time}`;
echo ${DIFF}
-bash: 1552214130: command not found
This is wrong:
DIFF=`${NOW} - ${Time}`;
Backticks mean to execute what's contained as a command, and then substitute the output into the command. So this will try to use the value of ${NOW} as the name of a command to execute. But it's just a numeric timestamp, not a command.
In order to perform calculations in bash use $(( expression )), not backticks.
DIFF=$((NOW - Time))
BTW, you should not use all-capital names for your variables. By convention those are reserved for environment variables.

How can I replace 'bc' tool in my bash script?

I have the following command in my bash script:
printf '\n"runtime": %s' "$(bc -l <<<"($a - $b)")"
I need to run this script on around 100 servers and I have found that on few of them bc is not installed. I am not admin and cannot install bc on missing servers.
In that case, what alternative can i use to perform the same calculation? Please let me know how the new command should look like
In case you need a solution which works for floating-point arithmetic you can always fall back to Awk.
awk -v a="$a" -v b="$b" 'BEGIN { printf "\n\"runtime\": %s", a-b }' </dev/null
Putting the code in a BEGIN block and redirecting input from /dev/null is a common workaround for when you want to use Awk but don't have a file of lines to loop over, which is what it's really designed to do.
If you are only dealing with integers you can use bash's arithmetic expansion for this:
printf '\n"runtime": %s' $((a - b))
Note that this does assume you have bash available (as you've indicated you do). If you only have a stripped down Bourne shell (/bin/sh) arithmetic expansion is not available to you.

passing bash expression to AWK for floating point arithmetic

I was trying to do floating point arithmetic in bash, but as floats are not supported, was trying to get the solution using AWK. Below is the issue i am facing:
I see this working fine for me:
code
echo - | awk '{printf("%04.4f \n", (-225.2*7+30*6)/17 + (19^2)/9) }'
output
-42.0301
But my motive is to "read an expression" and compute value correct to 4 decimals, so tried below code inputting same expression (-225.2*7+30*6)/17 + (19^2)/9) and its giving incorrect values(i guess variable is passed as string to awk):
code
read inpt
echo - | awk -v input=$inpt '{printf("%04.4f \n", input) }'
output
0.0000
Note: Please ignore the space around second + in this example expression, that i can remove using sed or similar methods(with space i get syntactical error in awk while passing variable from bash).
Any help is highly appreciated. Thanks in advance
PS: the bash version in my case is "bash-4.2". I guess its the version of bash preventing me using from many other options.
You can't evaluate data in a variable in awk out of the box. In this case you need to write an arithmetic evaluator or use a pre-existing one, like https://github.com/radare/radare2-bindings/blob/master/awk/calc.awk . Once you fix that missing parenthesis and quote your expression properly, you can:
$ echo "((-225.2*7+30*6)/17 + (19^2)/9)" | awk -f calc.awk
((-225.2*7+30*6)/17 + (19^2)/9) = -42.0301
I recommend and have upvoted James Brown's answer. But if you need something here and now with no external dependencies, you can simply interpolate the input into the script.
awk "END { printf("%04.4f\n", $input) }" </dev/null
This is functionally equivalent to using eval so if this isn't deployed where you know you can trust the input (e.g. because it comes from a controlled process, not an actual user) you will need to perform some sort of sanitization (and even then probably cope with odd or outright misleading error messages if the input isn't a well-formed Awk expression).
read -p "Input an Awk expression: " input
case $input in
*[!-+/*()^%0-9]*)
echo "$0: invalid input" >&2
exit 1;;
esac
awk ...
Notice also the construct to avoid the basically useless echo. Redirecting input from /dev/null and putting your code in the END (or BEGIN) block is a standard technique for running an arbitrary piece of Awk script without requiring any input.
First, I don't see anything wrong in the command, it should work. Please try again the exact commands posted and provide exact details if the issue persists.
However, if all you need is formating the output, you can do it directly with printf.
$ read input
12.34
$ printf '%4.4f' $input
12.3400
EDIT:
If you need to format the output after performing some calculation, then you can alternatively use bc. (awk should still work)
$ echo "scale=4; (-225.2*7+30*6)/17+(19^2)/9" | bc
-42.0300
You can use variables in the expression as usual,
$ read inpt
1234
$ echo "scale=4; $inpt * 0.01" | bc
12.34
Is this what you are looking for ?
echo "25 50"| awk '{print $2,"/",$1}' | bc

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'}`

Resources