Syntax error in conditional expression and near `then' in Ubuntu terminal - bash

I'm just learning terminal commands, I'm trying to create my own command, but I have the following problem
./myFile: line 10: syntax error in conditional expression
./myFile: line 11: syntax error near `then'
./myFile: line 11: ` then'
there is my code
#!/bin/bash
echo "please enter name of the folder"
read NAME1
if [[ $NAME1 ]]
then
echo "enter first number"
read NUM1
if [[ $NUM1 ]] && [[ $NUM1 -ge 0]]
then
echo "enter last number"
read NUM2
if [[ $NUM2 ]] && [[ $NUM2 -gt $NUM ]]
then
mkdir $NAME1{$NUM1..$NUM2}
else
echo"please enter last number to continue"
fi
else
echo "please enter first number to continue"
fi
else
echo "please enter name of the folder to continue"
fi

Firstly, the expression [[ $NAME1 ]] is not valid, or at least, does not do what you think it does. I believe you are trying to determine if the user actually typed in a string. To do this, you should either test for a non-empty string ([[ -n ${NAME1} ]]) or test for a string length greater than zero ([[ ${#NAME1} -gt 0 ]]). The same applies when you are testing $NUM1 and $NUM2.
Secondly, when dealing with user input, you should take care to avoid testing empty strings. This is best achieved by quoting your variables. For example: [[ "${NUM1}" -gt 0 ]].
Thirdly, spaces are important in tests. always leave a space after the [[ and before the ]].
In addition, $NUM (line 15) is not declared, so will evaluate to the empty string. This should be set somewhere earlier in the script.
There are many other areas in which this script could be improved (e.g. checking for numerical input, checking for valid folder names, etc. But the above changes should get you past the immediate errors.

Related

Parameters work properly when remove their quoting

I am puzzled about the verbose of quoting in the script. Take an example from the instruction I followed:
min_val=1
max_val=100
int=50
if [[ "$int" =~ ^-?[0-9]+$ ]]; then
if [[ "$int" -ge "$min_val" && "$int" -le "$max_val" ]]; then
echo "$int is within $min_val to $max_val."
else
echo "$int is out of range."
fi
else
echo "int is not an integer." >&2
exit 1
fi
Run it and come by:
$ bash test_integer3.sh
50 is within 1 to 100.
When I removed all the quoting in testing:
if [[ $int =~ ^-?[0-9]+$ ]]; then
if [[ $int -ge $min_val && $int -le $max_val ]]; then
echo "$int is within $min_val to $max_val."
else
echo "$int is out of range."
fi
else
echo "int is not an integer." >&2
exit 1
fi
It's still working properly.
$ bash test_integer3.sh
50 is within 1 to 100.
Why should live with the habit of writing redundant quoting?
The real problem comes when you start to use [ command over [[ in your scripts. [[ is bash's improvement to the [ command. It has several enhancements that make it a better choice to write scripts targeting bash.
One such improvement would be that you no longer have to quote variables because [[ handles empty strings and strings with white-space more intuitively. For example consider your script written with [ for the un-quoted case and for discussions sake, one of your variables is empty
#!/usr/bin/env bash
min_val=
max_val=
int=50
if [[ $int =~ ^-?[0-9]+$ ]]; then
if [ $int -ge $min_val -a $int -le $max_val ]; then
echo "$int is within $min_val to $max_val."
else
echo "$int is out of range."
fi
else
echo "int is not an integer." >&2
exit 1
fi
One thing to note is I've re-written the combined conditional using the -a syntax since [ does not support && operator within but could be combined using && as [ $int -ge $min_val ] && [ $int -le $max_val ]
You would see things going bad and seeing errors as below which means that one of the conditionals involving -le is gone wrong on seeing an empty string.
1_script.sh: line 7: [: -a: integer expression expected
50 is out of range.
whereas with same code for undefined variables and replacing the expression to use [[ would gracefully handle the empty strings to produce just an incorrect result as
50 is out of range.
So to sum it up, from the many advantages over using [[, the particular advantage on your case is to handle variables if there could be empty strings in your conditionals.
Quoting is used to to stop the word splitting. In the case above it is not necessary but consider a case like this: you have a directory and having theses files file1.txt, file2.txt, old.txt and file1 old.txt.
If you wish to remove the file file1 old.txt and run the command
rm file1 old.txt
then it will remove the file old.txt instead of what you expected.
In your piece of code you don't need quotes as you discovered. However, using quotes is considered "good practice" because unexpected things can happen without quotes. For example if you run the code with int equal to say "foo bar" you might get some strange results without quotes.
It is like the double and triple equals in JavaScript. You could probably get away with only double equals but some unexpected results might occur.

0-9 and [[:digit:]] will match 1-10, but not more

I'm trying to make a script, just for fun which creates a new User and Generates a Password for him.
Now I have to check if the user enters something stupid, instead of a digit.
function checkifcorrectnum() {
#Check if the User did something else than enter a number
#And check if the Number is absurdly big
case "$1" in
[[:digit:]] ) if [ "$1" -gt "255" ]; then echo "Too big!" ; else : ; fi ;;
*) echo "Please enter a number!"; exit ;;
esac
}
But when I run the script, and enter 1-9 it works, but anything higher wont
You are only matching for a single digit by just [[:digit:]]. The bash globbing cannot be used like Regex and match any token any number of times by operators like * or +. If you want to stick with your method and you know exactly how many digits you want to allow then use e.g. for 2 digits:
case "$1" in
[[:digit:]][[:digit:]])
If you are not sure then:
case "$1" in
[[:digit:]]*)
* expands to any number of character.
But i think you should look at Regex matching offered by bash by the =~ operator of [[, so your whole function can be rewritten as:
if [[ $1 =~ ^[[:digit:]]+$ ]]; then
[[ $1 -gt 255 ]] && echo "Too big!"
else
echo 'Please enter a number!' && exit
fi
Also as you are not doing anything if the number is <=255 so [[ $1 -gt 255 ]] && echo "Too big!" is enough.
Since it is easier to test if a string is not a number than to test if it is, I suggest inverting the order of the tests:
function checkifcorrectnum() {
case "$1" in
*[^[:digit:]]*) echo "Please enter a number";;
*) [ "$1" -gt "255" ] && echo "Too big!" ;;
esac;
}
The glob *[^[:digit:]]* matches if any character in $1 is not a digit.
Examples:
$ checkifcorrectnum 255
$ checkifcorrectnum 256
Too big!
$ checkifcorrectnum 25z
Please enter a number
As an aside, the keyword function is bash-only and generally not necessary. With the keyword removed, the code works not only in bash but also in any POSIX compatible shell.

Ambiguous redirect in a Unix Shell Script with if-elif-else statement

I've just begun writing if-else statements in Unix. This is a short Bash script I recently wrote. I keep getting an ambiguous redirect error, however I can't figure out the problem.
When executed, I get the following error message:
stringComp: line 17: $str2: ambiguous redirect
stringComp: line 20: $str2: ambiguous redirect
stringComp: line 23: [: too many arguments
Here is the script:
str1="I like turtles"
str2="I want to be Iron Man"
if [ $str1 > $str2 ]
then
echo "The first string is longer than the second"
elif [ $str1 < $str2 ]
then
echo "The second string is longer than the first"
elif [ $str1 = $str2 ]
then
echo "Both strings are of equal length"
else
echo "Invalid argument"
fi
Any advice on what to do here? Thanks.
$str1 > $str2 is not comparing string length, > is actually redirection operator when used with [ which is a separate program in Unix systems.
You can just use ${#str1} to get the string length:
str1="I like turtles"
str2="I want to be Iron Man"
if [[ ${#str1} -gt ${#str2} ]]
then
echo "The first string is longer than the second"
elif [[ ${#str1} -lt ${#str2} ]]
then
echo "The second string is longer than the first"
elif [[ ${#str1} -eq ${#str2} ]]
then
echo "Both strings are of equal length"
else
echo "Invalid argument"
fi
Also better to use [[...]] in bash.

': not a valid identifier [duplicate]

This question already has answers here:
Are shell scripts sensitive to encoding and line endings?
(14 answers)
Closed 1 year ago.
I have removed the two if statements from before and replaced them with a case statement and tried to remove all the error from the code.
I am trying to run this code but i get an error in the case statement.
"': not a valid identifier
main.sh: line 5: syntax error near unexpected token $'in\r''
'ain.sh: line 5:case "$y" in "
#!/bin/bash
echo "1. Julian"
echo "2. Gregorian"
read y
case "$y" in
1)echo "Enter your year (1900-2050)"
read x
if [[ $x -ge 1900 && $x -le 2050 ]]
then
a=$((x%19))
b=$((x/100))
c=$((x%100))
d=$((b/4))
e=$((b%4))
g=$(((8*b+13)/25))
h=$(((19*a+b-d-g+15)%30))
m=$(((a+11*h)/319))
j=$((c/4))
k=$((c%4))
l=$(((2*e+2*j-k-h+m+32)%7))
n=$(((h-m+l+90)/25))
p=$(((h-m+l+n+19)%32))
o=$(date +"$x-$n-$p")
echo "Gregorian Easter is on $O."
else
echo "Invalid Input"
fi
;;
2) echo "Enter your year (1900-2050)"
read x
if [[ $x -ge 1900 && $x -le 2050 ]]
then
A=$((x%4))
B=$((x%7))
C=$((x%19))
D=$(((19*C+15)%30))
E=$(((2*A+4*B−D+34)%7))
M=$(((D+E+114)/31))
day=$(((D+E+115)%31))
o=$(date +"$x-$M-$day")
echo "Gregorian Easter is on $o."
else
echo "Invalid Input"
fi
;;
0) exit ;;
esac
Addressing the issue in the title, ': not a valid identifier
This happens when read is passed a variable name that ends with a carriage return symbol.
When that symbol is printed, it sends the cursor back to the beginning of the line.
Thus, read foo<CR> tries to print:
foo<CR>': not a valid identifier
However, because <CR> sends the cursor back, what it actually prints is:
': not a valid identifier
To fix this, run dos2unix on your file; run :set fileformat=unix in vim, or otherwise transform it to have UNIX newlines instead of DOS newlines.
Replace #!/bin/sh with #!/bin/bash.
Replace elif[ with elif [.
Add a fi line above the elif line.
Add a then line below the elif line.
Replace -gt with -ge.
Replace -lt with -le.
Having done this your o variable may still end up empty, but you can debug that by adding echo +"$x-$n-$p".
Still, there were too many errors. Anyway, I think you want something like this:
#!/bin/bash
echo "1. Julian"
echo "2. Gregorian"
read y
case "$y" in
1)echo "Enter your year (1900-2050)"
read x
if [[ $x -ge 1900 && $x -le 2050 ]]; then
a=$((x%19))
b=$((x/100))
c=$((x%100))
d=$((b/4))
e=$((b%4))
g=$(((8*b+13)/25))
h=$(((19*a+b-d-g+15)%30))
m=$(((a+11*h)/319))
j=$((c/4))
k=$((c%4))
l=$(((2*e+2*j-k-h+m+32)%7))
n=$(((h-m+l+90)/25))
p=$(((h-m+l+n+19)%32))
o="$date $x-$n-$p"
echo "Julian Easter is on $o."
else
echo "Invalid Input"
fi
;;
2) echo "Enter your year (1900-2050)"
read x
if [[ $x -ge 1900 && $x -le 2050 ]]; then
A=$((x%4))
B=$((x%7))
C=$((x%19))
D=$(((19*C+15)%30))
E=$(((2*A+4*B-D+34)%7))
M=$(((D+E+114)/31))
day=$(((D+E+115)%31))
o="$date $x-$M-$day"
echo "Gregorian Easter is on $o."
else
echo "Invalid Input"
fi
;;
0) exit
;;
esac
One input here-
I was facing same issue for one of my scripts. I found one blunder I have done.
Always make sure function names should not have - and instead _ should be used.

Bash - syntax error near unexpected token `fi'

