sh: 1:语法错误:未终止的引号字符串n



我正在制作django框架中的在线裁判,我试图通过子进程在docker容器内运行c++程序。这是我得到错误

的行
x=subprocess.run('docker exec oj-cpp sh -c 'echo "{}" | ./a.out ''.format(problem_testcase.input),capture_output=True,shell=True)

这里的oj-cpp是我的docker容器名称和problem_testcase.input是c++文件

的输入c++代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
int t;
while (t--)
{
int a, b;
cin >> a >> b;
cout << a + b << endl;
}
return 0;
}

problem_testcase.input:

2
4
5
3
5

错误:

CompletedProcess(args='docker exec oj-cpp sh -c 'echo "2n4n5n3n5" | ./output'', returncode=2, stdout=b'', stderr=b'2: 1: Syntax error: Unterminated quoted stringn')

我没有得到什么是错误的是在我的代码

subprocess.call(shell=True)有一些严重的安全问题,我尽量避免使用它。打开应用程序到一个壳注入攻击。你实际上在你的代码中看到:如果problem_testcase.input中的字符串包含任何对shell有意义的字符,shell将解释它们,这可能导致它做意想不到的事情。

在您的输入中,Python展开n换行符,然后将它们交给它启动的主机shell。所以shell命令是

docker exec oj-cpp sh -c 'echo "2
4
5
3
5" | ./a.out '

, shell在第一个换行符处停止,并尝试将命令运行到那里,这会产生错误。如果可以控制输入字符串,并且您知道docker命令可以工作,那么使用它来接管整个主机实际上是很简单的。


这里要知道的重要事情是docker exec将自己的stdin传递给它正在运行的程序。所以你不需要echo input | program语法;因为你不需要那个,你不需要sh -c调用;因为你只运行一个固定的命令,没有替换,你不需要shell=True

x = subprocess.run(['docker', 'exec', 'oj-cpp', './a.out'],
input=problem_testcase.input,
capture_output=True)

我可能会对这个程序做另外两个更改。

Docker SDK for Python提供了一个更直接的访问Docker功能,而不使用subprocess。它不需要docker二进制文件,但它仍然需要Docker守护进程正在运行,并且您需要有访问它的权限。

我也会避免编写docker exec。如果将容器看作单个进程的包装器,那么docker exec在该进程中启动第二个进程,这有点奇怪。如果你docker run一个新的(临时的)容器,它在一个已知的状态下启动,并且减少了不同程序调用相互干扰的风险。

import docker
client = docker.from_env()
# Start the container (`docker run -d -i some_image`)
container = client.containers.run(some_image, detach=True, stdin_open=True)
# Send the input string into the container
socket = container.attach_socket()
socket.send(problem_testcase.input.encode('utf-8'))
socket.close()
# Let the program run, collect its output, clean up
container.wait()
output = container.logs()
container.remove()