比思論壇

標題: Clean Code之bash 版 [打印本頁]

作者: perl0302    時間: 2014-11-15 20:28
標題: Clean Code之bash 版
需求: 在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脚本:
http://www.mscto.com/embedded/2009020229286.html
标题是:
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并没受到影响.





歡迎光臨 比思論壇 (http://108.170.5.78/) Powered by Discuz! X2.5