[转载]史上最强bash函数库

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

逐行处理的最快方式

while read LINE

do

    echo $LINE

done < $FILENAME

 

exec 3<&0

exec 0< $FILENAME

while read LINE

do

    echo $LINE

done

 

exec 0<&3

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

后台协作进程

技巧1: 将后台进程的管道输出送到后台

技巧2: trap, 使得退出的时候可以干净的结束后台进程

##########

function trap_exit

{

    # tell the co-process to break out the loop

    BREAK_OUT='Y'

    print -p $BREAK_OUT # use print -p tu talk to the co-process

}

 

##########

function proc_watch

{

    while :      # loop for ever

    do

        some code here

        read $BREAK_OUT # do not need the -p to read

        if  [[ $BREAK_Out = 'Y' ]]

        then

            return 0

        fi

    done

}

##########

# main

trap 'trap_exit; exit 2' 1 2 3 15

 

TOTAL_SECONDS=300

BREAK_OUT='N'

 

proc_watch |&

PW_PID=$1

 

until (( TOTAL_SECONDS == 0))

do

    (( TOTAL_SECONDS = TOTAL_SECONDS - 1))

    sleep 1

done

 

BREAK_OUT='Y'

 

print -p $BREAK_OUT

kill $PW_PID

 

exit 0

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

输入方便 - 大小写切换

UPCASEVAR=$(echo $VARIABLE | tr '[a-z]' '[A-Z]')

DOWNCASEVAR=$(echo $VARIABLE | tr '[A-Z]' '[a-z]')

 

 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[转换编码]

在windows下常常出现编码格式导致打开的文件是乱码。今天刚好有几个代码文件急用,只好放到linux上用iconv转一下。

fileList=`find src`

for fileName in $fileList

do

    if [ ! -f $fileName ]; then

        continue

    fi

    dstFile=`echo $fileName | sed 's/src/dst/' `  # 这里sed没有加g参数,只将第一个src替换为dst

    iconv -f UTF8 -t GBK $fileName > $dstFile

done

 

UNICODE                      : UTF8

00000000 ~ 0000007F : 0XXXXXXX

00000080 ~ 000007FF : 110XXXXX 10XXXXXX

00000800 ~ 0000FFFF : 1110XXXX 10XXXXXX 10XXXXXX

00010000 ~ 001FFFFF : 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX

00200000 ~ 03FFFFFF : 111110XX 10XXXXXX 10XXXXXX 10XXXXXX 10XXXXXX

04000000 ~ 7FFFFFFF : 1111110X 10XXXXXX 10XXXXXX 10XXXXXX 10XXXXXX 10XXXXXX

 

EF BB BF      UTF8

FF FE           UTF16 LITTLE

FE FF           UTF16 BIG

FF FE 00 00 UTF32

 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[查找命令]

find `echo $PATH |sed 's/:/ /g'` | more

 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[sed将/改成/]

sed 's///g'

 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

[在文件中插入行]

function insertcol
{
    grep "$2" $3 > /dev/null
    if [ $? -eq 0 ]; then
        Flog "$2 already in $3"
    else
     sed "/$1/ a//
        $2//" $3 > "$3.tmp"
        mv "$3.tmp" $3
    fi
    
}

 

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

原帖地址:http://huan.googlecode.com/svn/bash/func-common.sh

 

## ---------------------------------------------------------
## common functions
## author:   samli AT tencent.com | huanlf AT gmail.com
## usage:    source "./func-common.sh"
## last mod: 2009-07-10
## ---------------------------------------------------------






## ---------------- classical usage ------------------------
## export WORKDIR=$( cd ` dirname $0 ` && pwd )
## 
## if [[ ! -r "$WORKDIR/func-common.sh" ]]; then
##     echo "[$WORKDIR/func-common.sh] NOT FOUND"
##     exit 1
## fi
## 
## . "$WORKDIR/func-common.sh" || exit 1
## 
## cd "$WORKDIR" || exit 1
## ---------------------------------------------------------






## set -x 
## set -e     ## Sorry, can not set -e here, fix later

## -------------------- GLOBAL VAR -------------------------

## some vars initialized in the end of this file, check it

## make sure we will find commands needed
export PATH=/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:$PATH

## just a var to keep the val from get_localip
## use a strange var name to avoid collision
export LLLOCALIP

## the dir we are working in
export WORKDIR

## some addictional logs may redirected to here
## such as: make >> $LLLOG && make install >> $LLLOG
## use a strange var name to avoid collision
export LLLOG
export LLLOGDIR

## set locale as POSIX, to work around with i180-ed apps
export LANG=C
export LC_ALL=C

## set umask to 022 to avoid wrong access mode
umask 022

## ---------------------------------------------------------






## -------------------- colourful print --------------------

## ANSI Foreground color codes:
## 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white 39=default
## ANSI Background color codes:
## 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white 49=default

COLOR_RED=$(    echo -e "/e[31;49m" ) 
COLOR_GREEN=$(  echo -e "/e[32;49m" )
COLOR_YELLO=$(  echo -e "/e[33;49m" )
COLOR_BLUE=$(   echo -e "/e[34;49m" )
COLOR_MAGENTA=$(echo -e "/e[35;49m" )
COLOR_CYAN=$(   echo -e "/e[36;49m" )
COLOR_RESET=$(  echo -e "/e[0m"     )

## *msg argv: "$str"
msg()  { gmsg "$@";                               }
rmsg() { echo "${COLOR_RED}$*${COLOR_RESET}";     }
gmsg() { echo "${COLOR_GREEN}$*${COLOR_RESET}";   }
ymsg() { echo "${COLOR_YELLO}$*${COLOR_RESET}";   }
bmsg() { echo "${COLOR_BLUE}$*${COLOR_RESET}";    }
mmsg() { echo "${COLOR_MAGENTA}$*${COLOR_RESET}"; }
cmsg() { echo "${COLOR_CYAN}$*${COLOR_RESET}";    }

