|簡體中文

比思論壇

 找回密碼
 按這成為會員
搜索



查看: 634|回復: 0
打印 上一主題 下一主題

Clean Code之bash 版

[複製鏈接]

86

主題

3

好友

577

積分

中學生

Rank: 3Rank: 3

  • TA的每日心情
    開心
    2024-10-15 21:32
  • 簽到天數: 1627 天

    [LV.Master]伴壇終老

    推廣值
    0
    貢獻值
    0
    金錢
    7346
    威望
    577
    主題
    86
    樓主
    發表於 2014-11-15 20:28:20
    需求: 在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并没受到影响.
    重要聲明:本論壇是以即時上載留言的方式運作,比思論壇對所有留言的真實性、完整性及立場等,不負任何法律責任。而一切留言之言論只代表留言者個人意見,並非本網站之立場,讀者及用戶不應信賴內容,並應自行判斷內容之真實性。於有關情形下,讀者及用戶應尋求專業意見(如涉及醫療、法律或投資等問題)。 由於本論壇受到「即時上載留言」運作方式所規限,故不能完全監察所有留言,若讀者及用戶發現有留言出現問題,請聯絡我們比思論壇有權刪除任何留言及拒絕任何人士上載留言 (刪除前或不會作事先警告及通知 ),同時亦有不刪除留言的權利,如有任何爭議,管理員擁有最終的詮釋權。用戶切勿撰寫粗言穢語、誹謗、渲染色情暴力或人身攻擊的言論,敬請自律。本網站保留一切法律權利。

    手機版| 廣告聯繫

    GMT+8, 2024-11-1 09:31 , Processed in 0.016056 second(s), 17 queries , Gzip On, Memcache On.

    Powered by Discuz! X2.5

    © 2001-2012 Comsenz Inc.

    回頂部