如何将CSV文件作为stdin传入Docker容器并处理?
我需要运行一个Docker容器,实现读取CSV文件、转置DataFrame并输出CSV的功能,但不清楚怎么把CSV文件通过stdin传入容器读取。
我试过两个命令,都报错:
执行命令:
docker run --rm -i -v C:\Users\EngineerA\containerpython\container.py -v C:\Users\EngineerA\containerpython\file.csv/file.csv container python C:\Users\EngineerA\containerpython\container.py file.csv
错误信息:can't find '__main__' module in '//C:\Users\EngineerA\containerpython\container.py执行命令:
cat ./file.csv | docker run -i container
错误信息:can't open file '//container.py': [Errno 2] No such file or directory
我的代码
Dockerfile
FROM python:latest ENV PYTHONDONTWRITEBYTECODE=1 # Turns off buffering for easier container logging ENV PYTHONUNBUFFERED=1 RUN pip install pandas WORKDIR /app COPY . /app CMD ["python", "container.py"]
container.py
import argparse import contextlib import io from typing import IO import sys import pandas as pd def main(): parser = argparse.ArgumentParser() parser.add_argument('FILE') args = parser.parse_args() with file_ctx(args.FILE) as input_file: data = pd.read_csv(input_file, index_col=0) df = data.transpose() df.to_csv("C:\Users\EngineerA\containerpython\out.csv", encoding='utf-8', index=False) @contextlib.contextmanager def file_ctx(filename: str) -> IO[bytes]: if filename == '-': yield io.BytesIO(sys.stdin.buffer.read()) else: with open(filename, 'rb') as f: yield f if __name__ == "__main__": main()
问题根源
- 第一个命令的
-v参数完全用错:你把本地文件直接当卷挂载,路径格式混乱,容器内根本找不到指定的Python脚本;而且容器内不能用本地Windows路径,得用容器内部的路径。 - 第二个命令没给脚本传参数:你的
container.py要求必须传入FILE参数,你只启动了容器但没传参,导致脚本逻辑出错。另外输出路径写的是本地Windows路径,容器里根本不存在这个目录。
修正步骤
1. 先改脚本的输出逻辑
把硬编码的本地输出路径改成输出到stdout,这样更适合流式处理,也不用纠结容器内的路径问题:
修改container.py里的to_csv行:
df.to_csv(sys.stdout, encoding='utf-8', index=False)
2. 用stdin传入CSV的正确命令
你的脚本已经支持用-代表读取stdin,所以执行时要给脚本传这个参数:
- Linux/macOS终端:
cat ./file.csv | docker run -i container python container.py - > ./out.csv
- Windows PowerShell:
Get-Content .\file.csv | docker run -i container python container.py - > .\out.csv
最后面的> ./out.csv是把容器输出的内容重定向到本地文件。
3. 备选方案:用卷挂载本地文件(不用stdin)
如果不想用流式处理,也可以把本地文件挂载到容器里,命令格式要正确:
docker run --rm -v C:\Users\EngineerA\containerpython:/app/data container python container.py /app/data/file.csv
同时要把脚本里的输出路径改成/app/data/out.csv,这样本地的containerpython文件夹里就能看到输出文件。
最终优化后的container.py(推荐流式版本)
import argparse import contextlib import io from typing import IO import sys import pandas as pd def main(): parser = argparse.ArgumentParser() parser.add_argument('FILE', help='输入文件路径,-代表从stdin读取') args = parser.parse_args() with file_ctx(args.FILE) as input_file: data = pd.read_csv(input_file, index_col=0) df = data.transpose() # 输出到stdout,方便重定向到本地文件 df.to_csv(sys.stdout, encoding='utf-8', index=False) @contextlib.contextmanager def file_ctx(filename: str) -> IO[bytes]: if filename == '-': yield io.BytesIO(sys.stdin.buffer.read()) else: with open(filename, 'rb') as f: yield f if __name__ == "__main__": main()
内容的提问来源于stack exchange,提问作者Eliot Kim