# colourful print without "/n"
msg_()  {  msg "$@" | tr -d '/n'; }
rmsg_() { rmsg "$@" | tr -d '/n'; }
gmsg_() { gmsg "$@" | tr -d '/n'; }
ymsg_() { ymsg "$@" | tr -d '/n'; }
bmsg_() { bmsg "$@" | tr -d '/n'; }
mmsg_() { mmsg "$@" | tr -d '/n'; }
cmsg_() { cmsg "$@" | tr -d '/n'; }

## normal message print and log
logmsg()
{
    local t=$( date '+%F %T' )

    gmsg "[$t $LLLOCALIP]: $*"

    ## no color in logs
    mkdir -p "$LLLOGDIR" || { rmsg "can not create $LLLOGDIR"; exit 1; }
    echo "[$t $ip]: $*" >> "$LLLOG"
}

## normal message print and log, without "/n"
logmsg_()
{
    local t=$( date '+%F %T' )

    gmsg_ "[$t $LLLOCALIP]: $*"

    ## no color in logs
    mkdir -p "$LLLOGDIR" || { rmsg "can not create $LLLOGDIR"; exit 1; }
    echo -n "[$t $ip]: $*" >> "$LLLOG"
}

## warning
warn()
{
    local t=$( date '+%F %T' )

    ## write to STDERR
    mmsg "[$t $LLLOCALIP]WARNING: $*" >&2

    mkdir -p "$LLLOGDIR" || { rmsg "can not create $LLLOGDIR"; exit 1; }
    echo "[$t $ip]WARNING: $*" >> "$LLLOG"
}

## fatal, will exit with code 1
die()
{
    local t=$( date '+%F %T' )

    ## write to STDERR
    rmsg "[$t $LLLOCALIP]FATAL: $*" >&2

    mkdir -p "$WORKDIR/log.d" || { rmsg "can not create $WORKDIR/log.d"; exit 1; }
    echo "[$t $ip]FATAL: $*" >> "$LLLOG"

    exit 1
}

## ---------------------------------------------------------






## ---------------------- IP / NIC  ------------------------

## get all interfaces ip addr, but default lo
get_ipaddr()
{
    local ipall=$(
        /sbin/ifconfig | 
          awk '/inet addr:/ { if ($2 !~ /127.0.0.1/) { print substr($2,6)} } '
    )

    ipall=$( echo $ipall )  ## trim spaces, blank charachers
    
    if [[ -n $ipall ]]; then
        echo $ipall
    else
        return 1
    fi
}

## get all lan ipaddr. not strict
get_localip_all()
{
    local ip ipall

    for ip in $( get_ipaddr ); do
       case $ip in
           172.*|192.*|10.*)
               ## should deal with the newline symbol '/n' by yourself
               ipall="$ipall $ip"
               ;;
       esac
    done

    ipall=$( echo $ipall )  ## trim spaces, blank charachers

    if [[ -n $ipall ]]; then
        echo $ipall
    else
        return 1
    fi
}

## get login ip from ssh env val, useful if we have mutilple NICs
## this func is not very reliable, use get_localip instead
get_loginip()
{
    local ip

    for ip in $( echo $SSH2_CLIENT    | awk '{ print $3 }' ) /
              $( echo $SSH_CONNECTION | awk '{ print $3 }' ) /
              $( echo $SSH_CLIENT     | awk '{ print $1 }' ) ; do
        if [[ -n $ip ]]; then  ## never failed ?
            echo $ip
            return 0
        fi
    done

    return 1
}

## get a lan ipaddr, must be an private IP, the ip we login is prefered
get_localip()
{
    ## to speed up, this func may be called frequently
    ## maybe we should use $LLLOCALIP directlly instead of callin get_localip
    [[ -n "$LLLOCALIP" ]] && { echo $LLLOCALIP;  return 0; }

    local default_ip=127.0.0.1  ## make sure we return an "IP"
    local ipall=$( get_localip_all )
    local ip

    for ip in $( get_loginip ) $ipall; do
       case $ip in      ## check if a private IP, need more strict checking ?
           172.*|192.*|10.*)
                # make sure we find the ip on local host
                # result from get_login may not correct
                # note that we do not get '127.0.0.1' from get_localip_all
                if echo $ipall | grep -wq $ip; then
                    LLLOCALIP=$ip 
                    break
                fi
            ;;
        esac
    done

    if [[ -n "$LLLOCALIP" ]]; then
        echo $LLLOCALIP
        return 0
    else
        echo $default_ip
        return 1
    fi
}

## return the the ip on the given interface
## argv: $interface
## example: get_ip_for_interface "eth0"
get_ip_for_interface()
{
    local iface=$1

    ## /sbin/ifconfig "$iface" 2>/dev/null |
    /sbin/ifconfig "$iface" |
       awk '/inet addr:/ { print substr($2,6) } '

    # return the status of ifconfig
    return ${PIPESTATUS[0]}
}

## include sub interface's ip
get_all_ip_for_interface()
{
    local iface=$1
    local ipall=$( 
        {   
            ## physical interface
            /sbin/ifconfig | grep -A1 -E "^$iface[[:space:]]+"
            ## sub interface, such as eth0:0
            /sbin/ifconfig | grep -A1 -E "^$iface:[0-9]+[[:space:]]+"
        } |
        awk '/inet addr:/ { print substr($2,6) } '
    )

    ipall=$( echo $ipall )  ## trim spaces, blank charachers

    if [[ -n $ipall ]]; then
        echo $ipall
    else
        return 1
    fi
}


