要从重复的库存表中收集同一单元格的内容,请将每张表导出为 CSV,然后运行一个 Finder 快捷指令来读取该位置并创建概览。本指南收集 B3,保留每个源文件名,并拒绝覆盖已有结果。

构建快捷指令前选择合适的输入

此工作流程适用于维护多张布局相同的库存表,并希望在单独的概览中为每张表生成一行的人。示例使用三个包含模拟计数的临时文件。每个文件代表一张导出的表;目标是 B 列第 3 行(包括标题行)。

请区分三件事:电子表格文档、文档中的表格,以及导出的 CSV 文件。Apple 的 Numbers 文档说明,CSV 可以按每张表分别导出为文件,也可以将多个表合并导出。对于此任务,请选择分别导出:合并表格可能会改变目标记录的位置。CSV 导出包含单元格值,因此生成的概览只是一个快照。在 Mac 上的 Numbers 中导出为其他文件格式。

下面的快捷指令会将提取工作交给一个小型 Python 程序。它不会把 Numbers 文档或 Excel 工作簿当作纯文本处理,也不会假设某个操作接受电子表格文件后就能访问其中的单元格。Apple 介绍了如何通过信息面板查看某个操作接受的输入和生成的输出。检查 Mac 上可用的操作时,请使用该面板。在 Mac 上的快捷指令中浏览操作列表。

开始前请参考此决策表:

起始情况 此工作流程所需的准备
每个 Numbers 文档中有一张匹配的表 将每张表导出为 CSV。
一个文档中有多张匹配的表 将表分别导出为 CSV 文件,并确认要使用的表。
已有逗号分隔的 CSV 文件 确认其编码、布局和目标位置。
布局不同或库存行位置会变化 先将临时示例统一为相同格式;仅靠固定坐标无法识别目标数量。

在示例中,每个源文件的 A3 都必须是 Counted units,而 B3 中必须是所需的值。检查相邻标签有助于诊断:如果行位置偏移,运行会停止,而不会悄悄收集无关的值。这无法发现所有可能的布局错误,因此准备输入时请确认这两个单元格。

准备临时库存文件

此示例需要单独安装 Python 3。使用 Python 网站提供的标准安装程序时,可执行文件链接会放在 /usr/local/bin/ 中。下面的命令会显式使用 /usr/local/bin/python3,避免依赖快捷指令的命令搜索路径。请按照受支持 Python 版本的安装说明完成安装,然后确认以下命令可以运行。在 macOS 上使用 Python。

/usr/local/bin/python3 --version

如果该路径不存在,请先完成 Python 安装再继续。此程序仅使用 Python 标准库,无需安装其他软件包。Apple 的终端指南说明,可以使用可执行文件的完整路径名并附上参数来调用命令。在 Mac 上的终端中执行命令和运行工具。

要创建模拟输入,请将以下完整命令粘贴到终端中。它会在桌面上创建 Inventory Demo,如果该文件夹已存在则会停止。请使用新建的临时文件夹,不要将这些示例合并到现有目录中。

/usr/local/bin/python3 - <<'PY'
import csv
from pathlib import Path

root = Path.home() / "Desktop" / "Inventory Demo"
root.mkdir()
examples = [
    ("Birch", "A", "12"),
    ("Cedar", "B", "7"),
    ("Maple", "C", "0"),
]
for name, bin_name, count in examples:
    with (root / f"{name}.csv").open(
        "x", encoding="utf-8", newline=""
    ) as handle:
        csv.writer(handle).writerows([
            ["Item", "Value"],
            ["Bin", bin_name],
            ["Counted units", count],
        ])
print(root)
PY

若要单独测试 Numbers 导出步骤,请在 Numbers 中打开一个临时 CSV,并使用 文件 > 导出到 > CSV 将其导出到另一个临时文件夹。为每张表分别创建文件,关闭 包括表格名称,并使用 UTF-8 编码。检查导出的表格,确认 A3 和 B3 仍包含预期的标签和值。Apple 说明了这些导出选项。Numbers 导出设置。

