python - 在带有 os.system 的 python 脚本中正确使用 ssh 和 sed

标签 python regex ssh sed os.system

我正在尝试使用 os.system 在 python 脚本中运行 ssh 命令添加 0在远程服务器中使用 ssh 的完全匹配字符串的末尾和 sed .
我有一个名为 nodelist 的文件在远程服务器中,它是一个看起来像这样的列表。

test-node-1
test-node-2
...
test-node-11
test-node-12
test-node-13
...
test-node-21
我想用sed做如下修改,我要搜索test-node-1 ,并且当找到完全匹配时,我想在末尾添加一个 0,文件必须最终看起来像这样。
test-node-1 0
test-node-2
...
test-node-11
test-node-12
test-node-13
...
test-node-21
但是,当我运行第一个命令时,
hostname = 'test-node-1'
function = 'nodelist'

os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/{hostname}/s/$/ 0/' ~/{function}.txt\"")
结果变成了这样,
test-node-1 0
test-node-2
...
test-node-11 0
test-node-12 0
test-node-13 0
...
test-node-21
我尝试像这样在命令中添加一个\b,
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/\b{hostname}\b/s/$/ 0/' ~/{function}.txt\"")
该命令根本不起作用。
我必须手动输入节点名称,而不是像这样使用变量,
os.system(f"ssh -i ~/.ssh/my-ssh-key username@serverlocation \"sed -i '/\btest-node-1\b/s/$/ 0/' ~/{function}.txt\"")
使我的命令起作用。
我的命令有什么问题,为什么我不能做我想让它做的事情?

最佳答案

此代码存在严重的安全问题;修复它们需要从头开始重新设计。让我们在这里这样做:

#!/usr/bin/env python3
import os.path
import shlex  # note, quote is only here in Python 3.x; in 2.x it was in the pipes module
import subprocess
import sys

# can set these from a loop if you choose, of course
username = "whoever"
serverlocation = "whereever"
hostname = 'test-node-1'
function = 'somename'

desired_cmd = ['sed', '-i',
               f'/\\b{hostname}\\b/s/$/ 0/',
               f'{function}.txt']
desired_cmd_str = ' '.join(shlex.quote(word) for word in desired_cmd)
print(f"Remote command: {desired_cmd_str}", file=sys.stderr)

# could just pass the below direct to subprocess.run, but let's log what we're doing:
ssh_cmd = ['ssh', '-i', os.path.expanduser('~/.ssh/my-ssh-key'),
           f"{username}@{serverlocation}", desired_cmd_str]
ssh_cmd_str = ' '.join(shlex.quote(word) for word in ssh_cmd)
print(f"Local command: {ssh_cmd_str}", file=sys.stderr)  # log equivalent shell command
subprocess.run(ssh_cmd) # but locally, run without a shell
如果您运行此程序(最后的 subprocess.run 除外,这需要真正的 SSH key 、主机名等),输出如下所示:
Remote command: sed -i '/\btest-node-1\b/s/$/ 0/' somename.txt
Local command: ssh -i /home/yourname/.ssh/my-ssh-key whoever@whereever 'sed -i '"'"'/\btest-node-1\b/s/$/ 0/'"'"' somename.txt'
这是正确/期望的输出;有趣的'"'"' idiom 是在符合 POSIX 的 shell 中安全地将文字单引号注入(inject)到单引号字符串中的方式。

有什么不同?很多:
  • 我们正在生成我们想要作为数组运行的命令,并让 Python 在必要时将这些数组转换为字符串。这避免了外壳注入(inject)攻击,这是一种非常常见的安全漏洞。
  • 因为我们自己生成列表,所以我们可以更改引用每个列表的方式:我们可以在合适的时候使用 f 字符串,在合适的时候使用原始字符串,等等。
  • 我们没有通过 ~到远程服务器:这是多余且不必要的,因为 ~是 SSH session 开始的默认位置;并且我们使用的安全预防措施(防止值被 shell 解析为代码)防止它产生任何影响(因为 ~ 的事件值替换 HOME 不是由 sed 完成的。本身,而是通过调用它的 shell;因为我们根本没有调用任何本地 shell,所以我们还需要使用 os.path.expanduser 来使 ~ 中的 ~/.ssh/my-ssh-key 得到尊重)。
  • 因为我们没有使用原始字符串,所以我们需要将 \b 中的反斜杠加倍。以确保它们被 Python 视为文字而不是语法。
  • 至关重要的是,我们永远不会在可以被任何 shell(本地或远程)解析为代码的上下文中传递数据 .
  • 关于python - 在带有 os.system 的 python 脚本中正确使用 ssh 和 sed,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63865912/

    相关文章:

    linux - 无法使用 jenkins 在 ubuntu 上运行远程脚本

    bash - Ubuntu:sshpass 运行远程 bash 脚本

    python - 在 numpy 或 scipy 中左逆?

    python - 只匹配IP地址,不匹配其他数字

    regex - 捕获组在 Select-String 的 -Pattern 末尾不起作用

    ruby - 这个正则表达式如何拆分第一个元音?

    python - 在 Python 中延迟初始化的字符串

    python - 将 JSON 键值分解为新行

    Java 相当于 Pythons urllib.urlencode(基于 HashMap 的 UrlEncode)

    linux - 如何从 expect 脚本中传递的 shell 'read' 命令返回?