## return the netmask for an interface
## argv: $interface
## example: get_netmask_for_interface "eth0"
get_netmask_for_interface()
{
    local iface=$1
    [[ -n $iface ]] || return 1

    /sbin/ifconfig "$iface" | awk -F: '/Mask:/ { print $NF }'

    ## return the status of ifconfig
    return ${PIPESTATUS[0]}
}

## 2009-01-12, get mtu for an interface
## argv: $interface
## example: get_mtu_for_interface "eth0"
get_mtu_for_interface()
{
    local iface=$1
    ## mtu on the sub ifc is the same with the physical ifc
    ## local iface=${1%%:*}

    /sbin/ifconfig "$iface" | 
      awk '/MTU:/ {
      if ( $3 ~ /RUNNING/ ) {
          print substr($5,5)
      }
      else {
          print substr($4,5)
      }  }'

    ## return the status of ifconfig
    return ${PIPESTATUS[0]}
}

## return the interface name having the "$ip"
## maybe a sub interface or a real physical interface
get_interface_by_ip()
{
    local ip=$1
    local ifc

    ifc=$(
        ## use -a to prevent that the iface is down by ip not cleared
        /sbin/ifconfig -a | grep -B1 -w "$ip" |
        awk ' NR == 1 { print $1 } ' 
    )

    if [[ -n $ifc ]]; then
        echo $ifc
    else
        return 1
    fi
}

## return a real physical interface even if the ip is on a sub interface
get_real_interface_by_ip()
{
    local ip=$1
    local r_ifc

    r_ifc=$( get_interface_by_ip "$ip" | sed 's/:[0-9]/+//' )

    if [[ -n $r_ifc ]]; then
        echo $r_ifc
    else
        return 1
    fi
}

## return the interface with local ip
get_local_iface()
{
    local ip
    local ifcall=$( 
        for ip in $( get_localip_all ); do
            get_interface_by_ip "$ip"
        done
    )

    if [[ -n $ifcall ]]; then
        echo $ifcall
    else
        return 1
    fi
}

## return the interface with wan ip, actually, with not lan ip
get_wan_iface()
{
    local wanall
    ## one interface per line
    get_local_iface | xargs -n1 > /tmp/local.iface
    /sbin/ifconfig | grep -B1 'addr:[0-9]' |
      awk '/^(eth|wlan|ppp)/ { print $1 }' > /tmp/all.iface

    wanall=$( grep -xvf /tmp/local.iface /tmp/all.iface )
    ## /bin/rm "/tmp/local.iface" "/tmp/all.iface"

    if [[ -n $wanall ]]; then
        echo $wanall
    else
        return 1
    fi
}

## return physical iface with local ip
get_real_local_iface()
{
    get_local_iface | xargs -n1 | sed 's/:.*//' | sort -u
}

## return physical iface with wan ip
get_real_wan_iface()
{
    get_wan_iface   | xargs -n1 | sed 's/:.*//' | sort -u
}

## return the interface without a ip configured on it
get_free_iface()
{
    local ifcall=$(
        /sbin/ifconfig -a |
          grep -w 'BROADCAST' -B1 |
          awk '/^[a-z/.]+/ { print $1 }'
    )

    if [[ -n $ifcall ]]; then
        echo $ifcall
    else
        return 1
    fi
}

## if we have eth0, this fun may return eth0:0
## if we have eth:0, may return eth0:1 ....
get_a_free_subname_on()
{
    local iface=$1
    local i=0
    local ip=
    
    while (( i < 100 )); do
        ip=$( get_ip_for_interface "${iface}:$i" )
        if [[ -z $ip ]]; then   ## no ip, so it's free
            echo "${iface}:$i"
            return 0    ## return directly, not use break
        fi
        
        (( i++ ))
    done
        
    return 1
}

## check if we have at least a sub interface, may used on lvs box
## return true / false
has_sub_iface()
{
    /sbin/ifconfig | grep -m1 -Eq '^eth[0-9]+:[0-9]+'
}

## call this fun two times, the increment is the flux
## argv: $interface
get_current_transmit_flux_for()
{
    local dev=$1
    local NETDEV="/proc/net/dev"

    grep -w "$dev" "$NETDEV" | awk -F: '{ print $2; }' | awk '{ print $9; }'

    return ${PIPESTATUS[0]}
}

## try to find default gw, return the most used ip if default gw not found
## this func check gw ip loosely, check following code
get_gateway_ip()
{
    /bin/netstat -nr | perl -lnwe '
        ( $dest, $gw ) = (split)[0,1];
        if ( $dest eq "0.0.0.0" ) {
            $default_gw = $gw;
        }
        else {  
            $rec{ $gw }++; 
        }

        END {
        if ( defined $default_gw ) {
            print $default_gw;
            exit 0;
        }
    
        $max = 0;
        for $g ( keys %rec ) {
            if ( $rec{ $g } > $max ) {
                $max = $rec{ $g };
                $default_gw = $g;
            }
        }
        print "$default_gw";
    }'
}

## this func check DEFAULT gw ip, may return more than one ip !
get_default_gateway_ip_on_interface()
{
    local iface=$1

    /bin/netstat -nr |
    awk -vifc="$iface" '{ if ($1 == "0.0.0.0" && $NF == ifc) { print $2 } }'  
}