不要把电子表格的扩展名改成 .csv 来代替导出。此程序需要实际的逗号分隔文本。请将三个原始模拟 CSV 文件放在一起,以便按照后续步骤操作。

创建用于构建概览的程序

使用纯文本编辑器,将以下完整程序保存为 overview.py,放在 Inventory Demo 中。确认文件名以 .py 结尾,而不是 .py.txt。程序将源文件路径作为参数,读取 B3,并在这些文件所在的同一文件夹中创建 overview.csv。

import csv
import io
import sys
from pathlib import Path

ROW, COLUMN = 2, 1  # Zero-based: B3
LABEL = "Counted units"

def build_overview(arguments):
    if not arguments:
        raise ValueError("Select at least one source CSV.")
    paths = [Path(name).expanduser().resolve(strict=True)
             for name in arguments]
    if len(set(paths)) != len(paths):
        raise ValueError("The same source was supplied twice.")
    if len({path.parent for path in paths}) != 1:
        raise ValueError("Keep all source CSVs in one folder.")
    rows = []
    for path in sorted(paths, key=lambda p: p.name):
        if (not path.is_file() or path.suffix.lower() != ".csv"
                or path.name.casefold() == "overview.csv"):
            raise ValueError(f"Not a source CSV: {path.name}")
        with path.open(encoding="utf-8-sig", newline="") as handle:
            records = list(csv.reader(handle, strict=True))
        if len(records) <= ROW or len(records[ROW]) <= COLUMN:
            raise ValueError(f"{path.name}: B3 does not exist.")
        if records[ROW][0] != LABEL:
            raise ValueError(f"{path.name}: A3 must be {LABEL!r}.")
        value = records[ROW][COLUMN]
        status = "blank" if value.strip() == "" else "present"
        rows.append([path.name, "B3", value, status])
    buffer = io.StringIO(newline="")
    writer = csv.writer(buffer)
    writer.writerow(["Source file", "Cell", "Value", "Status"])
    writer.writerows(rows)
    return paths[0].parent / "overview.csv", buffer.getvalue()

def main(arguments):
    try:
        destination, content = build_overview(arguments)
        with destination.open("x", encoding="utf-8", newline="") as handle:
            handle.write(content)
    except (OSError, ValueError, csv.Error) as error:
        print(f"Stopped: {error}", file=sys.stderr)
        return 1
    print(f"Created: {destination}")
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))

此程序使用 CSV 解析器,而不是按逗号或换行符拆分文本。Python 的读取器会将字段作为字符串返回,其文档规定应使用 newline="" 打开 CSV 文件。这样可以处理包含逗号或嵌入换行符的带引号字段。程序会原样保留提取的值,不会求和或转换为数字。CSV 文件的读取和写入。

程序设置了几项有意为之的限制,使结果更容易核对。每个输入文件都必须存在,所有解析后的源文件路径必须位于同一文件夹中,并且不接受重复的解析后路径。程序按文件名排序,而不依赖 Finder 的选择顺序。它还会拒绝名为 overview.csv 的输入文件,以防意外将上一次的结果包含进来。

程序会先读取并检查所有源文件,再开始创建输出。如果缺少 B3,或 A3 中的标签不符合预期,运行就会停止。若单元格 B3 存在但为空,则生成的行会标记为 blank;如果值为 0,则仍会保留为 0,并标记为 present。因此,单元格不存在、单元格为空和数量为零会产生不同结果。

输出采用独占创建模式 "x"。Python 文档说明,如果目标文件已存在,此模式会失败。因此,程序既不会替换先前的概览,也不会自动创建带编号的文件名。实际写入过程中仍可能发生故障并留下不完整的新文件;如果写入出错后留下了文件,请先检查再重试。Python 的文件打开模式。

将程序连接到 Finder 快捷指令

在快捷指令中创建一个名为 Inventory Overview 的快捷指令。打开其 详细信息,启用 用作快速操作,然后选择 Finder。在输入配置中,保留 文件 处于启用状态,并关闭无关的内容类型。Apple 说明了接受文件或文件夹的快捷指令如何在 Finder 中使用,以及如何限制接受的输入。在 Mac 上工作时运行快捷指令、限制快捷指令的输入。

