Bash unexpected token near 'then' - bash

I have a syntax error, more like a unexpected symbol near a token 'then', but I can't figure it out..
#!/bin/bash
function Functie(){
LINE=1
while read -r CURRENT_LINE; do
CONTOR=1
for word in "$CURRENT_LINE"; do
if[ "$word" == "$2" ];
then
CONTOR=$CONTOR+1
fi
done
if [ "$CONTOR" -eq "$3" ];
then
echo "$CURRENT_LINE"
fi
LINE=$LINE+1
done < "./"$1""
}
Functie "File1.txt" "Ana" "2"

Run your code through ShellCheck to catch several syntax errors.
Correcting them yields:
#!/bin/bash
function Functie(){
LINE=1
while read -r CURRENT_LINE; do
CONTOR=1
for word in $CURRENT_LINE; do
if [ "$word" == "$2" ];
then
CONTOR=$CONTOR+1
fi
done
if [ "$CONTOR" -eq "$3" ];
then
echo "$CURRENT_LINE"
fi
LINE=$LINE+1
done < ./"$1"
}
Functie "File1.txt" "Ana" "2"
One issue it doesn't detect is the bad assignments. To increment a variable write one of these:
CONTOR=$(($CONTOR+1))
CONTOR=$((CONTOR+1))
((CONTOR += 1))
((++CONTOR))

Related

How do I use round brackets in an 'if' condition