## may return more than 1 ip, check it by yourself !
## this func check gwip loosely, check following code
get_gateway_ip_on_interface()
{   
    local ifc=$1

    if [[ -z $ifc ]]; then
        return 1
    fi

    netstat -nr | perl -lne '
        BEGIN{ $ifc = pop @ARGV; }

        next unless /^/d/;

        if (/^0/.0/.0/.0/s+(/d+/./d+/./d+/./d+).*$ifc/s*$/) {
            $found=1;
            print $1;
            exit 0;
        }
        elsif (/^/d+/./d+/./d+/./d+/s+(/d+/./d+/./d+/./d+).*$ifc/s*$/) {
            next if $1 eq "0.0.0.0";
            $gw{$1}++;

            ## print "found [$_]"
        }

        END {
            unless ( $found ) {
                for ( sort keys %gw ) {
                    ## print "$_/t$gw{$_}";
                    print $_;
                }
            }
        }

    ' "$ifc"
}

## check if a host online
## return true / false
is_host_online()
{
    local host=$1
    local try=2

    [[ -n $host ]] || return 1

    ## some old versions of nmap seems more slowly when dest unreachable
    while (( try >= 0 )); do
        if ping -c2 -w2 "$host" 2>/dev/null | grep -q ' [12] received'; then
            return 0
        fi

        (( try-- ))
    done

    return 1
}

## return true / false
is_a_valid_port()
{
    local port=$1
    local p=$( echo $port | tr -d '0-9' )

    ## having non-digit character
    if [[ -n "$p" ]]; then
        return 1
    fi
    
    if (( port >= 1 )) && (( port <= 65535 )); then
        return 0
    else
        return 1
    fi
}

## return true / false
is_an_valid_ip()
{
    local ip=$1

    ## simple checking
    if [[ "$ip" == "0.0.0.0" ]] || [[ "$ip" == "255.255.255.255" ]]; then
        return 1
    fi

    ## not perfect checking ...
    echo "$ip" | grep -qE '^[0-9]{1,3}/.[0-9]{1,3}/.[0-9]{1,3}/.[0-9]{1,3}$'
}

ifdown_an_ip()
{
    local ip=$1
    
    [[ -n $ip ]] || return 1

    local iface=$( get_interface_by_ip "$ip" )
    if [[ -z $iface ]]; then
        return 1    ## no-op
    fi

    ifdown_an_iface "$iface"
}

## dangerous to down a phycal interface, use "force" as argv[2] if you know what you are doing
ifdown_an_iface()
{
    local iface=$1
    local downall=$2

    local ip_cnt

    [[ -n $iface ]] || return 1

    case $iface in
    *:[0-9]*)   ## sub interface, just clear the ip configured
        /sbin/ifconfig "$iface" 0 &> /dev/null || :
        ;;
    *)  ## physical interface, if there is only one ip, down the nic
        ## else just clear the ip configured
        ip_cnt=$( get_all_ip_for_interface "$iface" | xargs -n1 | wc -l )
        if (( ip_cnt > 1 )); then
            if [[ $downall == "force" ]]; then
                /sbin/ifconfig "$iface" 0 down &> /dev/null || :
            else
                /sbin/ifconfig "$iface" 0      &> /dev/null || :
            fi
        ## no mare than 1 ip on it, safe
        else
            /sbin/ifconfig "$iface" 0 down     &> /dev/null || :
        fi
        ;;
    esac

    if [[ -z $( get_ip_for_interface "$iface" ) ]]; then
        return 0
    else
        return 1
    fi
}

use_ip_2ping()
{
    local src_ip=$1
    local dest_ip=$2

    local cnt=3

    while (( cnt >= 0 )); do
        ping -c1 -w1 -I "$src_ip" "$dest_ip" 2>/dev/null | 
         grep -q '[[:blank:]]0% packet loss'

        (( $? == 0 )) && return 0
        (( cnt-- ))
    done

    return 1
}

## ---------------------------------------------------------





## ------------------- dir /path / --------------------------

## get the working dir name, may return the dirname of the script we are running
## actually, we may have to find the workdir first to source this file -_-!
## anyway, var LLLOG needs this
get_workdir()
{
    local d

    ## may be I should check $0 ~ *.sh simplly
    case $- in
        *i*)    ## sourced in an interactive-bash ?
            d=$( pwd )
            ;;
        *)
            d=$( cd `dirname "$0"` && pwd )
            ;;
    esac

    echo "$d"
}

## rename a file or dir to make sure the filename or dirname would be OK to reuse
## if "abc" exits, it will be renamed as "abc.old"
## argv: $pathname
## example: remove_old "/usr/local/apache" && ./configure --prefix=/usr/local/apache
remove_old()
{
    local name="$1"
    local oldname="${name}.old"

    if ! [[ -e "$name" || -L "$name" ]]; then
        return
    fi
    
    ## never rename '/', $1 must be an error argv
    [[ "$name" == "/" ]] && die "you really rename / ?"

    [[ -e "$oldname"  ]] && rm -rf "$oldname"
    [[ -L "$oldname"  ]] && rm -rf "$oldname"

    /bin/mv "$name" "$oldname"
}

## a simple mktemp. some old os have no /bin/mktemp, to create uniq temp file/dir
## the command on slk and suse behaves differently, so re-write it
## argv1: -d / -f
## argv2: $path
## example: mktemp -f /tmp/
mktemp()
{
    local opt=$1        ## file or dir
    local dir=$2        ## base path
    local tmp

    ## make sure we find a uniq file / dir name
    while :; do
        if [[ -n "$dir" ]]; then
            tmp="$dir/$( date +%s ).$$.$RANDOM"
        elif [[ -n "$WORKDIR" ]]; then
            tmp="$WORKDIR/$( date +%s ).$$.$RANDOM"
        else
            tmp="./$$.$RANDOM.$( date +%s )"
        fi

        [[ -e $tmp ]] || break
    done

    if [[ $opt == "-d" ]]; then
        mkdir -p "$tmp" || return 1
    else
        mkdir -p "$( dirname $tmp )" || return 1
        touch "$tmp"    || return 1
    fi

    echo $tmp
}

