加速重复的 python 调用(或者,将复杂的正则表达式移植到 sed)

加速重复的 python 调用(或者,将复杂的正则表达式移植到 sed)

我是一名学术医学物理学家。我做的实验会产生大量数据,但运行起来很昂贵。我的大学有一个备份系统,由位于废弃盐矿中的机器人磁带库组成,该系统使用 IBM 的频谱保护(调用为dsmc)我用于异地备份。虽然我可以发送到盐矿的总大小没有限制,但是每天传输限制为 200 GB。据我所知,没有办法让 Spectrum Protect 客户端遵守此限制,并在达到传输限制后停止。

如果突破了这个限制,服务器就会锁定该节点,我必须向某人发送一封充满歉意的电子邮件,要求他们解锁它。他们因为我使用了太多带宽而责备我,并且大约 24-48 小时后,解锁节点。

为了解决我以离散块(在实验日)创建数据并且每月或每周都远低于带宽限制的事实,我编写了一个简单的包装器脚本来解析dsmc和终止的输出如果传输太大。

解析是通过dsmc使用简单的 python 脚本将 bash 中的输出视为此处文档来完成的:

#!/bin/bash
# A silly wrapper script to halt TSM backups
#
# Usage: sudo /path/to/script /path/to/backup/location 
# 
# Requires python3 accessible as python3, and the regex / os modules. 
# Tested on MacOS and Linux 
BYTES_SENT=0;
#MAX_SIZE_TO_SEND=150 #Bytes, for testing  
MAX_SIZE_TO_SEND=$[185*(2**30)] 

args=("$@")
sudo rm -f /tmp/dsmc-script.PID

function outputParser() { 
    python3 <<'EOF'
import os, re
rex=re.compile(r"Normal File\-\-\>\s*?([,0-9]*,?)\s*?\/")
valueToParse=os.environ.get('line');
match=rex.match(valueToParse);
try:
    stringToReturn = str(match.group(1));
    stringToReturn =stringToReturn.replace(',','');
except AttributeError:
    stringToReturn = "";
#Check for failed transfers 
failedResults = re.findall(r"\*\* Unsuccessful \*\*", valueToParse); 
nFailedResults = len(failedResults); 
if (nFailedResults >0):
    stringToReturn = ""; 
print(stringToReturn);
EOF
} #I am sure that the above is a one-liner in sed or awk. I just don't know what the one line is. 

function trapCaught() { 
    #Do cleanup, not shown     
    echo ", quitting."
}

trap trapCaught sigint
killCount=0 
startTime=$SECONDS

while read -r line; do  
    echo "$line"
    export line; 
    X=$(export line=$line; outputParser)
    if [[ ! -z "$X" ]]; then
        BYTES_SENT=$[$BYTES_SENT + $X]
        echo "Sent $X bytes, $BYTES_SENT in total"
    fi
    if (( BYTES_SENT > MAX_SIZE_TO_SEND )); then
        if (( killCount < 1)); then 
            echo "STOPPED BACKUP BECAUSE $BYTES_SENT is GREATER THAN THE PERMITTED MAXIMUM OF $MAX_SIZE_TO_SEND"; 
            killStartTime=$(( SECONDS - startTime ))
            pid=$(cat /tmp/dsmc-script.PID)
            echo "PID is $pid"
            echo $pid | sudo xargs kill 
        fi 

        killCount=$[$killCount + 1]; 
        timeKillNow=$(( SECONDS - killStartTime ))
        rm -f /tmp/dsmc-script.PID

        if (( killCount > 100 || timeKillNow > 30  )); then 
            echo "Taking too long to die; retrying" 
            echo $pid | sudo xargs kill -9;
            sleep 0.1; 
            sudo kill -9 0; 
        fi

    fi
done < <( sudo dsmc incr ${args[0]} &  echo $! > /tmp/dsmc-script.PID  ) 

这有效,并且适合我的目的。然而,性能很差,接近可怕,我认为这是因为循环的每次迭代while都会产生 python 解释器/脚本组合的另一个实例。

鉴于我无法更改限制或二进制编译的 blob 的行为dsmc,我有三个相关问题:

(a)这是解决这个问题的明智方法,还是有我缺少的更简单的方法,例如高级巫术netstat

(b) 鉴于 python实际上确实如此在循环中的每次迭代中本质上是完全相同的,有没有一种方法可以缓存解释器对代码的翻译,从而极大地加快整个过程?

(c) 如果我用等效的sedawk构造替换 python 脚本,我怀疑整个事情会快得多。为什么?是否可以轻松地进行这种类型的算术,或者这是另一个转移注意力的事情?

编辑:对于那些不熟悉的人来说,示例输出dsmc如下 - 仅当“普通文件”出现在字符串中时才会发送文件,后跟其大小(以字节为单位)。因此,在下面,文件spclicert.kdb被发送,但既不发送TSM.PWD,也不发送目录CaptiveNetworkSupport

# dsmc incr / 
< header message containing personal information> 
Incremental backup of volume '/'
ANS1898I ***** Processed    79,000 files *****
Directory-->                   0 /Library/Preferences/SystemConfiguration/CaptiveNetworkSupport [Sent]
Normal File-->             5,080 /Library/Preferences/Tivoli Storage Manager/Nodes/SHUG2765-MACBOOKPRO-PHYSICS/spclicert.kdb [Sent]
Updating-->                  224 /Library/Preferences/Tivoli Storage Manager/BrokenOrOld/TSM.PWD (original) [Sent]

因此,上面的脚本去掉了发送的每个文件的大小(以字节为单位),然后简单地将它们相加。

答案1

假设连接是可靠的,一个简单的办法就是使用用户空间流量整形器。只需将其设置为每天使用的带宽不超过最大即可。

一个使用的例子trickle,一个大文件, 和scp:

l=$(( (200*10**6)/(24*60**2) ))
trickle -d $l    scp foo username@remotehost:~/

并且trickle会减慢传输速度2314K每秒,最高速度不超过199,929,600,000每天字节。文件传输程序不必是scp,它可以是任何东西(甚至是网络浏览器),(或dsmc),只是从命令行启动。

此方法的优点是无需分解文件如果大于每日限额。当然发送需要一段时间结束,(如果如果是 1TB,则需要 5 天),但无论如何也需要那么长时间。

trickle有一个名为 的守护程序版本trickled,它控制“trickle”的每次后续运行。例子:

l=$(( (200*10**6)/(24*60**2) ))
trickled -d $l
trickle    scp foo username@remotehost:~/ &
trickle    scp bar username@remotehost:~/ &
trickle    scp baz username@remotehost:~/ &

假设每个文件,酒吧, 和巴兹1TB在规模上,trickled仍将转移保持在200GB/天限制。

答案2

您的输入可以完全在bash.这是一个示例:

max=$[185*(2**30)]

export total=0
while read first second third rest; do
    [[ "$first" == "Normal" && "$second" == "File-->" ]] && {
        size=${third//,/}
        echo "file: $size"
        total=$(( total + size ))
        (( total > max )) && kill something
    }
done < ~/tmp/your-input

如果您确实受到生成子进程所需时间的限制,那么这甚至可以避免调用awk或的开销sed

相关内容