I'm creating a bash script and somewhere inside I have this code:
if [ $# -eq 2 -a (! -r "$2" -o ! -f "$2") ]; then
echo "rvf: bestand \""$2"\" bestaat niet of is onleesbaar" 1>&2
exit 2
fi
When i try to run this inside the script I get this error:
Syntax Error (bash -n):
rvf: line 14: syntax error in conditional expression
rvf: line 14: syntax error near `-a'
rvf: line 14: `if [[ $# -eq 2 -a (! -r "$2" -o ! -f "$2") ]]; then'
How does '()' work inside Bash scripts?
[[ doens't support -a, and it is considered obsolete and non portable for [. The correct solution using [ would be
if [ "$#" -eq 2 ] && { [ ! -r "$2" ] || [ ! -f "$2" ]; }; then
Grouping is done with { ... } rather than ( ... ) to avoid creating an unnecessary subshell.
Using [[ is simplifies to
if [[ "$#" -eq 2 && ( ! -r "$2" || ! -f "$2" ) ]]; then
Parentheses can be used for grouping inside [[; as a compound command, it uses separate parsing and evaluation rules, compared to an ordinary command like [ (which is just an alias for test, not syntax of any kind).
In either case, De Morgan's laws lets you refactor this to something a little simpler:
if [ "$#" -eq 2 ] && ! { [ -r "$2" ] && [ -f "$2" ] }; then
if [[ "$#" -eq 2 && ! ( -r "$2" && -f "$2" ) ]]; then
There are multiple points of confusion here.
[ can (as an optional XSI extension to the standard) support ( as a separate word (meaning there needs to be spaces around it), but the POSIX sh specification marks it (like -a and -o) as "obsolescent" and advises against its use.
[[ does support (, but again, it needs to always be a separate word.
Don't do that at all, though. You're using only well-supported and portable functionality if you keep each test its own simple command and combine them only with the shell's boolean logic support.
That is:
if [ "$#" -eq 2 ] && { [ ! -r "$2" ] || [ ! -f "$2" ]; }; then
echo "rvf: bestand \"$2\" bestaat niet of is onleesbaar" >&2
exit 2
fi
Restructure your logic.
"Not A or Not B" is just a more complicated way to say "not (A and B)".
In bash, try
if [[ "$#" == 2 ]] && ! [[ -r "$2" && -f "$2" ]]; then
Better,
if [[ "$#" == 2 && -r "$2" && -f "$2" ]]
then : all good code
else : nope code
fi
Even better,
if [[ "$#" == 2 ]] # correct args
then if [[ -r "$2" ]] # is readable
then if [[ if -f "$2" ]] # is a file
then echo "all good"
: do all good stuff
else echo "'$2' not a file"
: do not a file stuff
fi
else echo "'$2' not readable"
: do not readable stuff
fi
else echo "Invalid number of args"
: do wrong args stuff
fi
Clear error logging is worth breaking the pieces out.
Even better, imho -
if [[ "$#" != 2 ]]
then : wrong args stuff
fi
if [[ ! -r "$2" ]]
then : unreadable stuff
fi
if [[ ! -f "$2" ]]
then : do not a file stuff
fi
: do all good stuff

How to use elif with && and -o in a bash script properly

Bash version 4.4.20
Ubuntu 16.04
I need to compare time and extensions for a particular project I have. Below is something similar to what I am trying to do but the error is the same. I am not sure where exactly the error is as shellcheck is not producing one.
#!/bin/bash
#
while read -r filename; do
extension="${filename##*.}"
if [ "$extension" == "zip" ] && [ "$filename" == "one.zip" ]; then
echo "Filename is $filename"
elif [ "$extension" == "zip" ] && [ "$filename" == "file_1.zip" ] -o [ "$filename" == "file_2.zip" ] -o [ "$filename" == "file_3.zip" ]; then
echo "Filename is $filename"
elif [ "$extension" == "csv" ] && [ "$filename" == "two.csv" ]; then
echo "Filename is $filename"
else
echo "Filename is $filename"
fi
done<fileList.txt
Error:
Filename is one.zip
check.sh: line 8: [: too many arguments
Filename is file_1.zip
check.sh: line 8: [: too many arguments
Filename is file_2.zip
check.sh: line 8: [: too many arguments
Filename is file_3.zip
Filename is two.csv
Filename is three.sql
Use pattern matching to your advantage:
while IFS= read -r filename; do
if [[ "$filename" = one.zip ]; then
echo "Filename is $filename"
elif [[ "$filename" = file_[123].zip ]; then
echo "Filename is $filename"
elif [[ "$filename" = two.csv ]; then
echo "Filename is $filename"
else
echo "Filename is $filename"
fi
done < fileList.txt
A case statement will work in any POSIX shell, not just a shell that supports a bash-like [[ ... ]] command.
while IFS= read -r filename; do
case $filename in
one.zip) echo "Filename is $filename" ;;
file_[123].zip) echo "..." ;;
two.csv) echo "..." ;;
*) echo "..." ;;
esac
done
To match a range of years(?), you can use
case $value in
200[0-9]|201[0-9]|202[0-1]) echo "Year between 2000 and 2021" ;;
esac
You can't do that as simply with [[ value = ... ]], since the | is part of the case statement's syntax, not an alternation operator in the pattern. Instead, you would need multiple match operators:
if [[ $value = 200[0-9] || $value = 201[0-19] || $value = 202[0-1] ]]; then
Here an explanation for why you get this error, as you get already good answers on how to do it correctly:
Note that the command [ is equivalent to test, hence your line 8 in effect (also removing unnecessary quotes) contains:
elif test "$extension" == zip ] && test "$filename" == file_1.zip ] -o [ "$filename" == file_2.zip ] -o [ "$filename" == file_3.zip ]
From the test man-page, we can see that the closing ] is optional, but if it is present, it terminates the expression to be tested.
The first test, which is just
test "$extension" == "zip" ]
is fine in this respect, but the second one starts with
test "$filename" == file_1.zip ] -o [ ...
Hence, when parsing the arguments, test encounters a ] and knows that this is the terminator for the arguments, but then finds another argument (-o), and doesn't know what to do with it. Hence it complains that it has got too many arguments
I have updated the syntax to what I think is the modern bash idioms. The script now works. Please correct me if I missed something else :
#!/bin/bash
#
while read -r filename
do
extension="${filename##*.}"
if [[ "$extension" = "zip" && "$filename" = "one.zip" ]]
then
echo "Filename is $filename"
elif [[ "$extension" = "zip" && "$filename" = "file_1.zip" ]] \
|| [[ "$filename" = "file_2.zip" ]] \
|| [[ "$filename" = "file_3.zip" ]]
then
echo "Filename is $filename"
elif [[ "$extension" = "csv" && "$filename" = "two.csv" ]]
then
echo "Filename is $filename"
else
echo "Filename is $filename"
fi
done < fileList.txt
Regards!

My max function is throwing an error even though its the same as my min function but flipped, can't find the error?

When I run ShellCheck on my script, it gives me these errors:
Line 27:
{
^-- SC1009: The mentioned syntax error was in this brace group.
Line 30:
for in `cat popconfile`
^-- SC1073: Couldn't parse this for loop. Fix to allow more checks.
^-- SC1058: Expected 'do'.
^-- SC1072: Expected 'do'. Fix any mentioned problems and try again
The script is:
#!/bin/bash
#getalldata() {
#find . -name "ff_*" -exec sed -n '4p' {} \;
#}
#Defining where the population configuration file is which contains all the data
popconfile=$HOME/testarea
#Function to find the average population of all of the files
averagePopulation()
{
total=0
list=$(cat popconfile)
for var in "${list[#]}"
do
total=$((total + var))
done
average=$((total/$(${#list[*]} popconfile)))
echo "$average"
}
#Function to find the maximum population from all the files
maximumPopulation()
{
max=1
for in `cat popconfile`
do
if [[ $1 > "$max" ]]; then
max=$1
echo "$max"
fi
done
}
#Function to find the minimum population from all the files
minimumPopulation()
{
min=1000000
for in `cat popconfile`
do
if [[ $1 < "$min" ]]; then
max=$1
echo "$min"
fi
done
}
#Function to show all of the results in one place
function showAll()
{
echo "$min"
echo "$max"
echo "$average"
}
Although my min function is very similar, I get no error from it; if I switch my min and max function around, then the error is reported for the function that occurs first.
The error is just saying "expected do" - but I already have a do statement. So where is the error?
You are missing the index in the for loop. The immediate fix would be
maximumPopulation()
{
max=1
for i in `cat popconfile`
do
if [[ $i > "$max" ]]; then
max=$i
echo "$max"
fi
done
}
However, you shouldn't use a for loop to iterate over the lines of a file; see Bash FAQ 001. Instead, use a while loop.
maximumPopulation () {
max=1
while IFS= read -r i; do
if (( i > max )); then
max=$i
fi
done < popconfile
printf '%d\n' "$max"
}

line 21: syntax error near unexpected token `done', and then: line 21: ` done'

I got the mentioned errors in this bash script:
line 21: syntax error near unexpected token `done'
line 21: ` done'
I looked for empty loops like mentioned here in other questions, and I see nothing. Any idea why?
#!/bin/bash
while read -a line; do
:
for word in ${array[*]}; do
let last_index=${#arr[*]}-1
bool=false
if [[ $word != [1-9]* && bool==true ]]; then
echo -n $word
else if [[ $word != [1-9]* && bool==false ]]; then
bool=false
echo -n "_$word"
else if [[ $word == ${array[last_index]} ]]; then
date=${word//./ }
for element in $date; do
element=${element/0}
done
echo " $date"
else echo -n " $word "
fi
done
done
You've only got one fi, where you have three if statements, so the done isn't expected. You need to use elif rather than else if.
#!/bin/bash
while read -a line; do
:
for word in ${array[*]}; do
let last_index=${#arr[*]}-1
bool=false
if [[ $word != [1-9]* && bool==true ]]; then
echo -n $word
elif [[ $word != [1-9]* && bool==false ]]; then
bool=false
echo -n "_$word"
elif [[ $word == ${array[last_index]} ]]; then
date=${word//./ }
for element in $date; do
element=${element/0}
done
echo " $date"
else echo -n " $word "
fi
done
done
Or you need to indent the second and third if statements more and add the missing fi markers.
Incidentally, the colon command is harmless but does nothing useful in this script.

[: : integer expression expected

COUNTER=0
let COUNTER=COUNTER+1
count=`ssh -i /var/www/.ssh/id_rsa_root -o stricthostkeychecking=no $host $cmd`
count1=`echo $count | awk '{print $4}'`
printf "count1 : $count1\n"
result1=${count1/.*}
if [ "$result1" -ge "0" ]; then
echo $host
else
echo $host
exit
fi
If the value of $result1 is INTEGER and greater than zero, it'll goto IF loop (works fine for me)
But when it is not INTEGER, it is coming to else loop (which it is suppose to do) with the following error in the Output
line 55: [: : integer expression expected
but i dont want the above error in my output. I tried to use 2>/dev/null with this but no luck.
please help!
If you want to handle an empty result gracefully, check for it explicitly:
if [ -z "$result1" ]; then
: "ignoring empty string"
elif [ "$result1" -ge 0 ]; then
printf '%s\n' "$host"
else
printf '%s\n' "$host"
exit
fi
You could also check if result1 is a valid integer before making arithmetic comparisons:
function isNumber () {
[[ $1 =~ ^-?[0-9]+$ ]]
}
if ! isNumber "$result1"; then
echo "not a number"
elif [ "$result1" -ge "0" ]; then
echo "null or positive"
else
echo "negative"
fi
Change if [ "$result1" -ge "0" ]; then to
if (( result1 >= 0 )); then
This syntax won't throw any errors if result1 isn't defined (or empty) or happen to be a string somehow.

Resources