在写一个脚本时需要实现掩码位和掩码之间的转换，想简单的通过shell 实现，在openwrt程序上刚好有此脚本，内容如下：

<br />

#!/bin/bash
# code from www.361way.com
{
# Assumes there's no "255." after a non-255 byte in the mask
local x={1##*255.}
set -- 0^^^128^192^224^240^248^252^254^(( ({#1} -{#x})*2 )) {x%%.*}
x={1%%3*}
echo(( 2 + ({#x}/4) ))
}
{
# Number of args to shift, 255..255, first non-255 byte, zeroes
set -- (( 5 - (1 / 8) )) 255 255 255 255 $(( (255 << (8 - ($1 % 8))) & 255 )) 0 0 0
[ $1 -gt 1 ] && shift$1 || shift
echo ${1-0}.${2-0}.${3-0}.${4-0}
}
# examples:
cdr2mask 24
上面的代码看起来比较玄妙，其具体解释可以看下英文解释。

<br />


To get the CIDR prefix from a dot-decimal netmask like this one:

<br />

255.255.192.0
<br />

you first have to convert the four octets to binary and then count the most significant bits (i.e. the number of leading ones):

<br />

11111111.11111111.11000000.00000000 # 18 ones = /18 in CIDR
<br />

This function does that rather creatively. First, we strip off all of the leading&nbsp;255&nbsp;octets (i.e. the octets that are all ones in binary) and store the results in variable&nbsp;x:

<br />

local x=${1##*255.} <br /> This step uses&nbsp;<a href="http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html">parameter expansion</a>, which the entire script relies on pretty heavily. If we continue with our example netmask of&nbsp;255.255.192.0, we now have the following values: <br />  $1: 255.255.192.0 $x: 192.0 <br /> Next we set three variables:&nbsp;1,&nbsp;2, and&nbsp;$3. These are called&nbsp;<a href="http://www.gnu.org/software/bash/manual/bashref.html#Positional-Parameters">positional parameters</a>; they are much like ordinary named variables but are typically set when you pass arguments to a script or function. We can set the values directly using&nbsp;set --, for example:

<br />

set -- foo bar # 1 = foo,2 = bar
<br />

I prefer using named variables over positional parameters since it makes scripts easier to read and debug, but the end result is the same. We set&nbsp;$1&nbsp;to: <br />  0^^^128^192^224^240^248^252^254^ <br /> This is really just a table to convert certain decimal values to binary and count the number of&nbsp;1&nbsp;bits. We'll come back to this later. We set&nbsp;$2&nbsp;to

<br />

$(( (${#1} - ${#x})*2 )) <br /> This looks complex, but it is really just counting the number of&nbsp;1&nbsp;bits we stripped off in the first command. It breaks down to this: <br />  (number of chars in 1 - number of chars inx) * 2 <br /> which in our case works out to <br />  (13 - 5) * 2 = 16 <br /> We stripped off two octets so we get 16. Makes sense. We set&nbsp;$3&nbsp;to:

<br />

${x%%.*} <br /> which is the value of&nbsp;$x&nbsp;with everything after the first&nbsp;.&nbsp;stripped off. In our case, this is&nbsp;192.

We need to convert this number to binary and count the number of&nbsp;1&nbsp;bits in it, so let's go back to our "conversion table." We can divide the table into equal chunks of four characters each:

<br />

0^^^ 128^ 192^ 224^ 240^ 248^ 252^ 254^
<br />

In binary, the above numbers are:

<br />

00000000 10000000 11000000 11100000 11110000 11111000 11111100 11111110
# 0 ones 1 one    2 ones   3 ones   ...
<br />

If we count from the left, each four-character block in the table corresponds to an additional&nbsp;1&nbsp;bit in binary. We're trying to convert&nbsp;192, so let's first lop off the rightmost part of the table, from&nbsp;192&nbsp;on, and store it in&nbsp;x:

<br />

x={1%%3*}
<br />

The value of&nbsp;$x&nbsp;is now <br />  0^^^128^ <br /> which contains two four-character blocks, or two&nbsp;1&nbsp;bits in binary. Now we just need to add up the&nbsp;1&nbsp;bits from our leading&nbsp;255&nbsp;octets (16 total, stored in variable&nbsp;$2) and the&nbsp;1&nbsp;bits from the previous step (2 total):

<br />

echo ((2 + (${#x}/4) )) <br /> where  ${#x}/4
is the number of characters in&nbsp;x&nbsp;divided by four, i.e. the number of four-character blocks in&nbsp;x.


### Output:

<br />

18
<br />


Let's keep running with our previous example, which had a CIDR prefix of&nbsp;18.

We use&nbsp;set --&nbsp;to set positional parameters 1 through9:

<br />

$1:$(( 5 - ($1 / 8) )) # 5 - (18 / 8) = 3 [integer math]$2: 255
$3: 255$4: 255
$5: 255$6: $(( (255 << (8 - ($1 % 8))) & 255 ))  # (255 << (8 - (18 % 8))) & 255 = 192
$7: 0$8: 0
$9: 0 <br /> Let's examine the formulas used to set&nbsp;1&nbsp;and&nbsp;6&nbsp;a little closer.&nbsp;$1&nbsp;is set to:

<br />

$(( 5 - ($1 / 8) ))
<br />

The maximum and minimum possible values for a CIDR prefix are 32 for netmask

<br />

11111111.11111111.11111111.11111111
<br />

<br />

00000000.00000000.00000000.00000000
<br />

The above formula uses integer division, so the possible results range from 1 to 5:

<br />

5 - (32 / 8) = 1
5 - ( 0 / 8) = 5
<br />

$6&nbsp;is set to: <br />  $(( (255 << (8 - ($1 % 8))) & 255 )) <br /> Let's break this down for our example CIDR prefix of&nbsp;18. First we take the modulus and do some subtraction: <br />  8 - (18 % 8) = 6 <br /> Next we bitwise shift 255 by this value: <br />  255 << 6 <br /> This is the same as pushing six&nbsp;0&nbsp;bits onto the end of 255 in binary: <br />  11111111000000 <br /> Finally, we bitwise AND this value with 255: <br />  11111111000000 & 00000011111111 # 255 <br /> which gives <br />  00000011000000 <br /> or simply <br />  11000000 <br /> Look familiar? This is the third octet in our netmask in binary: <br />  11111111.11111111.11000000.00000000 ^------^ <br /> In decimal, the value is 192. Next we shift the positional parameters based on the value of&nbsp;$1:

<br />

[ 1 -gt 1 ] && shift1 || shift
<br />

In our case, the value of&nbsp;1&nbsp;is 3, so we shift the positional parameters 3 to the left. The previous value of&nbsp;4&nbsp;becomes the new value of&nbsp;1, the previous value of&nbsp;5&nbsp;becomes the value of&nbsp;$2, and so on: <br />  $1: 255
$2: 255$3: 192
$4: 0$5: 0
\$6: 0
<br />

These values should look familiar: they are the decimal octets from our netmask (with a couple of extra zeros tacked on at the end). To get the netmask, we simply print out the first four with dots in between them:

<br />

echo {1-0}.{2-0}.{3-0}.{4-0}
<br />

The&nbsp;-0&nbsp;after each parameter says to use&nbsp;0&nbsp;as the default value if the parameter is not set.


### Output:

<br />

255.255.192.0
<br />

<br />

<br />