我正在编写Tcl脚本,其中一部分包含用另一行替换包含某些字符串模式的第一次出现的行。我使用sed作为Tcl内部的bash命令,如下所示:
exec bash -c {sed 0,/Apple/{s/Apple/Banana/ File}
我的问题是有没有直接的方法来使用Tcl命令,也是错误或不建议在Tcl中使用大量的Bash命令?
由于
答案 0 :(得分:5)
答案 1 :(得分:1)
您可以使用[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.
答案 2 :(得分:0)
如果要替换文本块中的字符串:
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实例,这不是艾哈迈德想要的(感谢Glenn指出这一点)。我们应该使用acheong87的regsub解决方案:
package require fileutil
proc processLine {line} { return [regsub -- {Apple} $line "Banana"] }
fileutil::updateInPlace file.txt processLine
答案 3 :(得分:0)
首先,在这里伸出sed
看起来很整洁(由于sed
的陈述性表达性)但是多余并且因为添加每个“活动部分”而使你的程序维护更加困难复杂性。
基本上,在普通Tcl中重新实现代码段所需的步骤是:
gets
以逐行读取数据。regsub
执行正确构造的调用,这会尝试将“Apple”替换为“Banana”。regsub
的调用返回1或更多意味着该字符串与您的正则表达式匹配,请退出文件读取循环。其余的实现取决于您对数据的处理方式。
其次,即使你选择从你的Tcl脚本执行sed
,你肯定不需要在这里涉及shell,因为Tcl的exec
命令能够处理命令管道和流重定向本身。
当前方法的另一个非显而易见的问题是,在另一个脚本(对于shell)中内联一个脚本(对于sed
)可能很容易创建逃避噩梦:尝试思考如果您的源模式或您的源模式会发生什么替换模式可能是任意数据,可以包含空格字符以及单引号和双引号。
第三,当你需要调用一个shell 时,永远不要打电话给bash
,除非你真的想要需要在{<3}}中使用bashism你传递给shell的脚本。在所有其他99.9%的情况下,您应该调用/bin/sh
而不是标准shell的标准路径,该标准shell保证为其运行的脚本实现POSIX shell语义。这样做的原因是某些系统(特别是Debian及其至少一些衍生产品)的/bin/sh
链接到另一个shell(在Debian中,它是dash
),缺少{{1}的交互功能但作为交换,它启动并更快地运行其脚本。