Let's say I have two bash variables that contain binary values:

a=0011 # decimal 3
b=1000 # decimal 8

Is there a way I can loop through all the possible values between $a and $b keeping it binary? Something like:

for blah in $(seq $a $b) ; do
    print "Blah is: $blah"

So it will output:

Blah is: 0011
Blah is: 0100
Blah is: 0101
Blah is: 0110
Blah is: 0111
Blah is: 1000

I have tried:

for blah in $(seq "$((2#$a))" "$((2#$b))") ; do

But then $blah becomes decimal, and I'd like to keep it a binary (I can always transform the decimal back to binary, but that seems a waste, since I alredy have the extremes in binary)

This code must run in a limited linux (OpenWRT) that doesn't have obase available. If the answer is that it's not possible to keep the binary value, that's a useful answer as well (I can create a function that converts decimal to binary without using obase) Besides, it can be a useful answer to people using regular bash.

  • Do you have the dc command?
    – jthill
    Nov 22, 2013 at 4:07
  • @jthill, I'm afraid I don't, either :-(
    – Savir
    Nov 22, 2013 at 4:10
  • Do you really have bash? Which version?
    – rici
    Nov 22, 2013 at 5:13
  • 4.2.28(1)-release (mips-openwrt-linux-gnu)
    – Savir
    Nov 22, 2013 at 6:28

4 Answers 4


You're close with this:

for blah in $(seq "$((2#$a))" "$((2#$b))") ; do

You just need to convert the decimal values from the for loop back into binary with something like dc.


$ for blah in $(seq "$((2#0011))" "$((2#1000))"); do \
     printf "%04d\n" $(echo "obase=2;$blah" | bc);done

The printf is being used to control the output so that it's padded with leading zeros, and formatted to a certain width. The arguments, %04d is what specifies the output.

The other key pieced to this command is the use of bc - a command line calculator. This command for example:

echo "obase=2;$blah" | bc

Is taking the value, $blah and converting it to base 2 (aka. Binary) through the use of bc.

Without bc or dc

If you're on a system that's limited such that neither of these tools are present you can do the conversion directly using just awk and this function from awk's manual.


Create a file with the following content, call it dec2bin.awk.

# bits2str --- turn a byte into readable 1's and 0's

function bits2str(bits, data, mask)
    if (bits == 0)
        return "0"

    mask = 1
    for (; bits != 0; bits = rshift(bits, 1))
        data = (and(bits, mask) ? "1" : "0") data

    while ((length(data) % 8) != 0)
        data = "0" data

    return data

  printf("%s\n", bits2str($1))

Now to use the above function:

$ for blah in $(seq "$((2#0011))" "$((2#1000))"); do echo $blah \
     | awk -f dec2bin.awk; done

seq is not a built-in. It's also not part of the Posix standard. But the usual implementations of seq don't have any ability to sequence in bases other than 10.

In bash, you can specify a range as {start..finish}. However, that also doesn't work in bases other than 10 (although it does work with letters: {a..f} expands to a b c d e f.

And as far as I know, that's it for simple sequence generators, which leaves you with a couple of possibilities.

The silly way to do it is to filter out the non-binary values. That's simple but hugely inefficient if a and b aren't tiny:

for x in $(seq -w $a $b); do
  if [[ ! ($x =~ [2-9]) ]]; then
    echo $x

Here's a better solution. Assuming a and b are the same length (if not, you can use printf to fix that), the following will loop through all binary numbers from a to b, inclusive:

# We need a string of 0s at least as long as a:
while [[ ! ($a > $b) ]]; do
  # do something with $a
  # The following "increments" a by removing the last 0 (and trailing 1s)
  # and replacing that with a 1 and the same number of 0s.
  a=$(printf "%.*s" ${#a} ${a%0*}1$z)
  • Clever second trick!
    – Savir
    Nov 22, 2013 at 14:38

It's easier with zsh:

for ((i=2#$a; i<=2#$b; i++)) echo $(([##2]i))

Or with 0-padding:

for ((i=2#$a; i<=2#$b; i++)) printf '%04d\n' $(([##2]i))

Otherwise you can use bc:

echo "ibase=obase=2; for (i=$a; i<=$b; i++) i" | bc

Or dc:

echo "2doi $a [p1+d$b!<a]dsax" | dc

To 0-pad, you can always pipe the output to:

sed 's/^/000/;s/^0*\(.\{4\}\)/\1/'

This method will do a lot of processing, so not good for large ranges.

seq -f '%04g' 0011 1000 | grep -v '[2-9]'

  • @Stephane the quotes you added and the ones your removed are both optional, in this case. I would recommend always having them. Nov 22, 2013 at 10:42
  • 1
    No, the quotes around [2-9] are not optional. It will cause it to fail with csh, tcsh or zsh if there's no file matching that pattern in the current directory, or worse with other shells (not fish), it will fail badly and more obscurely if there are files matching that pattern in the current directory. You're right that quotes around % are necessary for the fish shell. single quotes are better than double quotes as in your earlier version since that means it also works with rc and es. As it is written now, it would work with any shell (on systems where GNU seq is installed). Nov 22, 2013 at 11:01
  • @stephane Sorry I missed those cases where it did not work, this is why I recommend using quotes where ever they may be needed. Nov 22, 2013 at 11:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .