编写性能标准的bash代码

Ama*_*ith 0 linux bash shell netstat iptables

有没有更好的方法来重写此代码以获得增强的性能?

如果你要获得一堆IP,系统似乎就会挂起.

TMP_PREFIX='/tmp/synd'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"
BANNED_IP_MAIL=`$TMP_FILE`
BANNED_IP_LIST=`$TMP_FILE`
echo "Banned the following ip addresses on `date`" > $BANNED_IP_MAIL
echo >> $BANNED_IP_MAIL
BAD_IP_LIST=`$TMP_FILE`
netstat -ntu | grep SYN_RECV | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST
cat $BAD_IP_LIST
if [ $KILL -eq 1 ]; then
    IP_BAN_NOW=0
    while read line; do
        CURR_LINE_CONN=$(echo $line | cut -d" " -f1)
        CURR_LINE_IP=$(echo $line | cut -d" " -f2)
        if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then
            break
        fi
        IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
        if [ $IGNORE_BAN -ge 1 ]; then
            continue
        fi
        IP_BAN_NOW=1
        echo "$CURR_LINE_IP with $CURR_LINE_CONN SYN_RECV connections" >> $BANNED_IP_MAIL
        echo $CURR_LINE_IP >> $BANNED_IP_LIST
        echo $CURR_LINE_IP >> $IGNORE_IP_LIST
        if [ $CSF_BAN -eq 1 ]; then
            $CSF -d $CURR_LINE_IP
        else
            $IPT -I INPUT -s $CURR_LINE_IP -j DROP
        fi
    done < $BAD_IP_LIST
    if [ $IP_BAN_NOW -eq 1 ]; then
        dt=`date`
                hn=`hostname`
        if [ $EMAIL_TO != "" ]; then
            cat $BANNED_IP_MAIL | mail -s "IP addresses banned on $dt $hn" $EMAIL_TO
        fi
    fi
fi
rm -f $TMP_PREFIX.*
Run Code Online (Sandbox Code Playgroud)

ric*_*ici 7

当然,有很多方法可以改进,但你应该试着弄清楚真正的瓶颈在哪里.(它可能是iptables,在这种情况下,您可能希望尝试在一次调用中执行所有表更新,而不是一次执行一次.但我只是在猜测.)

以下是一些建议; 我没有读完所有内容:

netstat -ntu | grep SYN_RECV | awk '{print $5}' | cut -d: -f1 |
sort | uniq -c | sort -nr > $BAD_IP_LIST
Run Code Online (Sandbox Code Playgroud)

如果您只对SYN_RECV状态的连接感兴趣,为什么要列出udp?无论如何,您使用三个实用程序(grep,awkcut)来执行一个简单的面向行的操作.你可以在一个中完成所有操作,例如awk:

awk '$6 == "SYN_RECV" {print substr($5, 1, index($5, ":") - 1)}'
Run Code Online (Sandbox Code Playgroud)

事实上,你也可以在awk中进行统一和计数:

awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]} END{for (i in ip) print ip[i], i}'
Run Code Online (Sandbox Code Playgroud)

编辑:你也可以在这里过滤所需的数量:

awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]}
     END              {for (i in ip) if (ip[i] >= '$NO_OF_CONNECTIONS') print ip[i], i}'
Run Code Online (Sandbox Code Playgroud)

现在您只需输出IP地址,因为您不再需要在bash脚本中进行过滤.我不知道这是否比通过排序和uniq和再次排序更快,但它可能很好.

while read line; do
    CURR_LINE_CONN=$(echo $line | cut -d" " -f1)
    CURR_LINE_IP=$(echo $line | cut -d" " -f2)
    if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then
        break
    fi
Run Code Online (Sandbox Code Playgroud)

你想从stdin中读取两个字段.你为什么不这样做:

while read CURR_LINE_CONN CURR_LINE_IP IGNORED &&
      ((CURR_LINE_CONN >= NO_OF_CONNECTIONS)); do
Run Code Online (Sandbox Code Playgroud)

这节省了两个子壳和两个切割调用.(读取内置的IGNORED只是偏执狂,因为awk只会输出两个字段.但这并不是好偏执,因为它会默默地忽略错误.)

编辑:如上所述,你也可以在这里摆脱测试.所以它只会是:

netstat -nt |
awk '$6 == "SYN_RECV" {++ip[substr($5, 1, index($5, ":") - 1)]}
     END { for (i in ip)
             if (ip[i] >= '$NO_OF_CONNECTIONS')
               print ip[i], i}' | tee $BAD_IP_LIST
if ((KILL)); then
  IP_BAN_NOW=0
  while read IP IGNORED; do
Run Code Online (Sandbox Code Playgroud)

下一个:

IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
    if [ $IGNORE_BAN -ge 1 ]; then
        continue
    fi
Run Code Online (Sandbox Code Playgroud)

grep -c使grep读取整个输入文件以获取计数; 你只想知道ip是否存在.你想要grep -q:

if $(grep -q -F -x $CURR_LINE_IP $IGNORE_IP_LIST); then continue; fi
Run Code Online (Sandbox Code Playgroud)

(-F告诉grep将模式解释为字符串而不是正则表达式,这是你想要的,因为否则.是通配符.-x告诉grep匹配整行.一个ip可能是前缀或后缀甚至是中缀另一个会导致错误匹配.-F和-x的组合也可能更快一些,因为grep可以在很大程度上优化匹配.)

可能还有更多.就我而言.