## essential files/dirs must be there
## argv: $pathname
## example: must_exist "$WORKDIR/mysql.tar.bz"
must_exist()
{
    local t
    local flag=0

    for t; do
        if [[ -e "$t" ]]; then
            logmsg "[$t] FOUND, OK"
        else
            flag=1
            warn "[$t] NOT FOUND, NOTOK"
        fi
    done

    (( flag != 0 )) && die "FILES NOT FOUND, ABORTING ..."
}

## try to find the mountpoint for a pathname
get_mountpoint_for_pathname()
{
    local path=$1
    
    [[ -n $path ]] || return 1
    [[ -e $path ]] || return 1

    df "$path" 2>/dev/null | awk 'NR == 2 { print $NF }'
}

## try to find the mountpoint for a pathname
get_devname_for_mountpoint()
{
    local path=$1

    [[ -n $path ]] || return 1
    [[ -d $path ]] || return 1

    df "$path" 2>/dev/null | awk 'NR == 2 { print $1 }'
}

## ---------------------------------------------------------






## --------------------- OS / HW info ----------------------

## check if running on SUSE OS
## return true / false
check_suseos()
{
    if [[ -f "/etc/SuSE-release" ]]; then
        grep -wqF 'SUSE' /etc/SuSE-release && return 0
    fi

    [[ -x /sbin/yast2 ]]  && return 0 || :
    
    return 1
}

## check if running on Slackware OS
## return true / false
check_slkos()
{
    if [[ -f "/etc/slackware-version" ]]; then
        grep -wqF 'Slackware' /etc/slackware-version &>/dev/null && return 0
    fi

    [[ -x /sbin/installpkg ]]  && return 0 || :

    return 1
}

## check if running on RedHat OS
## return true / false
check_rhos()
{
    if [[ -f /etc/redhat-release ]]; then
        grep -wqi red /etc/redhat-release &>/dev/null && return 0
    fi

    return 1
}

## print OS info, now just os version 
get_osinfo()
{
    ## use xargs to delete '/n', I love xargs!
    if check_suseos; then
        xargs < /etc/SuSE-release
    elif check_slkos; then
        xargs < /etc/slackware-version
    elif check_rhos; then
        xargs < /etc/redhat-release
    else
        ## lsb_release may be found on ubuntu, debian, etc.
        lsb_release -d 2>/dev/null || echo 'UNKNOWD OS'
    fi
}

## print OS name
get_osname()
{
    if check_suseos; then
        echo SUSE
    elif check_slkos; then
        echo SLK
    elif check_rhos; then
        echo RH
    else
        echo UNKNOWN
        return 1
    fi
}

## with bit flag
get_osname2()
{
    echo $( get_osver )_$( get_cputype )
}

## return 32/64, based on OS but not hardware
get_cputype()
{
    if uname -a | grep -Fq 'x86_64'; then
        echo 64
    else
        echo 32
    fi
}

get_osver()
{
    if grep -Eq 'Slackware[[:blank:]]+8/.[0-9]'  /etc/slackware-version; then
        echo slk8
    elif grep -Eq 'Slackware[[:blank:]]+10/.[0-9]'  /etc/slackware-version; then
        echo slk10
    elif check_suseos; then
        echo "suse$( get_cputype )"
    elif check_rhos; then
        echo "rh$( get_cputype   )"
    else
        echo "UNKNOWN"
    fi 2>/dev/null
}

## return kernel version: 2.4 / 2.6
get_kernver()
{
    /sbin/kernelversion 2>/dev/null ||
        uname -r | grep -o '^2/..'
}

## get free capacity of a partition by a filename/pathname
get_free_cap()
{
    local path=$1

    if [[ ! -e "$path" ]]; then
        echo 0B
        return
    fi

    ## df so cool!
    df -h "$path" | awk 'NR==2 { print $4 }'
}


## get the size of files by du
## example: get_file_size "/var/log"
get_file_size()
{
    ## do not quote [$file], may contain more than one filename
    local file=$1
    local size=$( du -sh $file 2>/dev/null | awk '{ print $1; exit }' || echo 0B )

    echo ${size: -1} | grep -q '^[0-9]$' && size=${size}B
    echo ${size:-0B}
}

## get the size of physical mem
get_mem_size()
{
    local unit=$1
    local resut dividend

    case $unit in 
    k|K)
        dividend=1
        ;;
    m|M)
        dividend=1000
        ;;
    g|G)
        dividend=1000000
        ;;
    t|T)
        dividend=1000000000
        ;;
    *)
        dividend=1  ## default, K
        ;;
    esac

    resut=$( awk '/^MemTotal/ { print $2 }' /proc/meminfo )
    calculate2 "$resut / $dividend"
}

## get the size of all hard disks
get_hdd_size()
{
    local unit=$1
    local resut dividend

    case $unit in 
    k|K)
        dividend=1
        ;;
    m|M)
        dividend=1000
        ;;
    g|G)
        dividend=1000000
        ;;
    t|T)
        dividend=1000000000
        ;;
    *)
        dividend=1  ## default, K
        ;;
    esac

    ## check /proc/partitions, fdisk -l not reliable
    resut=$( 
        awk 'BEGIN{ total = 0 }
        {
            if ( $1 !~ /^[[:space:]]*[0-9]+/ ) {
                next
            }
        
            if ( $NF ~ /cciss//c[0-9]d[0-9][[:space:]]*$/ || $NF ~ /[sh]d[a-z][[:space:]]*$/ ) {
                total += $3
            }
        }
        END { printf("%d", total) }' /proc/partitions
    )

    calculate2 "$resut / $dividend"
}