#!/usr/bin/env bash
if [[ $# -eq '0' ]]
then
var=command
if [[ ${var} -eq '0' ]]
then
do something
else
do something else
fi
fi
if [[ $# -eq '1' ]]
usage;
fi
if [[ $# -eq '2' ]]
if [[ "$1" != "-r" ]]
then
usage;
fi
if [[ "$2" =~ some_pattern ]]
then
do something
else
echo "Pattern is in an improper format. Please enter it as: correct_pattern, and try again"
exit 1
fi
usage="Usage: meta_script.sh -r correct_pattern
-r for reset is used to manually pass a parameter instead of using the default"
exit 1
fi
When I run this script, this is the error I get:
./meta_script.sh: line 31: syntax error near unexpected token `fi'
./meta_script.sh: line 31: `fi'
In the first if statement where I'm checking if the number of parameters are equal to 1, I had put a then, but I got the same error as above, except with then instead of fi. It's almost as if no matter what I put and where, I get these errors and when I remove them to try and fix it, I get another bunch of similar errors. Please help me correct this script. Thanks!
With regard to the segment:
if [[ $# -eq '2' ]]
if [[ "$1" != "-r" ]]
then
you are missing the then for the first if statement. Putting that in should get you past that error:
if [[ $# -eq 2 ]] ; then
if [[ "$1" != "-r" ]] ; then
You'll see I've put the then on the same line since it's a good habit to get into, realising that if and then always go together (same as while and do). It also allows you to see more lines of "useful" code on any given terminal :-)
I've also gotten rid of the useless quotes around 2 since $# always returns a numeric value. I'd advise sticking to using quotes just for strings.

Resources