Bash script to beep when temperature hot on FreeBSD - bash

I have been working on a Bash script to beep when the PC is too hot.
I have removed the beep to try identifying the problem.
What I have so far:
temp=$(sysctl -n hw.acpi.thermal.tz0.temperature | tr -d 'C')
echo $temp
if ["$temp" -gt "30.1"]
then
echo "temp hot"
else
echo "temp ok"
fi
My output is
54.1
temp.sh: line 4: [54.1: command not found
temp ok
Removing the if statement just outputs
54.1
so I think it's the if statement that's not working.

You should use double parenthesis (( )) to do arithmetic expressions, and since Bash cannot handle decimal values, you just have to remove the dot (as if you want to multiply it by ten).
temp=$(sysctl -n hw.acpi.thermal.tz0.temperature | tr -d 'C')
max_temp=50.2
(( ${temp//./} > ${max_temp//./} )) && echo "temp hot" || echo "temp ok"
Be sure to use the same format for both values (especially leading zeros, 54.10 would become 5410).
If the format cannot be guaranteed, there is a second method, as mentioned by Benjamin W, using bc. You can send to this command a logical operation involving floats, it returns 0 if true, 1 otherwise.
temp=$(sysctl -n hw.acpi.thermal.tz0.temperature | tr -d 'C')
max_temp=50.2
(( $(echo "$temp > $max_temp" | bc) )) && echo "temp hot" || echo "temp ok"

Your immediate problem is syntax error -- you omitted the space between the command ([ -- /bin/[) and its arguments ($temp). The last argument of [ must be ] -- your script is missing a blank there too, which makes the last argument "30.1"]. The quotes aren't necessary here, BTW, and only make things harder to read.
This is a generic sh-scripting quation, it has nothing to do with FreeBSD nor with temperature-measuring.

Related

Can't explain syntax error in bash script

In my script I wrote down this control expression:
if ! [[ $start -gt $(cut -f3 rooms.txt) -a $end -gt $(cut -f4 rooms.txt) ]]; then
echo "Invalid prenotation";
./prenote.sh;
fi
start and end are simple numbers. Each record in file rooms.txt is built in this way:
room;date;start;end;username
There are non blank spaces in the record.
When I run the script I get a syntax error near the if statement.
Can someone tell me where the error is? Thanks
The operator -a for "and" is not valid in [[...]] conditionals. Use && instead.
But if you're doing numeric comparisons in bash, it might make more sense to use ((...)) instead of [[...]]. Then the normal relational operators are numeric instead of string-based, so you can use > instead of -gt:
if ! (( start > $(cut -f3 rooms.txt) && end > $(cut -f4 rooms.txt) )); then
...
fi
However, this approach only works if rooms.txt has only one line; otherwise, you'll get syntax errors when the $(cut...) commands produce more than one number. I'm not sure exactly what problem you're solving, but an approach something like this might be fruitful:
while read _ _ low high _; do
if ! (( start > low && end > high )); then
...
fi
done <rooms.txt

Monitoring script does not output anything

Can anybody tell me what's wrong in this script, it's not working. When I run it, there is no output/error on the screen.
The script is to monitor a log file to check the value of one of the columns, and if it is more than 20 it will echo a message.
#!/bin/bash
while true ; do
COUNT=`tail -f /monitoring/log.20160121|cut -d" " -f39`
echo $COUNT
if [ $COUNT -gt 20 ] ;then
echo "Count is high"
break
fi
sleep 10
done
tail -f does not exit, so your script gets stuck there. I assume you are just interested in the last line of the log; tail -n 1 does that.
Other points:
Indentation: not sure how much got lost while copy pasting, but proper indentation massively increases readability of your code
Variable names: all uppercase variable names are discouraged as they might clash with reserved (environment) variable names
Command substitution with backticks (` `) is discouraged and the form $( ) is preferred; makes for example nesting easier
Since you're using Bash, you can use the (( )) conditional construct, which is better suited for comparing numbers than [ ]
Together:
#!/bin/bash
while true; do
count=$(tail -n 1 /monitoring/log.20160121 | cut -d " " -f 39)
echo $count
if (( count > 20 )); then
echo "Count is high"
break
fi
sleep 10
done

Using bash, how to assign integer to variable using echo

I'd like to understand bash a bit better as I'm apparently horrible at it...
I'm trying to generate a sequence of constant width integers, but then test them to do something exceptional for particular values. Like so:
for n in $(seq -w 1 150)
do
# The next line does not work: doit.sh: line 9: XX: command not found
#decval= $( echo ${n} | sed 's/^0//g' | sed 's/^0//g' )
#if [[ ${decal} -eq 98 ]] ; then
if [[ $( echo ${n} | sed 's/^0//g' | sed 's/^0//g' ) -eq 98 ]] ; then
echo "Do something different for 98"
elif [[ $( echo ${n} | sed 's/^0//g' | sed 's/^0//g' ) -eq 105 ]] ; then
echo "Do something different for 98"
fi
done
This script works for my purposes, but if I try and make the assignment 'decval= $(…' I get an error 'command not found'. I don't understand this, can someone explain?
Also, is there an improvement I can make to this script if I have a large number of exceptions to prevent a long list of if ; then elif … ?
The problem is in the space between = and $:
decval= $(…
You should write without spaces:
decval=$(...
Because, if you write the space, your shell reads decval= as declval="" and treats the result of $(echo...) as the name of a command to execute, and obviously it doesn't find the command.
Also (just a small optimization), you can write:
sed 's/^0\+//'
instead of
sed 's/^0//g' | sed 's/^0//g'
Here:
0\+ means 0 one or more times;
g is removed, because g means replace all occurences in the string, and you have only one occurence (^ can be only one time in a string).
Also, you can check your variable even with leading zeros, without sed:
[[ "$n" =~ "0*98" ]]

Unexpected end of file bash script

This is just a simple problem but I don't understand why I got an error here. This is just a for loop inside an if statement.
This is my code:
#!/bin/bash
if (!( -f $argv[1])) then
echo "Argv must be text file";
else if ($#argv != 1) then
echo "Max argument is 1";
else if (-f $argv[1]) then
for i in `cut -d ',' -f2 $argv[1]`
do
ping -c 3 $i;
echo "finish pinging host $i"
done
fi
Error is in line 16, which is the line after fi, that is a blank line .....
Can someone please explain why i have this error ????
many, many errors.
If I try to stay close to your example code:
#!/bin/sh
if [ ! -f "${1}" ]
then
echo "Argv must be text file";
else if [ "${#}" -ne 1 ]
then
echo "Max argument is 1";
else if [ -f "${1}" ]
then
for i in $(cat "${1}" | cut -d',' -f2 )
do
ping -c 3 "${i}";
echo "finish pinging host ${i}"
done
fi
fi
fi
another way, exiting each time the condition is not met :
#!/bin/sh
[ "${#}" -ne 1 ] && { echo "There should be 1 (and only 1) argument" ; exit 1 ; }
[ ! -f "${1}" ] && { echo "Argv must be a file." ; exit 1 ; }
[ -f "${1}" ] && {
for i in $(cat "${1}" | cut -d',' -f2 )
do
ping -c 3 "${i}";
echo "finish pinging host ${i}"
done
}
#!/usr/local/bin/bash -x
if [ ! -f "${1}" ]
then
echo "Argument must be a text file."
else
while-loop-script "${1}"
fi
I have broken this up, because I personally consider it extremely bad form to nest one function inside another; or truthfully to even have more than one function in the same file. I don't care about file size, either; I've got several scripts which are 300-500 bytes long. I'm learning FORTH; fractalism in that sense is a virtue.
# while-loop-script
while read line
do
IFS="#"
ping -c 3 "${line}"
IFS=" "
done < "${1}"
Don't use cat in order to feed individual file lines to a script; it will always fail, and bash will try and execute the output as a literal command. I thought that sed printing would work, and it often does, but for some reason it very often substitutes spaces for newlines, which is extremely annoying as well.
The only absolutely bulletproof method of feeding a line to a script that I know of, which will preserve all space and formatting, is to use while-read loops, rather than substituted for cat or for sed loops, as mentioned.
Something else which you will need to do, in order to be sure about preserving whitespace, is to set the internal field seperator (IFS) to something that you know your file will not contain, and then resetting it back to whitespace at the end of the loop.
For every opening if, you must have a corresponding closing fi. This is also true for else if. Better use elif instead
if test ! -f "$1"; then
echo "Argv must be text file";
elif test $# != 1; then
echo "Max argument is 1";
elif test -f "$1"; then
for i in `cut -d ',' -f2 "$1"`
do
ping -c 3 $i;
echo "finish pinging host $i"
done
fi
There's also no argv variable. If you want to access the command line arguments, you must use $1, $2, ...
Next point is $#argv, this evaluates to $# (number of command line args) and argv. This looks a lot like perl.
Furthermore, testing is done with either test ... or [ ... ], not ( ... )
And finally, you should enclose at least your command line arguments in double quotes "$1". If you don't and there is no command line argument, you have for example
test ! -f
instead of
test ! -f ""
This lets the test fail and go on to the second if, instead of echoing the proper message.

How to get first character of variable

I'm trying to get the first character of a variable, but I'm getting a Bad substitution error. Can anyone help me fix it?
code is:
while IFS=$'\n' read line
do
if [ ! ${line:0:1} == "#"] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
Am I doing something wrong or is there a better way of doing this?
-- EDIT --
As requested - here's some sample input which is stored in /some/file.txt
$MOZ_HOME/mobile/android/chrome/content/browser.js
$MOZ_HOME/mobile/android/locales/en-US/chrome/browser.properties
$MOZ_HOME/mobile/android/components/ContentPermissionPrompt.js
To get the first character of a variable you need to say:
v="hello"
$ echo "${v:0:1}"
h
However, your code has a syntax error:
[ ! ${line:0:1} == "#"]
# ^-- missing space
So this can do the trick:
$ a="123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
doesnt start with #
$ a="#123456"
$ [ ! "${a:0:1}" == "#" ] && echo "doesnt start with #"
$
Also it can be done like this:
$ a="#123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
$
$ a="123456"
$ [ "$(expr substr $a 1 1)" != "#" ] && echo "does not start with #"
does not start with #
Update
Based on your update, this works to me:
while IFS=$'\n' read line
do
echo $line
if [ ! "${line:0:1}" == "#" ] # Error on this line
then
eval echo "$line"
eval createSymlink $line
fi
done < file
Adding the missing space (as suggested in fedorqui's answer ;) ) works for me.
An alternative method/syntax
Here's what I would do in Bash if I want to check the first character of a string
if [[ $line != "#"* ]]
On the right hand side of ==, the quoted part is treated literally whereas * is a wildcard for any sequence of character.
For more information, see the last part of Conditional Constructs of Bash reference manual:
When the ‘==’ and ‘!=’ operators are used, the string to the right of the operator is considered a pattern and matched according to the rules described below in Pattern Matching
Checking that you're using the right shell
If you are getting errors such as "Bad substitution error" and "[[: not found" (see comment) even though your syntax is fine (and works fine for others), it might indicate that you are using the wrong shell (i.e. not Bash).
So to make sure you are using Bash to run the script, either
make the script executable and use an appropriate shebang e.g. #!/bin/bash
or execute it via bash my_script
Also note that sh is not necessarily bash, sometimes it can be dash (e.g. in Ubuntu) or just plain ol' Bourne shell.
Try this:
while IFS=$'\n' read line
do
if ! [ "${line:0:1}" = "#" ]; then
eval echo "$line"
eval createSymlink $line
fi
done < /some/file.txt
or you can use the following for your if syntax:
if [[ ! ${line:0:1} == "#" ]]; then
TIMTOWTDI ^^
while IFS='' read -r line
do
case "${line}" in
"#"*) echo "${line}"
;;
*) createSymlink ${line}
;;
esac
done < /some/file.txt
Note: I dropped the eval, which could be needed in some (rare!) cases (and are dangerous usually).
Note2: I added a "safer" IFS & read (-r, raw) but you can revert to your own if it is better suited. Note that it still reads line by line.
Note3: I took the habit of using always ${var} instead of $var ... works for me (easy to find out vars in complex text, and easy to see where they begin and end at all times) but not necessary here.
Note4: you can also change the test to : *"#"*) if some of the (comments?) lines can have spaces or tabs before the '#' (and none of the symlink lines does contain a '#')

Resources