How to compare variables in bash - linux

I'm begginer in linux console; I want to create if statement with integer variables
if[$x= [$#-2]]
But console receive if can't find this statment if[1 = [5-2]]
Please help me and correct my statement.

You need Arithmetic Expansion: $((expression))
if [ $x = $(($# - 2)) ]; then
# ^ ^ ^ ^ ^ spaces are mandatory

To start $# is the number of parameters passed to the bash script
./bash_script 1 2 3
$# auto-magically populates to 3. I hope you know this already.
#!/bin/bash
x=1
#If you are trying to compare `$x` with the value of the expression `$# - 2` below is how you do it :
if (( $x == $# - 2 ))
then
echo "Some Message"
fi
#If you are trying to check the assignment to `$x was successful below is how you do it :
if (( x = $# - 2 ))
then
echo "Some Message"
fi
The second condition is almost always true, but the first can be false.
Below are the results of my test runs :
#Here the both ifs returned true
sjsam#WorkBox ~/test
$ ./testmath1 1 2 3
Some Message
Some Message
#Here the first if returned false because we passed 4 parameters
sjsam#WorkBox ~/test
$ ./testmath1 1 2 3 4
Some Message

Related

Bash script : $x=$x+2 is not getting recognised

When I am executing the below script, I am getting the following error :-
The script executes infintely and below line is printed everytime.
"line 9: 1=1+2: command not found". Why?
#!/bin/bash
echo "Script 1 - Linux Scripting Book"
x=1
while [ $x -le 45 ]
do
echo x : $x
$x=$x+2
done
echo "End Of Script 1"
exit 0
Also if I change the $x=$x+2 to x+$x+2 then also I am getting the below error.
line 6: [: 1+2: integer expression expected
Same script when executed like this runs fine.
#!/bin/bash
echo "Script 1 - Linux Scripting Book"
x=1
while [ $x -le 45 ]
do
echo x : $x
let x=x+2
done
echo "End Of Script 1"
exit 0
You get line 9: 1=1+2: command not found because 1=1+2 is what $x=$x+2 is expanded into.
Use expr or let or ((...)) for integer calculations and bc for floating point:
let x=x+2
((x=x+2)) #same as above
((x+=2)) #same
((x++)) #if adding just one
((++x)) #if adding just one
x=$((x+2))
x=`expr $x + 2` #space before and after +
x=$(echo $x+2|bc) #using bc
x=$(echo $x+2.1|bc) #bc also works with floating points (numbers with decimals)
Since this part of the question isn't cleared yet, and not fine to post in a comment, I add this partial answer:
x=1; for i in 1 2 3 ; do x=$x+2; echo $x; done
1+2
1+2+2
1+2+2+2
As a side note: Don't use exit 0 at the end of your script without a good reason. When the script is done, it exits by itself without your help. The exit status will be the exit status of the last command performed, in your case a simple echo, which will almost always succeed. In the rare cases it fails, you will probably without intention hide that failure.
If you source the script, the exit will throw you out of your running shell.
But you can rewrite your while loop like this:
x=0
while (($((x)) < 9))
do
echo x : $x
x=$x+2
done
echo $((x))
x : 0
x : 0+2
x : 0+2+2
x : 0+2+2+2
x : 0+2+2+2+2
10
Because that's not the Bourne shell syntax for setting a variable; it looks more like Perl or PHP. The $ is used for parameter expansion and is not part of the variable name. Variable assignment simply uses =, and let evaluates arithmetic expressions (much like $((expression))). Another syntax that should work is x=$((x+2)). Note that these arithmetic evaluations are a bash feature; standard unix shells might require use of external tools such as expr.

calling function in shell script

the user will enter 3 numbers , and I want to calculate these number's roman values. I copy paste to_roman() function but its take 3 arguments . I'm confused . then I call this function in case statement.How can I implement this function for 3 numbers ? any implementation advice ?
to_roman () # Must declare function before first call to it.
{
number=$1
factor=$2
rchar=$3
let "remainder = number - factor"
while [ "$remainder" -ge 0 ]
do
echo -n $rchar
#echo $remainder
let "number -= factor"
let "remainder = number - factor"
done
return $number
}
to_roman $num 10 x
num=$?
to_roman $num 9 ix
num=$?
to_roman $num 5 v
num=$?
to_roman $num 4 iv
num=$?
to_roman $num 1 i
You just read the params in function like $1 $2 $3 and so on.
$# being the total number of parameters
Depending on your implementation you may want to loop over the arguments for n argument support per #EitanReiner suggestion.
It is done with $#. For example:
for param in "$#"
do
echo "$param"
done
In your case it probably would be an overkill since you get 3 parameters only. Anyway, you should check if you got exactly 3 parameters.

Printing IFS values from file

Script:
#!/bin/ksh
FILENAME=$1
while read RECORD VALUE
do
echo ${RECORD} ${VALUE} "X"
done <"$FILENAME"
input file:
A 1
B 2
The output of script:
X1
X2
If I remove from echo "x", e.g.
echo ${RECORD} ${VALUE}
I am getting
A 1
B 2
what is wrong?
Update:
If I do
echo "X" ${RECORD} ${VALUE}
it prints correctly:
X A 1
X B 2
and :
echo ${RECORD} "X"
also prints correctly, so i am guessing the issues is with VALUE that maybe contains return carriage symbol (as input file was created on windows)
adding this inside the loop:
VALUE=`echo $VALUE| tr -d '\r'`
solved the issue, if you have a better solution you are more than welcome.
There is a parameter expansion operator you can use to remove a character from the end of a value, if it is present.
VALUE=${VALUE%$'\r'}
This is handled in-shell, without needing to start a new process.

Require efficient and probably one to two liner solution to replace space

Having following bash scripts to find index of the command from command list
#!/bin/bash
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
cmdlist="${cmdlist//,/ }" #To Replace , with space in array list
cmdlist="${cmdlist//cmd/ }" #To get index of command
echo $cmdlist //Added for clarification
for a in $cmdlist
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
As you can see how I extract command index from command list but it's failing (of course it fails) in some condition. So want make some validations as follows:
1) In list, if argument is cmd then it will skipped and not replace space instead cmd string because argument length must be greater then 3.
2) In list, if argument is cmd0001 then also skipped and not replace space instead cmd string because argument length must be less or equal 5 and greater then 3.
Following above validation I achieved by taking for..loop, taking temporary array then compare each argument and validate then store in temporary array and finally copy temporary array in original one.So this is too long procedure.
Any one have idea for batter solution?
Like
cmdlist="${cmdlist//cmd/ }" #To get index of command
command only replace space instead cmd in target argument if condition [[ length -gt 3 ]] && [[ length -le 5 ]] match.
Note: have already have solution using for..loop.
UPDATE: Added more detail for what i want
I got output this from script
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
but i want this
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : cmd000001
invalid command index : cmdxyz
So basically leave the argument which not in validation range and mark as invalid index(nothing to do not require to replace space in place of cmd string.)
More UPDATE: Again added more detail to clarify exactly what i want
Have added one echo statement before for..loop in my script ( see modified above script ) which give me output like this
1 2 24 25 4 10 9 000001 xyz
but i want
1 2 cmd 24 25 4 10 9 cmd000001 cmdxyz
means leave argument as it is if it violate validation like in my list third argument is cmd.It violate the condition becasue it's length not greater then 3.Now see last two argument in list cmd000001,cmdxyz It violate the condition because it's length greater then 5. Valid argument is one for which length must be greater then 3 && less or equal 5.
Hope this will clarify what i want.
Editing as per your update:
Get the values in an array and check within the loop if those meet the required criteria:
cmdlist="cmd1,cmd2,cmd,cmd24,cmd25,cmd4,cmd10,cmd9,cmd000001,cmdxyz"
IFS=, read -a arr <<< "$cmdlist"
for a in "${arr[#]}"
do
v="${a/cmd/}"
if ((v > 50)) || ((v <= 0))
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $v
fi
done
For your input, it'd produce:
command index : 1
command index : 2
invalid command index : cmd
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : cmdxyz
Old answer:
Instead of attempting to replace , with spaces and so on, read the string delimited by comma into an array. Manipulate the array to get one containing the desired strings.
$ IFS=, read -a arr <<< "$cmdlist"
$ foo=(${arr[#]/cmd/})
$ for i in "${foo[#]}"; do echo $i; done
1
2
24
25
4
10
9
000001
xyz
$
Checking...
for a in "${foo[#]}"
do
if [[ $a == *[^[:digit:]]* ]] || [[ $a -gt 50 ]]
then
printf "invalid command index : %s\n" $a
else
printf "command index : %s\n" $a
fi
done
produces:
command index : 1
command index : 2
command index : 24
command index : 25
command index : 4
command index : 10
command index : 9
command index : 000001
invalid command index : xyz
A note of caution: Numbers with leading zeros (as you have in your example) would be considered as octal and might produce unexpected results:
$ [[ 0024 -gt 22 ]] && echo greater || echo smaller
smaller

Problem with bash code

function dec_to_bin {
if [ $# != 2 ]
then
return -1
else
declare -a ARRAY[30]
declare -i INDEX=0
declare -i TEMP=$2
declare -i TEMP2=0
while [ $TEMP -gt 0 ]
do
TEMP2="$TEMP%2"
#printf "%d" "$TEMP2"
ARRAY[$INDEX]=$TEMP2
TEMP=$TEMP/2
INDEX=$[ $INDEX + 1 ] #note
done
for (( COUNT=INDEX; COUNT>-1; COUNT--)){
printf "%d" "${ARRAY[$COUNT]}" <<LINE 27
#echo -n ${ARRAY[$COUNT]} <<LINE 28
}
fi
}
why is this code giving this error
q5.sh: line 27: ARRAY[$COUNT]: unbound variable
same error comes with line 28 if uncommented
One more question, I am confused with the difference b/w '' and "" used in bash scripting any link to some nice article will be helpfull.
It works fine for me except that you can't do return -1. The usual error value is 1.
The error message is because you have set -u and you're starting your for loop at INDEX instead of INDEX-1 (${ARRAY[INDEX]} will always be empty because of the way your while loop is written). Since you're using %d in your printf statement, empty variables will print as "0" (if set -u is not in effect).
Also, it's meaningless to declare an array with a size. Arrays in Bash are completely dynamic.
I would code the for loop with a test for 0 (because the -1 looks confusing since it can't be the index of an numerically indexed array):
for (( COUNT=INDEX - 1; COUNT>=0; COUNT--))
This form is deprecated:
INDEX=$[ $INDEX + 1 ]
Use this instead:
INDEX=$(( $INDEX + 1 ))
or this:
((INDEX++))
I also recommend using lower case or mixed case variables as a habit to reduce the chance of variable name collision with shell variables.
You're not using $1 for anything.

Resources