2

I was trying to make a small script that calculates a binary number to decimal. It works like this:

-Gets '1' or '0' digits as SEPARATE parameters. e.g. "./bin2dec 1 0 0 0 1 1 1".

-For each parameter digit: if it is '1', multiplies it with the corresponding power of 2 (in the above case, the most left '1' will be 64), then adds it in a 'sum' variable.

Here's the code (it is wrong in the noted point):

    #!/bin/bash

    p=$((2**($#-1)))  #Finds the power of two for the first parameter.
    sum=0  #The sum variable, to be used for adding the powers of two in it.


    for (( i=1; i<=$#; i++ ))  #Counts all the way from 1, to the total number of parameters.
    do

            if [ $i -eq 1 ]  # *THIS IS THE WRONG POINT* If parameter content equals '1'... 
            then
                    sum=$(($sum+$p))  #...add the current power of two in 'sum'.
            fi

    p=$(($p/2))  #Divides the power with 2, so to be used on the next parameter.
    done


    echo $sum  #When finished show the 'sum' content, which is supposed to be the decimal equivalent.

My question is in the noted point (line #10, including blank lines). There, I’m trying to check if EACH parameter's content equals 1. How can I do this using a variable?

For example, $1 is the first parameter, $2 is the 2nd and so on. I want it to be like $i, where 'i' is the variable that is increased by one each time so that it matches the next parameter.

Among other things, I tried this: '$(echo "$"$i)' but didn't work.

I know my question is complicated and I tried hard to make it as clear as I could. Any help?

3
  • Just a side note: bash can make the conversion like this --> $ printf '%d\n' "$((2#1000111))" which returns 71. Commented Jul 6, 2013 at 15:03
  • @fedorqui or just echo $((2#1000111)) Commented Jul 6, 2013 at 15:05
  • Ikr, I just wanted a script which uses that way I did above. And to find out if and how can we refer to a parameter using a variable. Commented Jul 6, 2013 at 15:11

4 Answers 4

2

How about this?

#!/usr/bin/bash

res=0

while [[ $# -gt 0 ]]
do
    res=$(($res * 2 + $1))
    shift
done

echo $res

$ ./bin2dec 1 0 0 1
9

shift is a command that moves every parameter passed to the script to the left, decreasing it's value by 1, so that the value of $2 is now at $1 after a shift. You can actually a number with shift as well, to indicate how far to shift the variables, so shift is like shift 1, and shift 2 would give $1 the value that used to be at $3.

Sign up to request clarification or add additional context in comments.

2 Comments

shift is a command that moves every parameter passed to the script to the left, decreasing it's value by 1, so that the value of $2 is now at $1 after a shift.
This Shift command is very interesting indeed, as well as the way you use to calculate the decimal. Although this still didn't answer my original question.
1

How about this one?

#!/bin/bash

p=$((2**$#))
while(($#)); do
   ((sum+=$1*(p/=2)))
   shift
done
echo "$sum"

or this one (that goes in the other direction):

#!/bin/bash

while(($#)); do
    ((sum=2*sum+$1))
    shift
done
echo "$sum"

Note that there are no error checkings.

Please consider fedorqui's comment: use echo $((2# binary number )) to convert from binary to decimal.

Also note that this will overflow easily if you give too many arguments.


If you want something that doesn't overflow, consider using dc:

dc <<< "2i101010p"

(setting input radix to 2 with 2i and putting 101010 on the stack and printing it).

Or if you like bc better:

bc <<< "ibase=2;101010"

Note that these need the binary number to be entered as one argument, and not as you wanted with all digits separated. If you really need all digits to be separated, you could also use these funny methods:

Pure Bash.

#!/bin/bash

echo $((2#$(printf '%s' "$@")))

With dc.

#!/bin/bash

dc <<< "2i$(printf '%s' "$@")p"

With bc.

#!/bin/bash

bc <<< "ibase=2;$(printf '%s' "$@")"

Abusing IFS, with dc.

#!/bin/bash

( IFS=; echo "2i$*p" ) | dc

Abusing IFS, with bc.

#!/bin/bash

( IFS=; echo "ibase=2;$*" ) | bc

So we still haven't answered your question (the one in your comment of the OP): And to find out if and how can we refer to a parameter using a variable. It's done in Bash with indirect expansion. You can read about it in the Shell Parameter Expansion section of the Bash reference manual. The best thing is to show you in a terminal:

$ a="hello"
$ b=a
$ echo "$b"
a
$ echo "${!b}"
hello

neat, eh? It's similar with positional parameters:

$ set param1 param2 param3
$ echo "$1, $2, $3"
param1, param2, param3
$ i=2
$ echo "$i"
2
$ echo "${!i}"
param2

Hope this helps!

1 Comment

Thanks for this analytic and helpful reply. It surely took much time and effort!
1

To reference variables indirectly by name, use ${!name}:

i=2
echo "${!i} is now equivalent to $2"

Comments

0

Try this one also:

#!/bin/bash - 
( IFS=''; echo $((2#$*)) )

2 Comments

This will set IFS globally. Can be dangerous. See my answer for safer alternatives!
Changed after gniourf_gniourf's comment.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.