What will be the output of the following unix code? - linux

a=x b=x
If [ $a -eq $b ]
Then
echo "a is equal to b"
else
echo "a is not equal to b"
fi
Will the above code successfully output "a is equal to b" or there is some error?

When looking at your script, you use the single bracket version of the test command. This is in contrast to the double-bracket version which is a shell-internal test.
The test command states:
INTEGER1 -eq INTEGER2 : INTEGER1 is equal to INTEGER2
But you are not comparing integers, but strings (unless x could be a dummy name for something that could be an integer.). Hence, the test will fail.
$ [ x -eq x ]
[: integer expression expected: x
So your output will read a is not equal to b.
If you want to compare strings, then it is advised to use the operator =:
STRING1 = STRING2 : the strings are equal

-eq is for integer comparisons, but you are comparing strings. Use = (or bash-ism ==):
[ "$a" = "$b" ]
Like i did, quote variable expansions to prevent word splitting and pathname expansion.

Related

Bash length of string starting with "#"

I am writing a script that reads input of color hex-code from user, i.e
#ffffff
I also need to check length of that string to be equal to 7 (1 hash symbol + 6 digits) and have "#" symbol as 1st character. Here's my code:
read color
until [ ${#color} -ge 7 ] && [ ${color:0:1} -eq "#" ]
do
echo "Color code must be 7 characters long and start with '#'!"
read color
done
But when I try to, I get an error
[: #: integer expression expected
It works perfect if string is not starting with hash.
Is there a way to make it work?
You are trying to do a numeric comparison -eq, instead of a string comparison:
${color:0:1} -eq "#"
should be
"${color:0:1}" = "#"
instead.
INTEGER1 -eq INTEGER2 - INTEGER1 is equal to INTEGER2
STRING1 = STRING2 - the strings are equal
(man test)
If you only need this to work in bash, use regular expression matching:
color=
until [[ $color =~ ^\#[[:xdigit:]]{6}$ ]]; do
IFS= read -r color
done
For any POSIX shell, use a case statement to allow pattern matching
color=
until :; do
IFS= read -r color
case $color in
\#[[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]][[:xdigit:]]) break ;;
esac
done
or use the expr command:
color=
until IFS= read -r color && expr "$color" : "#[[:xdigit:]]\{6\}$"; do
:
done
Bash scripts use = for string comparison, so your code should be:
read color
until [ ${#color} -ge 7 ] && [ ${color:0:1} = "#" ]
do
echo "Color code must be 7 characters long and start with '#'!"
read color
done
It's saying integer expression expected because -eq is for integer comparisons

What is "-le" in shell script?

I am going through this code. I would like to know what is meant by
-le in the following code segment.
if [ $stage -le 2 ]; then
In one of the questions it says that -le stands for <= of strings, but that is in Perl. Is it the same here as well?
Further, I would like to know if that $stage variable automatically gets updated. It has been initialized to 0 at the beginning, but later, how does that get incremented?
As is stated in the documentation:
integer comparison
(...)
-lt: is less than
if [ "$a" -lt "$b" ]
So it interprets the values of $a and $b (in your case $stage and 2) as integers and performs a comparison. If the first element is less than or equal to the second, the test succeeds and the then part will be executed.
As the documentation later states, one can use <= as well:
<=: is less than or equal to (within double parentheses)
(("$a" <= "$b"))
But then one uses double parentheses (as specified in the documentation).
-le is less than or equals to :
if [ $stage -le 2 ];
is same as:
stage <= 2

Test if string is "*"

For some reason I just cannot get an if statement to test if a string is literally equal to an asterisk. I have tried every combination I can think of and I don't want to mess with file globbing. Please help.
if [ $VAR = "\*" ]; then
* UPDATE *
Both of those suggestions work. The issue is apparently not with the * comparison, but with the other part of the if statement. This is supposed to compare whether or not $VAR is between 0 and 20 or is a wildcard.
if [ "$VAR" -gt 0 ] && [ "$VAR" -lt 20 ] || [ "$VAR" = "*" ]; then
This other part of the IF statement if apparently goofing up the last comparison.
* UPDATE *
Just tested it again and checked my syntax. When $VAR is between 0 and 20 it works great (true), when $VAR is over 20 it also works (reports false), however as soon as I try to set $VAR to an * the if statement freaks and pops out:
line 340: [: *: integer expression expected
Another version using bash's double brackets:
if [[ $VAR = "*" || ($VAR -gt 0 && $VAR -lt 20) ]]; then
The double brackets allow you to use && and ||. Also, bash doesn't perform word splitting or glob expansion on arguments to [[, so $VAR doesn't need to be quoted and ( doesn't need to be escaped.
[[ also works in zsh and ksh, if you need (some) portability.
$ VAR="*"
$ if [ "$VAR" = "*" ] ; then echo Star ; fi
Star
Quote variables when they could contain glob patterns, whitespace or other interpretable sequences you don't want interpreted. This also avoids syntax errors if $VAR is empty.
For your second problem, [ "$VAR" -gt 0 ] doesn't make sense if $VAR is anything but a number. So you must avoid having that test evaluated in that case. Simply exchange your tests - || and && are short-circuiting (in bash at least, not sure if that's POSIX):
if [ "$VAR" = "*" ] || [ "$VAR" -gt 0 -a "$VAR" -lt 20 ] ; then

grabbing all arguments after nth argument and concatenating them together in bash

So I have a bash script that needs to take an arbitrary number of command line arguments and put them into a single string
Example of what the user would type in:
give <environment> <email> <any number of integers separated by spaces>
give testing stuff#things.com 1 2 3 4 5
I want to get all of the arguments from $3 to $# and concat them into a string.
My (probably awful) solution right now is
if [ $# -gt 3 ]
then
env="env="$1
email="email="$2
entList=""
for i in {3..$#}
do
if [ $i -eq 3 ]
then
entList=$3
shift
fi;
if [ $i -gt 3 ]
then
entList=$entList","$3
shift
fi;
done
fi;
I handle the case of having only three arguments a bit differently, and that one works fine.
Final value of $entList given the example give testing stuff#things.com 1 2 3 4 5 should be: 1,2,3,4,5
Right now when i run this i get the following Errors:
/usr/local/bin/ngive.sh: line 29: [: {3..5}: integer expression expected
/usr/local/bin/ngive.sh: line 34: [: {3..5}: integer expression expected
Lines 29 and 34 are:
line 29: if [ $i -eq 3 ]
line 34: if [ $i -gt 3 ]
Any help would be appreciated.
You're on the right track. Here's my suggestion:
if [ $# -ge 3 ]; then
env="$1"
email="$2"
entlist="$3"
while shift && [ -n "$3" ]; do
entlist="${entlist},$3"
done
echo "entlist=$entlist"
else
echo "Arguments: $*"
fi
Note that variables should always be put inside quotes. I'm not sure why you were setting env=env=$1, but I suspect that if you want to recycle that value later, you should do it programatically rather than by evaluating the variable as if it were a statement, in case that was your plan.
Skip first three arguments using a subarray:
all=( ${#} )
IFS=','
threeplus="${all[*]:3}"
The reason you're getting those error messages is that in:
for i in {3..$#}
The brace expansion is performed before the parameter expansion and so the following if statement is evaluated as:
if [ {3..$#} -eq 3 ]
which isn't valid.
Change your for statement to use the C style:
for ((i = 3; i <= $#; i++))
Use this style for integer comparison:
if (( $# > 3 ))
and
if (( i == 3 ))
and
if (( i > 3 ))
Put your parameters inside the quotes:
env="env=$1"
email="email=$2"
and
entList="$entList,$3"
although the quotes aren't necessary since word splitting isn't performed on the right side of an assignment and you're not assigning special characters such as whitespace, semicolons, pipes, etc.

Linux shell programming string compare syntax

What is the difference between = and == to compare strings in Linux shell programming?
Maybe the following code works:
if [ "$NAME" = "user" ]
then
echo "your name is user"
fi
But I think it's not a correct syntax. It would be used to compare string by == statement.
What is correct?
The single equal is correct
string1 == string2
string1 = string2
True if the strings are equal. ‘=’ should be used with the test command for POSIX conformance
NAME="rafael"
USER="rafael"
if [ "$NAME" = "$USER" ]; then
echo "Hello"
fi
In general, the = operator works the same as == when comparing strings.
Note:
The == comparison operator behaves differently within a double-brackets test than within single brackets.
[[ $a == z* ]] # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
[ $a == z* ] # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).
source: http://tldp.org/LDP/abs/html/comparison-ops.html
These pages explain the various comparison operators in bash:
http://www.tech-recipes.com/rx/209/bournebash-shell-scripts-string-comparison/
http://tldp.org/LDP/abs/html/comparison-ops.html
http://www.faqs.org/docs/Linux-HOWTO/Bash-Prog-Intro-HOWTO.html#ss11.2
On the second linked page, you will find:
==
is equal to
if [ "$a" == "$b" ]
This is a synonym for =.
you can take a look here or here. Personally, to compare strings, I use case
case "$string1" in
"$string2" ) echo "matched";;
*) echo "not matched";;
esac
I do not have to know which operator i should use

Resources