Zed 是一个很好的答案,但要更加精简 - 对于即使是脚本也可以执行的编辑级别 - zsh 可以处理变量中的所有 256 个字符/字节值(包括 null)。这意味着您可以直接从命令行逐行或逐块编辑几乎任何类型的文件数据。这大约是 zed/vared 所做的。如果您有一个包含所有标准模块的当前版本,那么加载 zsh/mapfile 或 zsh/system 是一个很大的好处,这样您就可以捕获命令扩展遗漏的任何字符(zed 使用 @987654323 @ 将文件读入内存)。以下是您可以使用此变量操作方法的示例:
% typeset -T Buffer buffer $'\n'
% typeset -T Edit edit $'\n'
最常见的做法是使用换行符来分割希望编辑的文本文件。
这个方便的功能将使 zsh 让您一次完全访问一行或一系列行,而不会无意中弄乱数据。
% zmodload zsh/mapfile
% Buffer=$mapfile[path/to/file]
在这里,我使用方便的mapfile 模块,因为我可以逐字节加载文件的内容。或者,您可以使用 % Buffer="$(<path/to/file)",就像 zed 一样,但您总是会删除尾随的换行符,并且由于拼写错误或环境变化可能会进行其他分词,因此模块方法的简单性是最好的。完成后,您只需将 $Buffer 值分配回 $mapfile[file] 或使用更经典的命令(如 printf '%s' $Buffer >path/to/file)来保存更改(这是精确的字符串写入,逐字节,因此任何换行符或格式化您添加回来将被写入)。
您使用映射数组在 Buffer 和 Edit 之间传输行,但是,请记住,在最简单的形式中,将一个数组分配给另一个数组会丢弃完全空的元素(one \n \n two \n three 变为 one \n two \n three)。如果使用整个数组,您可以通过引用输入数组并将“@”符号添加到其索引"$buffer[@]" 来抑制此空元素删除;如果使用数组"${(@)buffer[2,50]}" 的范围,则将“@”符号添加到标志中。保留空行可能会有点麻烦,但这些多个数组只能在脚本或函数中使用,因为您可以在命令行中使用buffer[54]="echo This is a newly written line."一次编辑一行。
% edit=($buffer[50,70])
...
% buffer[50,70]=($edit)
这是标准的 Zsh 语法,这意味着在 ... 区域中,您可以随意编辑和操作 $edit 行数组或 $Edit 标量文本块,包括添加更多行或删除一些行.当您将行添加回$buffer 时,它将用新行替换指定的行块 (50-70),自动扩展或减少其他数组元素以适应重新集成的行。 -- 由于动态数组的调整,您也可以像buffer[40]=("new string as new line" "$buffer[40]") 这样的新行插入您需要的任何内容。这会将它插入到给定索引之前,而交换元素的顺序 ("$buffer[40]" "new string as new line") 在给定索引之后插入新行。要么将所有后续元素(包括完全空的元素)调整为当前索引加一。
如果您想重写 zed 函数以以某种复杂的方式使用此方法,例如:newzed /path/to/file [start-line] [end-line],那将非常方便。
在我离开之前,我想提一下,直接使用vared,一旦你在交互式终端上输入了这些命令,你可能会因为不能使用“Enter”来插入或追加新行而感到沮丧。我发现我的终端和使用 ESC-ENTER 的 Zsh 版本运行良好,但我不知道旧版本(如果我没记错的话,Mac 通常备有不是最新的版本)。如果这不起作用,您可能需要进行一些文档挖掘以了解如何设置 ZLE(Zsh Line Editor,Zsh 的一个组件)或获取更新版本的 Zsh。此外,其他一些 shell 在索引标量变量时可能按字节计数,因为在 ascii 和 C 中,字节与字符相同,但 Zsh 支持 UTF8 并且将按 UTF8 字符索引标量字符串,除非您关闭 shell选项multibyte(默认开启)。如果您需要使用旧的字节字符索引,这将有助于操作每一行。另外,如果你有一个没有用zsh/mapfile 或zsh/system 编译的Zsh 版本,那么你可以使用read 内置函数的多个选项来实现类似的效果,比如<path/to/file |read -u 0 -k $[5 * 2**20] -r -s Buffer ||(($#Buffer))。正如您在此处看到的,您必须使读取长度足够大以适应文件的大小,否则它将遗漏部分文件,并且read 返回码几乎总是因为无法读取而出现错误字符串的全长。我们用||(($#Buffer)) 解决了这个问题,但是这个内置函数根本不是为了有效地处理大规模字节操作,所以你所见即所得。