Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2315315
  • 博文数量: 527
  • 博客积分: 10343
  • 博客等级: 上将
  • 技术积分: 5565
  • 用 户 组: 普通用户
  • 注册时间: 2005-07-26 23:05
文章分类

全部博文(527)

文章存档

2014年(4)

2012年(13)

2011年(19)

2010年(91)

2009年(136)

2008年(142)

2007年(80)

2006年(29)

2005年(13)

我的朋友

分类: LINUX

2010-02-24 16:20:41

需求: 在C程序中使用了arping命令, 需要一个 broadcast address参数, 以下C代码实现了从IP 地址和netmask生成相应的广播地址.

但现在需要一个workaround的办法, 在脚本中使用该命令, 如何从bash脚本中做这件事?

对应的C函数是:
static const char * get_broadcast_addr(char * ip_str, char * netmask_str)
{
    struct in_addr addr;
    struct in_addr netmask;
    inet_aton(ip_str, &addr);
    inet_aton(netmask_str, &netmask);

    long network = ntohl(addr.s_addr) & ntohl(netmask.s_addr);

    long hostmask = ~ntohl(netmask.s_addr);
    long broadcast = network | hostmask;
    struct in_addr net_broadcast;
    net_broadcast.s_addr = htonl(broadcast);
    return inet_ntoa( net_broadcast );
}

在google上搜索, 找到了刚好做这件事的bash脚本:

标题是:
Script to get network number and broadcast address from ip and netmask

下面是它的bash版本实现(还有ksh版本的):
Bash vresion:

#!/bin/bash

ip=(${1//[![:digit:]]/ })
mask=(${2//[![:digit:]]/ })

for i in $
do
j=7
tag=1
while [ $j -ge 0 ]
do
k=$((2**$j))
if [ $(( $i & $k )) -eq $k ]; then
if [ $tag -eq 1 ]; then
(( n = 1 ))
else
echo -e "n is a bad netamsk with holesn"
exit
fi
else
tag=0
fi
(( j -= 1 ))
done
done

for i in 0 1 2 3
do
a=$a$$(($ & $))
b=$b$$(($ | ($ ^ 255)))
done

echo
echo Network number: $a
echo Broadcast address: $b
echo Netmask bits: $n

上面是网站上这段脚本的本来面目, 很多变量名不知怎么丢掉了, 只剩下了$, 我甚至不去替它重新缩进, 刚好最近在看Clean Code, 下面是以Clean code为宗旨的, 可理解性可维护优先, 简单干净的(希望做到了)bash版本.

# $1: The IP Address
# $2: The netmask
# $3: the (global) variable name to save the broadcast address
# Algorithm:
# network addr = (host addr & netmask)
# host mask = ~netmask
# broadcast = (network addr) | (host mask)
function get_broadcast_addr_from_ip_and_netmask()
{
    # replace all "." with " "
    ip="${1//./ }"
    netmask="${2//./ }"
    output_broadcast_var=$3

    # assign the dotted IP address to separate var from left to right
    set $ip
    ip_1=$1
    ip_2=$2
    ip_3=$3
    ip_4=$4

    set $netmask
    netmask_1=$1
    netmask_2=$2
    netmask_3=$3
    netmask_4=$4

    net_addr_1=$((ip_1 & netmask_1))
    net_addr_2=$((ip_2 & netmask_2))
    net_addr_3=$((ip_3 & netmask_3))
    net_addr_4=$((ip_4 & netmask_4))

    # bash treat numeric as int(typically C int), so bitwise-AND it with 255 to make it one byte only
    broadcast_1=$(( net_addr_1 | (255 & ~netmask_1) ))
    broadcast_2=$(( net_addr_2 | (255 & ~netmask_2) ))
    broadcast_3=$(( net_addr_3 | (255 & ~netmask_3) ))
    broadcast_4=$(( net_addr_4 | (255 & ~netmask_4) ))

    eval "$output_broadcast_var=$broadcast_1.$broadcast_2.$broadcast_3.$broadcast_4"
}

1. 函数的注释, 因为bash的函数并没有形式参数, 所以没机会通过参数名来代替注释, 对bash来说, 关于尽可能不写注释而是写更好的代码的规则需要更改. bash函数几乎一定需要注释来说明期望的参数和产出

2. 函数名字和功能, SRP, 只做一件事情, 原来的实现同时输出了 network number, netmask bits和broadcast address, 根据需要, 这个函数只产生广播地址.

3. 以1, 2, 3, 4的后辍来清楚表达保存的是 192.168.1.2 这样的地址中的4个部分.

4. 以重复的语句代替循环, bash中的for循环并不直观

5. 对magic number 255并没有另起一个名字如 single_byte_mask, 程序员应该对255的特殊性不陌生, 主要是即使用了这样的变量名, 也还需要说明为什么要跟它再执行一次AND 操作.

6. 最后是bash中函数的输出, 正常来说, 输出应该通过返回值, 但bash的函数无法返回一个字符串, 你可以把结果echo 出来, 但那需要调用方用 ``, 或$() 这样的技巧来捕获它, 而这样的方法可能会产生一个代价较高的子进程, 另一个办法是使用全局变量, 但这在调用者和被调函数之间产生了强耦合, 这里采用的是在一个额外的参数($3)中传递调用者希望用来保存结果的变量名, 仍然是全局变量名, 但只需要调用者知道这个名字即可, 不同的调用者可以指定不同的变量名来保存函数执行结果, 通过eval来进行赋值. 不知道这种做法有没有推广的价值. 这种做法还可以通过一个函数返回多个结果, 虽然这违反了SRP原则. 另外通过local 声明, 还可以避免对全局变量的污染, 使得用来保存结果的变量名对调用者来说是局部的.

my_var="123412341234"

function f()
{
        eval "$1='asdf'"
}


function x()
{
        local my_var
        f "my_var"
        echo [$my_var]
}

x
echo global: $my_var

这段脚本的执行证实了上面的结论. 全局的my_var并没受到影响.
阅读(976) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~