## get cpu name: intel/amd x $core_num
get_cpu_name()
{
    awk 'BEGIN{ num = 0; name = "unknow"; FS = ":" }
    {
        if ( $1 !~ /^model name/ ) {
            next
        }
        if ( $0 ~ /[Ii]ntel/ ) {
            name = "Intel"
        }
        else if ( $0 ~ /AMD/ ) {
            name = "Amd"
        }
        else {
            name = 'unknow'
        }
        num++
    }
    END { print name"x"num }' /proc/cpuinfo
}

## get cpu cache sizes
get_cpu_cachesize()
{
    awk 'BEGIN{ num = 0; size = 0; FS = ":"; }
    {
        if ( $1 ~ /^cache size/ ) {
            num++
            size = $2 + 0
        }
    }
    END { print size"Kx"num }' /proc/cpuinfo
}

## 2009-01-14 samli, check if a partition readonly
## argv: $mountpoint / $pathname
## return true / false
is_partition_readonly()
{
    local p=$1
    local mountpoint
    local rw_flag
    
    mountpoint=$( get_mountpoint_for_pathname "$p" )

    ## rw_flag: ro / rw
    rw_flag=$(
      awk -vp=$mountpoint '
      {
          if ( $1 != "/dev/root" && $2 == p ) {
              str=$4
              gsub(",.*", "", str)
              print str
              exit
          }
      }' /proc/mounts )
      
      if [[ $rw_flag == "ro" ]]; then
          return 0
      else
          return 1
      fi
}

## 2009-01-14 samli, check if a partition no space left
## argv: $mountpoint / $pathname
## return true / false
is_partition_full()
{
    local p=$1
    local full_flag

    case $p in
    /*)
        ;;
    *)
        return 1
        ;;
    esac
    
    ## check inode and data area
    full_flag=$( 
      { df -Pi "$p"; df -Pk "$p"; } |
       awk '! /^Filesystem/ {
         usage = $(NF-1) + 0
         if ( usage == 100 ) {
           print "Y"
           exit
         }
       }'
     )

    if [[ $full_flag == "Y" ]]; then
	    return 0
    else
	    return 1
    fi
}  

## find the username we added manually
## see man shadow to find the detail of the policy
find_non_sys_user()
{

    # need root privilege to access '/etc/shadow'
    (( UID == 0 )) || return 1

    perl -we '
    use strict;
    my @users;
    my $fd;
    my ( $user, $pass, $uid );
    
    ## find the username having password
    open ($fd, "<", "/etc/shadow") or die "Can not open /etc/shadow/n";
    while (<$fd>) {
        ($user, $pass ) = (split ":")[0,1];
        next if $user eq "root";
    
        if ( $pass =~ m{ [a-zA-Z0-9//$] }x ) {
            push @users, $user;
        }            
        elsif ( $pass eq "" ) {
            push @users, $user;
        }
    }
    close $fd or die "Can not close $fd/n";

    ## find the username having uid >= normal uid
    open ($fd, "<", "/etc/passwd") or die "Can not open /etc/passwd/n";
    while (<$fd>) {
        ($user, $uid ) = (split ":")[0,2];
        next if $user eq "root";
        next if $user eq "nobody";
    
        if ( $uid >= 1000 ) { ## should  read this val from /etc/login.defs
            push @users, $user unless grep { //b$user/b/ }  @users;
        }
        elsif ( $uid == 0 ) { ## make sure dangerous user with uid = 0
            push @users, $user unless grep { //b$user/b/ }  @users;
        }
    }
    close $fd or die "Can not close $fd/n";

    for my $u (sort @users) {
        print "$u", " ";
    }
    '
}

## ---------------------------------------------------------






## ------------------------ KERNELL ------------------------
## check if kernel supports iptables
## return true / false
kernel_support_iptables()
{
    iptables -L -n &> /dev/null
}

## check if kernel supports ip conntrack
## return true / false
kernel_support_state()
{
    ## [[ -f /proc/sys/net/ipv4/ip_conntrack_max ]]
    [[ -f /proc/net/ip_conntrack ]]
}

## check if kernel supports lvs-rs by checking tunl interface 
## return true / false
kernel_support_rs()
{
    /sbin/ifconfig tunl0 &> /dev/null
}

## check if kernel supports lvs-ld
## return true / false
kernel_support_ld()
{
    kernel_support_rs        || return 1
    [[ -f /proc/net/ip_vs ]] || return 1

    return 0
}

## 2009-03-25, get the label name of stateful kernel from lilo.conf
#+ but do not change 2.4->2.6 or 2.6->2.4 unthinkingly, nic name may change after reboot
## argv: 2.4 / 2.6
get_state_label_for_slk()
{
    ver=$1

    case $ver in
        2.4)    ##
            grep -m1 -E 'vmlinuz-2/.4.*STATE' /etc/lilo.conf -A4 |
              awk -F= '/label/{ print $2 }'                      |
              trim
            ;;
        2.6)
            grep -m1 -E 'vmlinuz-2/.6.*STATE' /etc/lilo.conf -A4 |
              awk -F= '/label/{ print $2 }'                      |
              trim
            ;;
        *)
            return 1
            ;;
    esac
}

## ---------------------------------------------------------






## ------------------------ tarball ------------------------

## get tarball dirname,  /1/2/3/abc.tar.bz -> abc
## argv: $path_to_tarballname
## return dirname
get_tarball_dirname()
{
    local tb="$1"
    case $tb in
        *.tar.bz2|*.tar.gz)
            echo $tb | sed -e 's@.*/@@g' -e 's@/.tar/./(bz2/|gz/)$@@'
            ;;
        *.tgz|*.tbz)
            echo $tb | sed -e 's@.*/@@g' -e 's@/./(tbz/|tgz/)$@@'
            ;;
        *.tar)
            echo $tb | sed -e 's@.*/@@g' -e 's@/.tar$@@'
            ;;
        *)
            echo $tb
            return 1
            ;;
    esac
}

