终端可执行带子文件夹的mkdir命令,但Python脚本执行失败
你遇到的问题根本不是-p参数的问题,而是subprocess模块默认不会通过shell解析你的命令导致的。
在终端里执行mkdir -p algorithmic-toolbox/week_{1..6}/{slides,exercise,assignment}时,是你的shell(比如bash)先把大括号{1..6}和{slides,exercise,assignment}展开成实际的路径列表,再传给mkdir命令执行。但Python的subprocess.call()默认是直接把整个字符串当作一个可执行文件的名称去查找,它不会识别shell的通配符/扩展语法,所以才会报错说找不到那个长串的"文件或目录"——它以为你要运行一个叫mkdir -p algorithmic-toolbox/week_{1..6}/{slides,exercise,assignment}的程序,而不是运行mkdir并传参数。
解决方法一:让subprocess通过shell执行命令
最简单的修改是给call()加上shell=True参数,这样subprocess会调用系统shell来解析你的命令,和终端里的行为一致:
from subprocess import call import csv def csv_dict_reader(file_obj): """ Read a CSV file using csv.DictReader """ reader = csv.DictReader(file_obj, delimiter=',') for line in reader: sanitized_name = line["course"].replace(" ", "-").lower() command = 'mkdir -p ' + sanitized_name + '/week_{1..'+ line["week"] +'}/{slides,exercise,assignment}' call(command, shell=True) # 这里加上shell=True if __name__ == "__main__": with open("courses.csv") as f_obj: csv_dict_reader(f_obj)
⚠️ 注意:如果你的CSV文件内容来自不可信的来源(比如用户输入),使用shell=True会有命令注入风险,比如如果course字段里包含; rm -rf /这样的内容,会执行恶意命令。如果CSV是你自己维护的,这个方法没问题。
解决方法二:用Python原生方法创建目录(更安全)
为了避免shell注入的风险,推荐用Python的os.makedirs()或者pathlib模块来处理目录创建,自己实现大括号扩展的逻辑:
import csv import os from pathlib import Path def csv_dict_reader(file_obj): reader = csv.DictReader(file_obj, delimiter=',') for line in reader: sanitized_name = line["course"].replace(" ", "-").lower() max_week = int(line["week"]) # 遍历所有周数 for week_num in range(1, max_week + 1): week_dir = f"{sanitized_name}/week_{week_num}" # 遍历每个子目录类型 for sub_dir in ["slides", "exercise", "assignment"]: full_path = os.path.join(week_dir, sub_dir) # exist_ok=True 相当于mkdir的-p参数,已存在不会报错 os.makedirs(full_path, exist_ok=True) # 或者用pathlib的写法: # Path(full_path).mkdir(parents=True, exist_ok=True) if __name__ == "__main__": with open("courses.csv") as f_obj: csv_dict_reader(f_obj)
这个方法完全不需要依赖shell,所有路径都是Python自己生成的,更安全也更跨平台(比如Windows系统也能运行,而shell方法只能在类Unix系统生效)。
内容的提问来源于stack exchange,提问作者Vivek V Dwivedi

