Watch at Ease看见世界,读懂生活。

科技 · 实用指南

将 FileZilla 的 MLSD 清单转成 CSV,保留原文件名

保留名称中的逗号、分号与有效空格,区分缺失大小和零值,并在离线副本中保留目录项目。

展示单条 MLSD 项目分离属性与未改文件名的原创示意图
原创示意图:第一个空格分离属性,模拟名称内部的分号仍属于名称。 Watch at Ease

已复制 FileZilla 原始目录清单时,把所有分号替换成逗号,并不是可靠的 CSV 转换方法。分号可能属于文件名,而名称里的逗号或引号需要正确的 CSV 引用处理。

本指南离线转换单次采集的 MLSD 数据块,保留显示的名称、项目类型与原始属性。它不下载文件、不更改服务器、不建立递归清单,也不证明采集清单完整。

先确认复制的是 MLSD 数据

使用已有的合法 FTP 或 FTPS 连接。FileZilla 原支持讨论提到在调试设置中显示原始目录清单;若你的版本提供该控件,可启用并只刷新目标文件夹。在日志中确认 MLSD 命令及成功完成,另记远程文件夹与采集时间。我们未测试当前 FileZilla 界面。

只把该次成功清单的数据项目复制到 UTF-8 纯文本文件 mlsd.txt。典型项目为 type=file;size=926; image.jpg。保留名称的所有字符,包括首尾空格。转换器接受原始项目,或准确的英文前缀 Listing: 后跟一个空格;不要夹入时间戳、翻译后的前缀、状态消息或空白分隔行。

若响应是 Unix/DOS LIST 文本、SFTP 清单、CLI JSON 对象、未解码字节或明显乱码,停止转换。不要猜编码或覆盖原日志。现代 FileZilla CLI 也提供结构化 JSON,这对已有 CLI 的用户是有效的另一路径,但不是本转换器的输入。

在 Linux Bash 终端转换副本

在 Linux Bash 终端使用 Python 3.9 或更高版本,进入 mlsd.txt 所在文件夹。把下一条命令复制为一整行,保留直引号与反斜杠,选择新的输出名。命令先验证所有输入记录,再独占创建 mlsd.csv;输入文件不变。

python3 -c "exec('import csv\\nimport sys\\n\\nrows = []\\nwith open(sys.argv[1], encoding=\"utf-8-sig\", newline=\"\") as source:\\n for number, raw in enumerate(source, 1):\\n line = raw.removesuffix(\"\\\\n\").removesuffix(\"\\\\r\")\\n if line.startswith(\"Listing: \"):\\n line = line[len(\"Listing: \"):]\\n facts, separator, name = line.partition(\" \")\\n if not separator or not name or (facts and not facts.endswith(\";\")):\\n raise ValueError(f\"Line {number}: expected one MLSD data entry\")\\n values = {}\\n for fact in facts[:-1].split(\";\") if facts else []:\\n key, equals, value = fact.partition(\"=\")\\n key = key.lower()\\n if not key or not equals or key in values or any(c.isspace() for c in fact):\\n raise ValueError(f\"Line {number}: malformed or duplicate fact\")\\n values[key] = value\\n size = values.get(\"size\", \"\")\\n if \"size\" in values and not (size.isascii() and size.isdecimal()):\\n raise ValueError(f\"Line {number}: invalid size\")\\n rows.append([name, values.get(\"type\", \"\"), size, values.get(\"modify\", \"\"), facts])\\nif not rows:\\n raise ValueError(\"No MLSD records: do not assume an empty directory\")\\nwith open(sys.argv[2], \"x\", encoding=\"utf-8-sig\", newline=\"\") as destination:\\n writer = csv.writer(destination)\\n writer.writerow([\"name\", \"type\", \"size_bytes\", \"modify_raw\", \"facts_raw\"])\\n writer.writerows(rows)\\nprint(len(rows), \"MLSD records exported; includes any directory entries\")\\n')" "mlsd.txt" "mlsd.csv"

五列依次为 name、type、size_bytes、modify_raw、facts_raw。第一个空格分开属性和名称;后续空格及文件名分号保持原样。属性名称按不区分大小写匹配,facts_raw 则保留原拼写、顺序和值。缺失大小留空,返回的零保留为 0。

按文本导入并核验采集记录

在 LibreOffice Calc 打开 CSV,选择 UTF-8、逗号分隔和直双引号文本分隔符。关闭“合并分隔符”“修剪空格”“计算公式”,五列都设为“文本”。导入窗口不出现时,打开时启用“编辑筛选器设置”。检查以 = 开头的名称仍是原文本,没有变成公式。

比较命令打印的数量与解析后的 CSV 数据行数,不含表头,并对照保存文本核验多个准确名称和属性。保留 cdir、pdir、dir 记录:这些描述目录,因此记录数不等于文件数。缺失 type 仍是未知。服务器可能为同一路径返回多种类型,所以重复名称也保留,不自动去重。

理解错误及元数据的证明范围

空输入、格式错误、重复属性键或无效的已提供大小,会在创建输出前停止。目标已存在时换新文件名。磁盘或写入失败可能留下不完整 CSV;弃用该输出,换新名称重跑并核对解析行数。即使服务器确实没返回项目,本转换器也会主动拒绝认证空清单。

大小是服务器报告的元数据,可能为近似值;缺失大小不是零,目录大小不是递归总量。modify_raw 保留未转换的服务器文本,不证明内容未变。解析器无法检测复制时遗漏的语法正确行。需自行确认采集成功、保留原日志,并只分享你愿意披露的元数据。

我们对该准确 Bash 命令实际运行了 17 项模拟子进程检查,覆盖标点、Unicode、有效空格、可选属性、目录类型、无效输入与拒绝覆盖。未运行真实 FileZilla、FTP 服务器或 Calc 界面,也未测试 Windows 或 macOS。两张配图是模拟示例,不是界面截图。

区分缺失大小、零值及保留目录类型的原创示意图
原创示意图:缺失大小留空,返回的零与目录项目类型分别保留。 Watch at Ease