需要在ssh之后运行多个md5操作到服务器

时间:2017-09-27 00:47:52

标签: python ssh paramiko

我目前需要在ssh进入服务器后对文件运行多个md5操作(> 15000)。但是我当前的实现非常慢,因为为每个md5操作创建了一个使用exec_command的新通道。使用paramiko有更好的方法吗?

def md5(fname):
    hash_md5 = hashlib.md5()
    if os.path.isfile(fname):
        with open(fname, "rb") as f:
            for chunk in iter(lambda: f.read(4096), b""):
                hash_md5.update(chunk)
    return hash_md5.hexdigest()

def connect_server(host1):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(host1)
    return ssh

for files in install_files:
    stdin, stdout, stderr = ssh.exec_command('md5 %s' % files)
    file_md5_map[file] = stdout.read().split()[0]

1 个答案:

答案 0 :(得分:0)

只调用exec_command一次的高性能实现(无论您有多少文件!)可能如下所示:

remote_script = '''
exec 2>/dev/null ## writes to stderr will block; suppress them.
bash -s <<'EOF'
  while IFS= read -r -d '' filename; do
    result=$(md5 "$filename");
    result=${result%%[[:space:]]*}
    printf '%s\t%s\0' "${result}" "${filename}"
  done
EOF
'''
stdin, stdout, stderr = ssh.exec_command(remote_script)

writer_thread = threading.Thread(
  target = lambda: stdin.write('\0'.join(install_files) + '\0')
)
writer_thread.start()

for datum in stdout.read().split('\0'):
    if len(datum) == 0: continue # should only happen for the last line
    (md5sum, filename) = datum.split('\t', 1)
    file_md5_map[filename] = md5sum

这会传递远程进程的stdin:

上的文件名流
/path/to/file1<NUL>/path/to/file2</NUL>

...并在其标准输出上返回一对配对的md5sums和文件名:

md5_of_file1<TAB>/path/to/file1<NUL>md5_of_file2<TAB>/path/to/file2<NUL>

这意味着您只需要一个远程进程将此输入流转换为输出,并且对具有令人惊讶或恶意名称的文件完全可靠:由于NUL字符不可能存在于文件名中(与换行符不同) ,通常用作分隔符但不应该),使用NUL分隔文件名是完全安全的。