要在现有 Mac 文本文件的指定行插入文本,请使用“快捷指令”的 Shell 操作创建一个编辑后的副本,检查副本,然后在 Finder 中重命名。本指南使用一次性示例,在第 3 行之前插入一行。

此处的任务是在小型本地纯文本文件中添加一行,同时保持周围各行的顺序。你将创建一个 Finder 快捷指令,该快捷指令只接受一个文件。插入文本和行号都写在脚本中,因此可以在一次性副本上轻松重复相同的编辑。

“在第 3 行插入”是什么意思?

本文中的插入是指将新文本放在现有编号行的前面。原文件有三行时,在第 3 行之前插入 Birch 会得到四行:

位置 原文件 编辑后的文件
1 Cedar Cedar
2 Maple Maple
3 Willow Birch
4 — Willow

一行是由换行符分隔的记录,而不是编辑器中因视觉换行而产生的行。空行也计入行数。此示例使用普通的 UTF-8 纯文本、LF 换行符,并且最后一行后有一个换行符。本练习请保持这些条件;富文本文件以及使用其他编码或换行符的文件需要采用不同的处理方案。

执行编辑的命令是 macOS sed。它的数字地址从 1 开始计数行号,而它的 i 命令会在指定行前写入插入文本。该命令通常会将转换后的内容写入标准输出。这些有文档说明的行为使快捷指令能够创建单独的结果文件。Apple 发布的 sed 手册

创建一次性文本文件

打开“终端”并粘贴以下完整的设置代码块。它会在桌面上创建一个名为 Line Insert Demo 的文件夹,以及一个名为 notes.txt 的三行文件。如果该文件夹名称已经存在,设置过程会停止;请先选择一个新的文件夹名称再重试。

/bin/sh <<'SH'
set -e
demo_dir="$HOME/Desktop/Line Insert Demo"
/bin/mkdir "$demo_dir"
printf '%s\n' 'Cedar' 'Maple' 'Willow' > "$demo_dir/notes.txt"
printf 'Created: %s\n' "$demo_dir/notes.txt"
SH

创建文件夹时有意不使用允许目录已存在的选项。只有成功创建新文件夹后,才会执行写入文件的步骤,因此此设置不会覆盖之前的示例。Apple 的 Shell 文档介绍了如何使用 printf 和输出重定向创建文本文件。Shell 输入与输出

请将本练习限制在这个一次性文件夹中。在 Finder 中打开桌面并找到 Line Insert Demo;创建快捷指令后,你将选择其中的 notes.txt 文件。

创建输入类型正确的 Finder 快捷指令

在“快捷指令”中创建一个名为 Insert Birch Before Line 3 的快捷指令。Apple 的文档介绍了如何使用工具栏中的“添加”按钮创建快捷指令、为其命名,以及从边栏添加操作。在 Mac 上创建自定义快捷指令

按以下方式配置:

  1. 打开快捷指令的 Details,启用 Use as Quick Action,并选择 Finder。
  2. 在快捷指令的输入标题中,将接受的内容改为 Files,并关闭其他类型。
  3. 添加 Run Shell Script 操作。
  4. 将其 Shell 设为 /bin/sh,将 Input 设为 Shortcut Input,并将 Pass Input 设为 as arguments。
  5. 将操作中的示例脚本替换为下一节中的完整程序。

Apple 的文档介绍了如何启用 Finder 快捷指令,以及如何限制它们接受的输入类型。Files 类型包含许多种文稿,因此此设置会让快捷指令能够接收文件;它不会验证文件的文本格式。脚本会添加更严格的检查。在 Mac 上工作时运行快捷指令,限制快捷指令的输入,了解输入类型

输入链路很重要:Finder 将文件传给“快捷指令”,而 Shell 操作会将文件路径作为参数传递,脚本中可通过 $1 访问该参数。不要在 Shell 操作之前添加读取文本的操作:此程序需要文件路径,以便读取现有文件并为相邻的输出文件选择名称。

Apple 确认,“快捷指令”在 Mac 上支持直接在编辑器中运行 Shell 脚本。如果脚本功能已停用,请选择 Shortcuts > Settings,打开 Advanced,然后启用 Allow Running Scripts。认识 macOS 版“快捷指令”,“快捷指令”的高级设置

插入一行并创建单独的结果文件

将以下完整程序粘贴到 Run Shell Script 中。它要求提供一个现有的普通文件,在第 3 行之前插入 Birch,并在源文件路径后附加 .inserted.txt,以此命名相邻的文件。

#!/bin/sh
set -eu

fail() {
    printf '%s\n' "$1" >&2
    exit 1
}

[ "$#" -eq 1 ] || fail 'Select exactly one example text file.'
file=$1
line=3