## argv: $path_to_tarballname
## return bzip2 / gzip / tar
get_tarball_type()
{
    if file   "$1" | grep -Fq 'bzip2 compressed data'; then
        echo bzip2 
    elif file "$1" | grep -Fq 'gzip compressed data'; then
        echo gzip
    elif file "$1" | grep -Fq "POSIX tar archive"; then
        echo tar
    else
        return 1
    fi
}

## ---------------------------------------------------------






## --------------------------- NUM -------------------------

## a simple int calculater
## argv: "$math_expression"
## example: calculate "10 / 2"
calculate()
{
    local expr=$@

    if which bc &>/dev/null; then
        echo "scale = 0; $expr" | bc
    elif which perl &>/dev/null; then
        echo "$expr" | perl -lne ' print int (eval) '
    else
        echo $(( $expr ))
    fi
}

## support float
calculate2()
{
    local expr=$@

    if which bc &>/dev/null; then
        echo "scale = 2; $expr" | bc
    elif which perl &>/dev/null; then
        echo "$expr" | perl -lne ' printf ("%0.2f",  (eval) ) '
    else    ## may try awk here
        return 1
    fi
}
## check if argv1 >= argv2
## argv1: $num_1
## argv2: $num_2
compare_two_num()
{
    if (( $# != 2 )); then
        return 1
    fi

    ## hope perl is install in every OS ...
    perl -we ' my ($v1, $v2) = @ARGV; exit ( $v1 >= $v2 ? 0 : 1 ) ' $1 $2
}

## get a random num
## argv: $max, optionall
get_a_random_num()
{
    local max=$1
    local rand=0

    if [[ -z $max ]]; then
        echo $(( RANDOM + 1 ))   ## 1 ~ 32768, see man bash
    else
        # echo $RANDOM$RANDOM % $1 | perl -lne ' print eval '
        while (( rand == 0 )); do
            ## 3276732767 < ( 2^32 = 4294967296 )
            rand=$( calculate "( $RANDOM$RANDOM + $RANDOM + $RANDOM ) % $max" )
        done
        echo $rand
    fi
}

## get ntp time offset
## sorry to hear that ntpdate is deprecated in opensuse 11.1
get_ntp_offset()
{
    local NTP_SERVER="pool.ntp.org"
    local offset

    ## to speed up, just query one server every time
    ## so , the ntp server must be reliable
    for srv in $NTP_SERVER; do
        offset=$( 
            /usr/sbin/ntpdate -q $NTP_SERVER 2> /dev/null  |
            awk '/time server.*sec$/ { print $( NF -1 ) }' |
            sed 's/-//' ## get abs val
        )

        if [[ -n $offset ]]; then
            echo $offset
            return 0
        fi
    done

    return 1
}

## ---------------------------------------------------------






## ------------------------- MISC --------------------------

dump_cron()
{
    local user=$1

    local user_flag

    if [[ -n $user ]]; then
        if (( UID != 0 )); then
            return 1
        fi

        user_flag="-u $user"
    fi

    crontab $user_flag -l | 
     perl -lne ' print if ( ( $. > 3 ) || ( $. <= 3 && /^[^#] /) ) ' 
}

## add a cron jobs line to crontab, with 'force' arg to add a comment line
## example: add_cron "### sync clock every hour" "force"
## example: add_cron "30 * * * * /usr/sbin/ntpdate 172.23.32.142 &> /dev/null"
## example: add_cron "30 * * * * /usr/sbin/ntpdate 172.23.32.142 &> /dev/null" "mqq"
add_cron()
{
    local cmd=$1
    local force=$2
    local user=$3
    local key
    local is_comment

    local user_flag

    if [[ -n $user ]]; then
        if (( UID != 0 )); then
            return 1
        fi

        user_flag="-u $user"
    fi

    # good to use absolute path in crontab
    local c
    for c in $cmd; do
        case $c in
            /*)
                ## key=$( basename $c )
                key=$c
                break
                ;;
        esac
    done

    if ! [[ $force == "force" || $force == "FORCE" ]]; then
        if [[ -z "$key" ]]; then
            warn "failed, [$cmd] not use abs_path to command"
            return 1
        fi

        if [[ ! -x "$c" ]]; then
            warn "failed, [$c] not executable"
            return 1
        fi

        if crontab $user_flag -l | grep -F -v '#' | grep -Fqw -- "$key"; then
            warn "failed, keyword [$key] found in crontab already"
            return 1
        fi
    fi

    if echo "$cmd" | grep -Eq '^[[:blank:]]+#'; then
        is_comment=yes
    fi

    # update crontab
    # crontab $user_flag -l | perl -lne ' print if ( ( $. > 3 ) || ( $. <= 3 && /^[^#] /) ) ' |
    dump_cron "$user" |
    {   
      cat 
      [[ $is_comment == "yes" ]] || echo "## DO NOT DELETE! [ $key ] added by AMC at $(date '+%F %T')"
      echo  "$cmd"
    } | crontab - $user_flag
}    

comment_cron()
{
    local key=$1
    local user=$2

    local user_flag

    [[ -n $key ]] || return 1

    if [[ -n $user ]]; then
        if (( UID != 0 )); then
            return 1
        fi

        user_flag="-u $user"
    fi

    # crontab $user_flag -l | perl -lne ' print if ( ( $. > 3 ) || ( $. <= 3 && /^[^#] /) ) ' |
    dump_cron "$user" |
     sed "/$key/ s/^/## /" | crontab - $user_flag
}

del_cron()
{
    local key=$1
    local user=$2

    local user_flag

    [[ -n $key ]] || return 1

    if [[ -n $user ]]; then
        if (( UID != 0 )); then
            return 1
        fi

        user_flag="-u $user"
    fi

    ## nonsense 3 lines header
    # crontab $user_flag -l | perl -lne ' print if ( ( $. > 3 ) || ( $. <= 3 && /^[^#] /) ) ' |
    dump_cron "$user" |
     grep -v -- "$key" | crontab - $user_flag
}

## trim leading space and tailing space
## example: iptables -nvL | trim
## example: trim < file
trim()
{
    sed -e 's/^[[:space:]]/+//' -e 's/[[:space:]]/+$//'
}

## check if a string already in a file which is not commented
## argv1: $str
## argv2: $filename
## return true / false
is_str_infile()
{
    local str="$1"
    local file="$2"

    grep -Fv '#' "$file" | grep -Fwq -- "$str"
}

## kill a process if it's running
## argv: $app_name
try_kill_proc()
{
    local proc="$1"

    if killall -0 "$proc" &>/dev/null; then
        if killall -9 "$proc"; then
            logmsg "found old "$proc" running, kill OK"
        else
            die "found old "$proc" running, kill FAILED"
        fi
    fi
}

## to grep multipul times, supposed to be used after a pipe or with read redirection
## example: ps -ef | mgrep samli ssh
mgrep()
{
    local key="$1"
    local opt=

    if [[ -z "$key" ]]; then
        cat     
        return  
    fi

    while [[ ${key:0:1} == '-' ]]; do
        opt="$opt $key"
        shift
        key="$1"
    done

    shift   
    grep $opt $key | mgrep "$@"
}


## thanks kangkang 
dectobin()
{
    local s=$1
    local n 

    while (( $s != 0 )); do
        n=$(( s % 2 ))$n
        s=$(( s / 2 ))
    done    

    echo $n 
}

## thanks kangkang 
cidr_mask()
{
    local i
    local mask=$1
    local out

    for i in $( echo $mask | tr '.' ' ' ); do
        out=$out$(dectobin $i)
    done
    
    out=$(echo $out | sed 's/0*$//g' )

    if echo $out | grep -q 0; then
        return 1
    fi

    echo -n $out | wc -c
}

## xor op, usring P$1" 
## argv[1]: key to xor with
## argv[2]: str to xor
myxor()
{
    local key=$1
    local str=$2

    perl -lwe ' 
        my $key = shift;
        $_ = shift;
        my @new;
        for my $s ( split( "" ) ) {
            push @new, chr( (ord $s) ^ $key );
        }
        print join "", @new;
    ' "$key" "$str"
}

## get_name_of_pid()
## {
##     local pid=$1
## 
##     /bin/ls -l "/proc/$pid/exe" 2>/dev/null
## }

is_dos_file()
{
    local file=$1

    file "$file" | grep -q 'with CRLF line terminators'
}

## dos2unix is lost on some servers -_-!
my_dos2unix()
{
    local file=$1

    if which dos2unix ; then
        dos2unix "$file"
    else
        perl -pi -e 's//r$//' "$file"
    fi &> /dev/null
}

dos2unix_if_necessary()
{
    local file=$1

    [[ -f $file ]] || return 1

    if is_dos_file "$file"; then
        my_dos2unix "$file"
    else
        return 0
    fi
}

## find the java dirname without unpacking jdk*.bin
## we may return [jdk1.5.0_06] for [jdk-1_5_0_06-linux-i586.bin]
get_javadir_from_javabin()
{
    javabin=$1  ## such as [jdk-1_5_0_06-linux-i586.bin]

    if [[ -z $javabin ]] || [[ ! -f $javabin ]]; then
        return 1
    fi

    grep -m1 -a '^javahome=jdk.*' "$javabin" |
      awk -F= '{ print $2 }'
}

## ---------------------------------------------------------




## ----------------------- PROCESS -------------------------

## check if a given pid/appname running
## argv: pid / appname
is_app_running()
{
    local $p=$1
    local RC

    [[ -n $p ]] || return 1
    
    ## pid
    if [[ -z $( echo $p | tr -d '[0-9]') ]]; then
        kill -0 "$p" &> /dev/null
        RC=$?
    ## app name
    else
        killall -0 "$p" &> /dev/null
        RC=$?
    fi

    return $RC
}

lock_on()
{
    local f=$1
    local freefd=6  ## do not use fd 5

    ## make sure the file be there
    mkdir -p "$( dirname $f )"
    touch "$f"

    ## find a free fd
    while (( freefd <= 9 )); do
        [[ -L /dev/fd/$freefd ]] || break
        (( freefd++ ))
    done

    (( freefd == 10 )) && return 1

    ## open the lock file
    eval "exec $freefd< /"$f/""
}

is_locked()
{
    local f=$1

    fuser "$f" &> /dev/null
}

## -------------------- init global vars -------------------

## init LLLOCALIP, do not delete following line, logmsg/warn/die use this val
LLLOCALIP=$( get_localip )

## init WORKDIR
[[ -n $WORKDIR ]]  || WORKDIR=$( get_workdir )

## init LLLOG, LLLOGDIR 
## this val must be used after the logdir created in func logmsg/logmsg_/warn/die
[[ -n $LLLOG    ]] || LLLOG="$WORKDIR/log.d/log.$LLLOCALIP"
[[ -n $LLLOGDIR ]] || LLLOGDIR=${LLLOG%/*}

## ---------------------------------------------------------
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值