我正在编写 Tcl 脚本,其中一部分涉及将包含某些字符串模式的行的第一次出现替换为另一行。我在 Tcl 中使用 sed 作为 bash 命令来完成它,如下所示:
exec bash -c {sed 0,/Apple/{s/Apple/Banana/ File}
我的问题是有没有使用 Tcl 命令的直接方法,在 Tcl 中使用大量 Bash 命令是错误的还是不推荐?
谢谢
您可以使用以下[string]
命令组合来执行此操作:
proc replaceFirst {text find replace} {
set idx [string first $find $text]
if {$idx == -1} {return $text} ;# search string not found in text
set end [expr {$idx + [string length $find] - 1}]
string replace $text $idx $end $replace
}
replaceFirst "I hear Apple makes many popular Apple products." Apple Banana
# => I hear Banana makes many popular Apple products.
首先,在这里伸出援手sed
似乎很整洁(由于sed
's 的声明性表达),但是多余的并且使您的程序的维护更加困难,因为添加每个“移动部分”会增加复杂性。
基本上,在纯 Tcl 中重新实现代码段所需遵循的步骤是:
gets
文件的通道,逐行读取数据。regsub
,这将尝试将“Apple”替换为“Banana”。regsub
返回 1 或更多意味着字符串与您的正则表达式匹配,退出文件读取循环。实施的其余部分取决于您对数据的处理方式。
其次,即使您选择sed
从您的 Tcl 脚本执行,您当然也不需要在此处涉及 shell,因为 Tcl 的exec
命令能够自行处理命令管道和流重定向。
您当前方法的另一个不明显的问题是,将一个脚本 (for sed
) 内联到另一个脚本 (for shell) 中可能很容易造成逃避噩梦:尝试考虑如果您的源模式或替换模式可能是任意数据会发生什么包含空格字符以及单引号和双引号。
bash
第三,当你需要调用shell时,永远不要调用,除非你真的意味着你需要在传递给 shell 的脚本中有一个bashism。在所有其他 99.9% 的情况下,您应该调用/bin/sh
它,它是标准 shell 的标准路径,它保证为它运行的脚本实现 POSIX shell 语义。这样做的原因是某些系统(特别是 Debian 和至少它的一些衍生产品)已经/bin/sh
链接到另一个 shell(在 Debian 中是 it's dash
),该 shell 缺乏交互功能,bash
但作为交换,它可以更快地启动和运行其脚本。
如果要替换文本块中的字符串:
set text [string map {Apple Banana} $text]
如果你想更新文件中的字符串,这个fileutil
包只有票fileutil::updateInPlace
::
package require fileutil
proc processLine {line} { return [string map {Apple Banana} $line] }
fileutil::updateInPlace file.txt processLine
请注意,该string map
命令可以一次替换多个字符串:
string map {Apple Banana Monkey Ape Coke Pepsi} $text
事实证明,字符串映射将替换 Apple 的所有实例,这不是 Ahmed 想要的(感谢 Glenn 指出这一点)。我们应该使用 acheong87 的 regsub 解决方案:
package require fileutil
proc processLine {line} { return [regsub -- {Apple} $line "Banana"] }
fileutil::updateInPlace file.txt processLine