case "$file" in
    /*) ;;
    *) fail 'The input must be an absolute file path.' ;;
esac

[ -f "$file" ] || fail 'The input file does not exist or is not a regular file.'
[ ! -L "$file" ] || fail 'Select the example file itself, not a symbolic link.'
[ -r "$file" ] || fail 'The input file cannot be read.'

count=$(/usr/bin/sed -n '$=' "$file") || fail 'Could not count the input lines.'
[ -n "$count" ] || fail 'The input file is empty.'
[ "$line" -le "$count" ] || fail 'The insertion line is beyond the end of the file.'

output="${file}.inserted.txt"
if [ -e "$output" ] || [ -L "$output" ]; then
    fail 'The output name already exists. Inspect or rename that result first.'
fi

if (
    set -C
    /usr/bin/sed "${line}i\\
Birch
" "$file" > "$output"
); then
    printf 'Created edited copy: %s\n' "$output"
else
    fail 'Could not finish the edited copy. Inspect any partial output before retrying.'
fi

sed 表达式中的实际换行符是有意安排的。请保留 i 后面的反斜杠、紧接其后的换行符,以及单独占一行的插入文本。命令结构遵循 Apple 发布的手册,避免依赖从其他 sed 实现中复制来的简写插入语法。sed 手册:命令与插入文本

外围检查是此工作流程的判定规则。在指定行之前插入文本,要求该行必须存在。因此,此程序会拒绝空文件以及目标行号大于最后一行的情况。它也会拒绝已存在的输出文件名,而 Shell 的 noclobber 模式则在打开输出文件时提供另一层检查。

脚本会读取原文件并写入单独的文件。如果写入失败,原文件仍然可用,但可能会留下不完整的输出文件。遇到错误时请将操作视为尚未完成,并在删除或重试前检查该输出文件。一次只运行一个实例,并在操作期间保持源文件不变。

此操作的输出是包含路径的文本状态消息,而不是“快捷指令”文件对象。编辑后的文件由脚本的重定向创建。在后面添加 Save File 会保存操作的输出,而不会执行预期的行插入。Apple 将 Save File 记载为文件管理共享操作;其文档并未将其描述为按行号编辑文件的工具。关于 Mac 上“快捷指令”中的共享操作

运行快捷指令、检查结果并保留原文件名

在 Finder 中按住 Control 键并点按一次性示例文件 notes.txt,选择 Quick Actions,然后选择 Insert Birch Before Line 3。Apple 的文档介绍了这种启动方式。如果找不到快捷指令,请检查其详细信息中是否已启用 Finder,以及接受的输入是否包含 Files。在 Mac 上工作时运行快捷指令

成功运行后,示例文件夹中会同时有 notes.txt 和 notes.txt.inserted.txt。打开后者,并检查内容是否完全如下:

Cedar
Maple
Birch
Willow

你也可以在“终端”中运行以下完整命令来检查输出:

/bin/cat "$HOME/Desktop/Line Insert Demo/notes.txt.inserted.txt"

要将编辑后的内容放到原始路径下,请关闭示例文稿,然后在 Finder 中执行以下两次重命名操作。先确认 notes.original.txt 尚未使用;如果该名称已存在,请选择另一个尚未使用的备份名称。

  1. 选择 notes.txt,按 Return,将其重命名为 notes.original.txt,然后再次按 Return。
  2. 选择 notes.txt.inserted.txt,按 Return,将其重命名为 notes.txt,然后再次按 Return。
  3. 打开新命名的 notes.txt 并核对四行内容。

Apple 的文档说明了如何选择项目并按 Return 为其重命名。两个名称都要保留末尾的 .txt 扩展名。如果 Finder 提示名称冲突,请停止操作并检查现有项目,然后再选择一个尚未使用的名称。在 Mac 上重命名文件、文件夹和磁盘

完成后,编辑后的内容位于 notes.txt,原始内容位于 notes.original.txt。这会替换占用原始路径的文件;它不会保留原始文件的身份,也不保证保留 Finder 标签、扩展属性或其他元数据。

调整插入内容而不改变其含义

对于另一个一次性文件,将 line=3 改为一个现有行号,并将 Birch 替换为简单的单行文本。请保持显示的插入语法不变。示例文本特意不含引号、美元符号、反引号或反斜杠;任意文本需要使用额外的 Shell 和 sed 引号处理。

编辑脚本前,请使用此判定表:

目标更改 操作
将文本放在第一行之前 在非空示例文件中设置 line=1。
将文本放在另一现有行之前 将 line 设为该正整数行号。
在最后一行之后添加文本 使用单独的追加操作;此程序会拒绝不存在的目标行。
插入到空文件中 单独创建文件的初始内容;文件中没有可指定的现有行。
将同一个快捷指令运行两次 先检查此前生成的输出。替换原文件后再次运行,会再次插入一份相同文本。

对于这个桌面任务,实用的检查点包括所选文件、插入位置、完成的结果和最终文件名。四行示例确认无误后,你就可以通过相同的步骤测试其他简单的插入,同时保留原始文本。

来源