Can't explain syntax error in bash script - bash

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

Related

Bash script to beep when temperature hot on FreeBSD

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.

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 '#')

unexpected EOF while looking for matching in sorting script

Can someone please assist with my script here, someone with fresh eyes, been working on it for a while. So:
At the end of the script I am trying to enable condition that if a user inputs a single letter
as an input script terminates/exits with "exit" message:
#!/bin/bash
echo 'Enter file names (wild cards OK)'
read files
for input_source in $files ; do
if test -f "$input_source" ; then
sort $input_source | uniq -c | head -10
elif "$input_source" = [a-z] ; then
exit
echo 'Exit'
fi
done
You have an unterminated string literal:
elif "$input_source = [a-z] ; then # where's the end?
Furthermore, once you fix that, it's still not valid. You'll probably want to use test, [, or [[, and even then, = won't do it. You probably want this:
elif [[ "$input_source" =~ "^[a-z]$" ]]; then

unary operator expected in shell script when comparing null value with string

I have two variables
var=""
var1=abcd
Here is my shell script code
if [ $var == $var1 ]; then
do something
else
do something
fi
If I run this code it will prompt a warning
[: ==: unary operator expected
How can I solve this?
Since the value of $var is the empty string, this:
if [ $var == $var1 ]; then
expands to this:
if [ == abcd ]; then
which is a syntax error.
You need to quote the arguments:
if [ "$var" == "$var1" ]; then
You can also use = rather than ==; that's the original syntax, and it's a bit more portable.
If you're using bash, you can use the [[ syntax, which doesn't require the quotes:
if [[ $var = $var1 ]]; then
Even then, it doesn't hurt to quote the variable reference, and adding quotes:
if [[ "$var" = "$var1" ]]; then
might save a future reader a moment trying to remember whether [[ ... ]] requires them.
Why all people want to use '==' instead of simple '=' ? It is bad habit! It used only in [[ ]] expression. And in (( )) too. But you may use just = too! It work well in any case. If you use numbers, not strings use not parcing to strings and then compare like strings but compare numbers. like that
let -i i=5 # garantee that i is nubmber
test $i -eq 5 && echo "$i is equal 5" || echo "$i not equal 5"
It's match better and quicker. I'm expert in C/C++, Java, JavaScript. But if I use bash i never use '==' instead '='. Why you do so?

compound comparisons in bash

can anybody explain why the following bash code involving compound operators is not behaving as expected? basically, nothing enters the if statement inside the for loop but i am passing it correct parameters that should return something by running:
./my_bash_script 20100101 20120101
dates.txt is a list of all days since 2000
#!/bin/bash
old_IFS=$IFS
IFS=$'\n'
lines=($(cat dates.txt)) # array
IFS=$old_IFS
for (( i=1; i<${#lines[#]}; i++ ))
do
if [[ ${line[$i]} -ge $1 && ${line[$i]} -le $2 ]]; then
echo 0 > ${line[$i]} # redirect to file
echo ${line[$i]}
fi
done
The problem is that you've declared an array named lines, but then you try to access it as though it were named line. You need to change every occurrence of ${line[$i]} to ${lines[$i]}.
Better yet, you can dispense with the arithmetic for-loop, and write:
for line in "${lines[#]}" ; do
which will let you refer to the line as $line or "$line" rather than as ${lines[$i]}.
(By the way, how come you have that logic to modify $IFS? It seems like its default value would work just as well.)

Resources