将无输入时的行为设为 停止并回应,并设置类似 请在 Finder 中选择源 CSV 文件。 的消息。这样可以避免空选择导致运行结果含糊不清。Apple 的输入配置指南介绍了该选项及其回应文本框。选择缺少输入时的回应方式。

添加 运行 Shell 脚本。将 Shell 设为 /bin/zsh,将其 输入 连接到 快捷指令输入,并将 传递输入 设为 作为参数。用以下完整命令替换示例脚本:

/usr/local/bin/python3 "$HOME/Desktop/Inventory Demo/overview.py" "$@"

保留引号。带引号的 "$@" 会分别传递传入的参数,从而保留包含空格的文件名。脚本路径也加了引号,原因相同。zsh 手册介绍了位置参数以及带引号数组元素的独立展开方式;Apple 的终端指南也说明了路径名加引号的用法。参数、在终端中指定文件和文件夹。

如果快捷指令要求启用脚本,请选择 快捷指令 > 设置 > 高级,然后选中 允许运行脚本。Apple 在关于允许快捷指令运行脚本的说明中介绍了此设置。快捷指令的高级设置。

配置后的流程是:选中的文件对象进入快捷指令,其路径成为 Shell 参数,Python 读取这些文件并写入概览。Shell 操作成功后返回的文本结果是一条 Created: 消息,其中包含目标路径名。该消息本身并不是 CSV 文件。由于程序已经创建了文件,因此不需要后续的保存操作。

在 Finder 中只选择 Birch.csv、Cedar.csv 和 Maple.csv。按住 Control 键并点按所选文件,然后选择 快速操作 > Inventory Overview。如果看不到该快捷指令,请使用 更多 > 自定 检查已启用的 Finder 扩展,然后重新检查其接受的输入类型。Apple 介绍了 Finder 快速操作的自定方式,以及如何将快捷指令添加到该菜单中。在 Mac 上的 Finder 中执行快速操作。

检查结果并处理重复运行

在 Numbers 中打开演示文件夹里的 overview.csv。使用提供的模拟文件时,内容应为:

Source file,Cell,Value,Status
Birch.csv,B3,12,present
Cedar.csv,B3,7,present
Maple.csv,B3,0,present

确认每个选定的源文件都对应一行输出,并检查文件名是否指向预期的表格。示例中的值仅用于说明,并非实测库存结果。程序会报告值是否存在,但不会检查它是否为有效的数量。

如果结果不同,请按以下顺序排查:

  1. Finder 中没有快捷指令: 检查其 Finder 设置、接受的 文件 输入以及启用的扩展。
  2. 找不到可执行文件或脚本: 确认 Python 路径正确,并且 overview.py 仍在指定的演示文件夹中。
  3. 缺少 B3 或 A3 错误: 在修改程序之前,检查源文件导出的记录和导出选项。
  4. 出现意外的空值: 检查 B3;程序会有意保留空值。
  5. 目标文件已存在: 重命名先前的临时概览,或将它移出源文件夹,然后再运行。

你也可以从终端调用同一快捷指令,以检查输入路径。Apple 说明可以使用 -i 传递文件输入;通过管道传递路径名时,传递的则是文本。请使用下面列出的明确文件,不要加输出标志,因为输出文件由 Python 程序负责创建。相同的名称冲突规则仍然适用。从命令行运行快捷指令。

shortcuts run "Inventory Overview" -i \
  "$HOME/Desktop/Inventory Demo/Birch.csv" \
  "$HOME/Desktop/Inventory Demo/Cedar.csv" \
  "$HOME/Desktop/Inventory Demo/Maple.csv"

每次生成的新概览都对应那次运行中选定的 CSV 文件。电子表格发生更改后,需要重新导出并再次运行。保留源文件名、检查相邻标签,并区分空值和零,可以让这个小型概览更容易核对,再用于下一次库存检查。

来源