accepting the default value using a while loop - bash

I am trying to make this piece of code accept either user given 4 value or a "/" to accept the default value.
#!/bin/bash
$komp=1
while [ ${#nbh[#]} -ne `expr 4 \* $komp` || ${nbh[0]} -ne "/" ]
do
echo "Enter 4 element or / to accept default"
read -a nbh
if [ ${#nbh[#]} -ne `expr 4 \* $komp` || ${nbh[0]} -ne "/" ]
then
echo "Wrong!!"
else
if [ nbh -eq "/" ]
then
declare -a nbh=('1' '0' '1' '1')
fi
fi
done
In present situation, it is giving error:
./mini.sh: line 3: [: missing `]'
./mini.sh: line 3: -ne: command not found
Kindly help.

The fundamental problem is that you can't do a boolean expression that way.
while [ expr1 || expr2 ] # WRONG
does not work because the shell parses it as two commands: [ expr (the command [ with one argument "expr1") and expr2 ] (the command expr2 with one argument "]"). This is an error, because the [ command requires that its last argument be the literal string ]. It is easier to understand the syntax if you use the test command instead of [, but either of the following will work:
while test expr1 || test expr2
while [ expr1 ] || [ expr2 ]
while [ expr1 -o expr2 ]
while test expr1 -o expr2

#!/bin/bash
komp=1
default=(1 0 1 1)
prompt="Enter 4 elements or / to accept default: "
valid_input=0
while ((!valid_input)) && read -a nbh -p "$prompt"; do
if ((${#nbh[#]}==1)) && [[ ${nbh[0]} = / ]]; then
for ((i=0,nbh=();i<komp;++i)); do nbh+=( "${default[#]}" ); done
fi
if ((${#nbh[#]} != 4*komp)); then
echo "Wrong!!"
continue
fi
valid_input=1
done
if ((!valid_input)); then
echo >&2 "There was an error reading your input. Do you have a banana jammed in your keyboard?"
exit 1
fi
# Here, all is good
Now you'll also want to check that user's inputs are valid numbers.
Please read (and understand) the answer I provided to your other question. (if you actually had done so, you wouldn't have asked this question).

Related

Why is my arithmetic condition evaluated as true?

I have a sample logic. By calculation, it is -2 > 0 . This is a false condition and I get no result, but I am getting the value as abcd.
#!/bin/bash
Val1=1
Val2=1
Val3=2
Name=abcd
log=/tmp/log
if [ "$((Val1-(Val2 + Val3))) -ge 0" ] ; then echo "$Name" | tee -a "$log"; fi
I have verified on ShellCheck, and it is giving me this message. I couldn't get how to fix this?
$ shellcheck myscript
Line 7:
if [ "$((Val1-(Val2 + Val3))) -ge 0" ] ; then echo "$Name" | tee -a "$log"; fi
^-- SC2157: Argument to implicit -n is always true due to literal strings.
If you use [ 'a string' ], that's the same as [ -n 'a string' ]: it checks if the parameter is non-empty. You're providing the argument "$((Val1-(Val2 + Va3))) -ge 0", which evaluates to the string "-2 -ge 0". "Implicit -n" means that the shell really checks
if [ -n "-2 -ge 0" ]
and that is always true, because that's not an empty string.
The reason is your quoting. This would work instead:
if [ "$((Val1-(Val2 + Val3)))" -ge 0 ]
Strictly speaking, the quotes aren't necessary, but they don't hurt, either.
Or, since you're using Bash, you could use an arithmetic conditional:
if ((Val1 - (Val2 + Val3) > 0))
I figured after posting the question
#!/bin/bash
Val1=1
Val2=1
Val3=2
Name=abcd
log=/tmp/log
if [ "$((Val1-(Val2 + Val3)))" -ge 0 ] ; then echo "$Name" | tee -a "$log"; fi

bash [: too many arguments greater than symbol

This isn't really a question (though I have one at the end), but rather a solution to a problem that I wanted to share in case it helps someone else.
For the longest time I had been getting bash: [: too many arguments when opening a new terminal (specifically iTerm2 on OS X with the bash-completion macport installed). This error originated from the line if [ -n "$BASH_VERSION" -a -n "$PS1" -a -z "$BASH_COMPLETION_COMPAT_DIR" ]; then in the file /opt/local/etc/bash_completion. I have finally tracked down the problem to the fact that I had export PS1='>' in my .bash_profile. Changing PS1 to something else (e.g. '> ') fixes the problem with bash completion.
Some experimenting in OS X and Debian reveals that this problem occurs when adding extra expressions (with -a or -o) into a test ([ ]) after the expression involving '>'. E.g.,
> A='>'; if [ -n "$A" ]; then echo "yes"; fi
yes
> A='>'; if [ -n "$A" -a -n "$A" ]; then echo "yes"; fi
bash: [: too many arguments
> A='> '; if [ -n "$A" -o -n "$A" ]; then echo "yes"; fi
yes
> A='>'; if [ -n "$A" -o -n "Hello" ]; then echo "yes"; fi
bash: [: too many arguments
> A='>'; if [ -n "Hello" -a -n "$A" ]; then echo "yes"; fi
yes
Is this a (known) bug in bash?
Your workaround is effective, as long as the string stored in $A is not an operator that [ / test recognizes - simply adding a space is sufficient, as you've discovered.
Surely the "greater than" should be interpreted as just a string? It works with '> ' after all.
No, the content of $A is not interpreted as just a string. (If you wanted that, you'd have to use [[ instead, which is parsed in a special context, more like you'd expect from traditional programming languages.)
[ (test) is a builtin (also exists as an external utility on most systems) and is therefore parsed with command syntax, which means:
the shell performs its expansions first - $A references are replaced with the content of the variable in this case.
the result is then passed to [
Thus, from the perspective of [, it doesn't matter whether or not the operator it ultimately sees - > in your example - came from a literal or was stored in a variable.
But note that whitespace matters: passing > (no spaces) is interpreted as an operator; >, by contrast, ><space> is not - because that exact literal is more than just the operator.
The bottom line is:
The bash-completion script you're using is not robust.
As #chepner states in a comment on the question, POSIX recommends not using -o / -a to avoid the ambiguity you encountered (emphasis mine):
The XSI extensions specifying the -a and -o binary primaries and the '(' and ')' operators have been marked obsolescent. (Many expressions using them are ambiguously defined by the grammar depending on the specific expressions being evaluated.)
Specifically, using separate [ ... ] expressions joined with && (instead of -a) and || (instead of -o) solves the problem:
[ -n "$BASH_VERSION" ] && [ -n "$PS1" ] && [ -z "$BASH_COMPLETION_COMPAT_DIR" ]
Or, more simply, taking advantage of a non-empty string evaluating to true:
[ "$BASH_VERSION" ] && [ "$PS1" ] && [ -z "$BASH_COMPLETION_COMPAT_DIR" ]
Note that while -a and -o introduce ambiguities, they are not a security concern - you cannot inject arbitrary code through their use.
If you want to use two or more condition you should use
if [ condition1 ] && [condition2 ]
or
if [ condition1 ] || [condition2 ]
so in your case (first if "and"):
A='>'; if [ -n "$A" ] && [ -n "$A" ]; then echo "yes"; fi
for the "or" if:
A='>'; if [ -n "$A" ] || [ -n "Hello" ]; then echo "yes"; fi
But be aware that that the second check [ -n "Hello" ] is always true, so it's better to remove it.
You may be interested in shellcheck to validate your bash script syntax.

"[:too many arguments" error in BASH

I'm learning BASH through HackerRank.There's an exercise in which the lengths of the triangle is given and then you need to find whether the triangle is isosceles,scalene or equilateral.I wrote the following code:
read a
read b
read c
if [ [ "$a" -eq "$b" ] && [ "$b" -eq "$c" ] ]
then
echo "EQUILATERAL"
elif [ [ "$a" -eq "$b" ] || [ "$b" -eq "$c" ] ]
then
echo "ISOSCELES"
else
echo "SCALENE"
fi
But then I get the following error
solution.sh: line 4: [: too many arguments
solution.sh: line 7: [: too many arguments
solution.sh: line 7: [: too many arguments
Why is this happening? I tried long and hard to rectify it but nothing worked out
You can combine conditions either ommiting the surrounding brackets like this
if [ "$a" -eq "$b" ] && [ "$b" -eq "$c" ]
or by combining the conditions with -a/-o like this
if [ "$a" -eq "$b" -a "$b" -eq "$c" ]
see http://wiki.bash-hackers.org/commands/classictest#and_and_or
&& and || are Bash list operators. In a chain of commands, the next command is executed only if the previous command returned 0 (&&) or nonzero (||).
[ is an alias for the Bash internal test command and has arguments such as -eq or -ne. ] ends its command line. Type help test for more information.
So if you write a conditional expression, you do not put the list operators inside brackets.
Try, for example, this instead of the respective line in your code:
if [ "$a" -eq "$b" ] && [ "$b" -eq "$c" ]
then
[ isn't a grouping operator in bash, you can't use it to group tests.
there are a number of different ways to express the tests you want to make, numeric evaluation mode is probably easiest to read
if (( a == b && b == c ))
if (( a == b || b == c || c == a ))
This is going to break if you have decimal fractions, but will work fine for integers.
[ is a conditional command, like an alias for sh's test built-in command.
[[ is the same for bash which has more test options.
So make a choice between [ and [[ but not [ [ which means two command.
Example:
# [ [ -n 'test' ] ]
bash: [: too many arguments
# [ -n 'test' ] && echo $?
0
# [[ -n 'test' ]] && echo $?
0

Multiple `if` statements in bash script

I'm trying to write a short bash script that optionally accepts arguments from the command line, or prompts for their input
if [ [ -z "$message" ] && [ -z "$predefined" ] ] ; then
read -p "Enter message [$defaultMessage]: " message
message=${message:-$defaultMessage}
else
if [ -n "$predefined" ]; then
if [ -f $base/$environment/vle/data/$predefined.txt ]; then
echo Predefined message file $predefined.txt does not exist
exit 1
fi
fi
fi
If neither message nor predefined has been passed in as command line arguments, then the code should prompt for a value for message; otherwise if predefined has been passed in as a command line argument, then the script should test for the existence of a file of that name and only continue if the file does exist
But I'm getting the following error
[: -z: binary operator expected
at the first of those if tests
Any help in explaining what's wrong with my syntax for that first if statement? Or providing an alternative syntax to achieve the same objectives.
The first if is not well-formed. This would work:
if [ -z "$message" ] && [ -z "$predefined" ]; then
or this:
if test -z "$message" && test -z "$predefined"; then
or this bash-specific, easy but dirty way:
if [[ -z "$message" ]] && [[ -z "$predefined" ]]; then
or this bash-specific proper way:
if [[ -z $message && -z $predefined ]]; then
In this last version the double-quotes are unnecessary, not a typo.
Thanks #mklement0 for the corrections in the bash-specific style, and for this final comment:
I should note that there's one case where double-quoting is still a must inside [[ ... ]], namely if you want a variable reference on the right side of a string comparison (==) to be treated as a literal:
v='[a]'
[[ $v == $v ]] # FALSE!
[[ $v == "$v" ]] # true
Without double-quoting, the right-hand side is interpreted as a pattern. Some people advocate always double-quoting variable references so as not to have to remember such subtleties. That said (from bash 3.2 on), you must NOT double-quote the right operand when regex matching with =~
test expression1 -a expression2
is true if both expressions are true.
test expression1 -o expression2
is true if either or both expressions are true.
if [ -z "$message" -a -z "$predefined" ]; then
read -p "Enter message [$defaultMessage]: " message
message=${message:-$defaultMessage}
else
if [ -n "$predefined" -a -f $base/$environment/vle/data/$predefined.txt ]; then
echo Predefined message file $predefined.txt does not exist
exit 1
fi
fi
This was able to combine 4 test into 2 while also getting rid of one nested if expression; then ; fi

bash shell script syntax error [duplicate]

This question already has answers here:
Why should there be spaces around '[' and ']' in Bash?
(5 answers)
Closed 6 years ago.
I wrote a function in bash script. However, it's complaining about syntax. I really can't see what is it..... the error message is [: missing `]'
addem() {
if [ $# -eq 0] || [ $# -gt 2 ]
then
echo -1
elif [ $# -eq 1 ]
then
echo $[ $1 + $1 ]
else
echo $[ $1 + $2 ]
fi
}
You need a space before the first ]. That is:
change:
if [ $# -eq 0] || [ $# -gt 2 ]
to:
if [ $# -eq 0 ] || [ $# -gt 2 ]
Try:
if [ $# -eq 0 ] || [ $# -gt 2 ]
(There was no space between 0 and ].)
indyK1ng: The "#" is not treated as a comment, since the "$" escapes the next character. The "$#" is an internal variable representing the number of positional parameters that exist at the current context. This can be thought of as the number of command line arguments to the shell script, but that array can be reset using the "set -- [args]" built in.
Joakim Elofsson: The overall structure of the if statement is correct, the ";" is only required before the "then" and before the "fi" if those are not listed on a separate line.
The problem is the space between the "0" and the bracket. Bash requires that brackets used to delimit conditional expressions be set off with at least a single space from the expression.
if [ $# -eq 0] || [ $# -gt 2 ] # Wrong
if [ $# -eq 0 ] || [ $# -gt 2 ] # Correct
On an additional note, the two conditional expressions can be combined. The operator association will ensure that everything works out.
if [ $# -eq 0 -a $# -gt 2 ] # Even Better
I tend to prefer the expanded features offered with double brackets for expression evaluation. Note that the combination of the two evaluations is done with a different operator. I find this to be more readable.
if [[ $# -eq 0 || $# -gt 2 ]] # My preference
Later in the script, the use of single brackets for integer addition is not recommended. The single brackets are evaluating an expression to a boolean. Double parens are used for integer math.
echo $[ $1 + $1 ] # Evaluation of an expression
echo $(( $1 + $1 )) # Integer math
Bash is sensitive to spaces. In your first line, replace if [ Y -eq X] with [ Y -eq X ] (space before the "]")
I would use extended test constructs (BASH) as demonstrated bellow. I think it would reduce the no of characters and increase readability (at least for programmers). :-)
addem() {
if (( $# == 0 || $# > 2 ))
then
echo -1
elif (( $# == 1 ))
then
echo (( $1 + $1 ))
else
echo (( $1 + $2 ))
fi
}
You should avoid brackets and use test instead:
if test $# -eq 0 || test $# -gt 2
then
echo -1
elif test $# -eq 1
then
echo $(( $1 + $1 ))
else
echo $(( $1 + $2 ))
fi
Getting a better shell style will make you much better. :)

Resources