如果合并后的 CSV 在各个批次之间重复出现列标题,您可以创建一个访达(Finder)快速操作,保留第一行并删除后续完全匹配的行。以下操作会在每个选定文件旁边生成一个独立的 .cleaned.csv 文件,同时保持原文件不变。它适用于每条记录占一行的普通 CSV 导出文件;但并不适用于解析所有合法的 CSV 格式。

该快速操作的功能

该工作流程比较的是整行内容,而非单个 CSV 字段。它将第一行保存为表头,复制到输出文件中,然后跳过后续与该表头完全相同的任何行。其他行均保持其原始顺序,包括重复的数据行。当导出的批次在记录之间插入了相同的标题时,这是一种非常实用的选择。

该方法通过自动操作(Automator)的运行 Shell 脚本操作来调用 macOS 内置的 awk。Apple 官方提供了创建访达快速操作以及向自动操作工作流程中添加 Shell 脚本的说明文档。创建快速操作工作流程 · 在自动操作工作流程中使用 Shell 脚本操作

创建访达快速操作

  1. 打开自动操作(Automator),选取文件 > 新建,选择快速操作,然后点按选取。
  2. 在工作流程顶部,将工作流程收到当前设置为文件或文件夹,并将应用程序设置为访达(Finder)。
  3. 在操作资源库中找到运行 Shell 脚本,并将其拖入工作流程中。
  4. 将 Shell 设置为 /bin/zsh,将传递输入设置为作为自变量。将以下脚本粘贴到该操作中。
for file in "$@"; do
    if [ ! -f "$file" ]; then
        printf 'Not a regular file: %s\n' "$file" >&2
        exit 1
    fi

    output="${file}.cleaned.csv"
    if [ -e "$output" ]; then
        printf 'Output already exists; skipped: %s\n' "$output" >&2
        exit 1
    fi

    temporary=$(mktemp "${file}.tmp.XXXXXX") || exit 1
    if awk 'NR == 1 { header = $0; print; next }
            $0 != header { print }' "$file" > "$temporary"; then
        if ! mv "$temporary" "$output"; then
            rm -f "$temporary"
            exit 1
        fi
    else
        rm -f "$temporary"
        exit 1
    fi
done

将该工作流程保存为类似于 Remove Repeated CSV Headers 的名称。临时文件会创建在源文件同级目录下,处理完成后再重命名为输出文件名。如果该输出文件已存在,脚本将停止运行而不是将其覆盖。源输入文件绝不会被执行写入操作。

运行并检查结果

在“访达”中,选择一个或多个 CSV 文件,打开右键快捷菜单中的快速操作,然后选取 Remove Repeated CSV Headers。在每个源文件旁边查找以 .cleaned.csv 结尾的文件。在文本编辑器或电子表格程序中打开生成的文件,检查它是否仍然保留了一个表头,且每个重复表头前后的数据行是否依然存在。该工作流程不会显示自定义的完成提示信息;如果未生成文件,可能是因为输出文件已存在或处理过程中发生了错误。

此匹配为完全匹配:如果表头的大小写、空格、标点符号或引号有所不同,都将被视为不同的行而予以保留。这里的限制是有意为之;这可以避免在文件文本存在差异时去盲目猜测哪些行算作表头。

了解该基于行的方法何时不适用

CSV 中可能包含带有嵌入换行符的带引号字段。在这种情况下,一条逻辑 CSV 记录可能会跨越多个物理行,而 awk 是逐行处理输入的。这样一来,此脚本可能会将字段内的某一行误判为一条记录,或者破坏文件结构。Apple 的 macOS awk 手册详细说明了其面向行的处理机制;对于多行 CSV,请改用支持该文件格式的专门 CSV 处理工具。macOS awk 手册

此外,该快速操作不会标准化空白字符、删除近似匹配项、检查列名或合并文件。它只适用于特定场景的清理:从基于行的 CSV 导出文件中删除与首个物理行完全重复的行,同时保留源文件以供检查。

参考资料