在 python 脚本中正确使用 ssh 和 sed os.system
Using ssh and sed within a python script with os.system properly
我正在尝试 运行 python 脚本中的 ssh 命令,使用 os.system
在远程完全匹配的字符串末尾添加 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密钥,主机名等),输出看起来像:
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'
那是correct/desired输出;有趣的 '"'"'
习语是如何在 POSIX-compliant shell.
中的 single-quoted 字符串中安全地注入文字单引号
有什么不同?手数:
- 我们正在生成我们想要 运行 作为数组 的命令,并在必要时让 Python 完成将这些数组转换为字符串的工作.这避免了 shell 注入攻击,这是一种非常常见的 class 安全漏洞。
- 因为我们自己生成列表,所以我们可以更改引用每个列表的方式:我们可以在适当的时候使用 f-strings,在适当的时候使用原始字符串,等等。
- 我们不会将
~
传递给远程服务器:这是多余且不必要的,因为 ~
是 SSH 会话开始的默认位置;以及我们正在使用的安全预防措施(以防止值被 shell 解析为代码)防止它产生任何影响(作为 ~
的替换为 [=17= 的活动值] 不是由 sed
本身完成的,而是由调用它的 shell 完成的;因为我们根本没有调用任何本地 shell,所以我们还需要使用 os.path.expanduser
使 ~/.ssh/my-ssh-key
中的 ~
得到尊重)。
- 因为我们没有使用原始字符串,所以我们需要将
\b
中的反斜杠加倍,以确保 Python.[=41= 将它们视为文字而非句法。 ]
- 至关重要的是,我们绝不会在任何 shell 本地或远程 .[=41= 都可以将数据解析为代码的上下文中传递数据]
我正在尝试 运行 python 脚本中的 ssh 命令,使用 os.system
在远程完全匹配的字符串末尾添加 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密钥,主机名等),输出看起来像:
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'
那是correct/desired输出;有趣的 '"'"'
习语是如何在 POSIX-compliant shell.
有什么不同?手数:
- 我们正在生成我们想要 运行 作为数组 的命令,并在必要时让 Python 完成将这些数组转换为字符串的工作.这避免了 shell 注入攻击,这是一种非常常见的 class 安全漏洞。
- 因为我们自己生成列表,所以我们可以更改引用每个列表的方式:我们可以在适当的时候使用 f-strings,在适当的时候使用原始字符串,等等。
- 我们不会将
~
传递给远程服务器:这是多余且不必要的,因为~
是 SSH 会话开始的默认位置;以及我们正在使用的安全预防措施(以防止值被 shell 解析为代码)防止它产生任何影响(作为~
的替换为 [=17= 的活动值] 不是由sed
本身完成的,而是由调用它的 shell 完成的;因为我们根本没有调用任何本地 shell,所以我们还需要使用os.path.expanduser
使~/.ssh/my-ssh-key
中的~
得到尊重)。 - 因为我们没有使用原始字符串,所以我们需要将
\b
中的反斜杠加倍,以确保 Python.[=41= 将它们视为文字而非句法。 ] - 至关重要的是,我们绝不会在任何 shell 本地或远程 .[=41= 都可以将数据解析为代码的上下文中传递数据]