使用 argparse、pexpect 和 docker 时如何在传递给 python 脚本的参数中保留引号?

问题描述 投票:0回答:1

我正在制作一个 python 脚本,通过使用 pexpect 创建子进程来包装对

docker run
的调用(pexpect 对我来说至关重要,因为命令可能是交互式的):

import argparse
import pexpect


parser = argparse.ArgumentParser()
parser.add_argument("some_positional_argument")
parser.add_argument("command", nargs=argparse.REMAINDER)

args = parser.parse_args()

command = " ".join(args.command)
docker_run = f"docker run -it --rm ubuntu {command}"
print(f"docker_run: {docker_run}")

child = pexpect.spawn(docker_run)
child.interact()
child.close()
exit(child.exitstatus)

问题是,当将带引号的 bash 命令传递给

command
参数时,Python 会将其解释为字符串值,因此引号将消失:

python3 t.py foo bash -c 'echo hi'
docker_run: docker run -it --rm ubuntu bash -c echo hi
    # empty line here, because 'bash -c echo' was actually called inside the container

我感兴趣的是,docker内部是如何解决这个问题的?因为

docker run
是类似的 CLI 程序,因此将收到相同的不带引号的
echo hi
。示例

docker run --rm -it ubuntu bash -c 'echo hi'
hi

效果很好。

我尝试查看 docker cli 源代码:

解析功能看起来很有前途:

但我没有找到参数的任何后期处理(例如添加引号)。

python bash docker argparse pexpect
1个回答
0
投票

pexpect.spawn()
具有接受命令和参数列表的形式。用这个。 切勿通过将字符串连接在一起来构造命令。

docker_args = ['run', '-it', '--rm', 'ubuntu'] + args.command
child = pexpect.spawn('docker', docker_args)

在您的示例中,

bash -c 'echo hi'
是三个shell单词,
bash
-c
echo hi
;主机 shell 会在程序看到单引号之前删除它(尝试打印出
args.command
)。保持参数列表形式意味着您无需担心可能需要实现哪种引号或转义:您有四个
docker
参数加上三参数命令,并将七个参数传递给
docker 
.

此处的字符串操作会让您面临 shell 注入攻击。如果命令包含“特殊”shell 字符或单引号,会发生什么?

./t.py foo \'word\' \; echo bar \> file

pexpect
文档表明它根本不会解释此标点符号或运行 shell,因此您可能会得到令人困惑的结果。值得注意的是
subprocess.call(shell=True)
will 运行 shell,这将导致您的程序在程序退出后运行该
echo
命令。使用列表语法可以避免这种歧义:您有一个包含
;
的列表项,它作为参数传递给命令,并且永远不会解释为其他任何内容。

还要考虑Docker SDK for Python是否可以满足您的需求。

client.containers.run()
采用与
docker run
类似的 Python 原生关键字参数,但与容器的 stdin/stdout 交互的相应
container.attach_socket()
方法使用起来可能会比较棘手。

© www.soinside.com 2019 - 2024. All rights reserved.