0

我想使用grep命令获取行号,但是当搜索模式不是单个单词时我收到错误消息:

couldn't read file "Pattern": no such file or directory

grep应该如何正确使用?代码在这里:

set status [catch {eval exec grep -n '$textToGrep' $fileName} lineNumber]
if { $status != 0 }  {
    #error
} else {
    puts "lineNumber = $lineNumber"
} 

此外,如果搜索模式根本不匹配,则返回值为:"child process exited abnormally"

这是一个简单的测试用例:

 set textToGrep  "<BBB name=\"BBBRM\""

文件内容:

<?xml version="1.0"?>
<!DOCTYPE AAA>
<AAA>
  <BBB name="BBBRM" />
</AAA>
4

4 回答 4

2

好吧,我也遇到了您的代码和单个单词模式的问题!

首先,我认为您不需要该eval命令,因为catch它本身会对其第一个参数进行评估。

然后,问题是您将$textToGrep变量exec放在单引号内',这对 Tcl 没有任何意义。

因此,如果 的内容textToGrepfoo,则您要求grep搜索字符串'foo'。如果在文件中找不到该字符串(包括单引号),则会出现错误。

尝试重写你的第一行

set status [catch {exec grep -n $textToGrep $fileName} lineNumber]

看看它是否有效。另外,请阅读exec手册页,它很好地解释了这些问题。

于 2013-08-12T10:22:49.407 回答
2

如果您的系统安装了 tcllib,您可以使用软件包中的fileutil::grep命令fileutil

package require fileutil

set fileName data.xml
set textToGrep {<BBB +name="BBBRM"}; # Update: Add + for multi-space match
set grepResult [::fileutil::grep $textToGrep $fileName]
foreach result $grepResult {
    # Example result:
    # data.xml:4:  <BBB name="BBBRM" />
    set lineNumber [lindex [split $result ":"] 1]
    puts $lineNumber

    # Update: Get the line, squeeze the spaces before name=
    set line [lindex [split $result ":"] 2]
    regsub { +name=} $line " name=" line
    puts $line
}   

讨论

  • 在为 赋值时textToGrep,我使用了花括号,因此允许在内部使用双引号,而不必转义它们。
  • ::fileutil::grep命令的结果是一串字符串。每个字符串都包含文件名、行号和行本身;用冒号分隔。
  • 提取行号的一种方法是首先将字符串(结果)拆分为多个片段,使用冒号作为分隔符。接下来,我lindex用来抓取第二项(索引=1,因为列表是零基数)。
  • 我已经更新了代码以解决name=之前有多个空格的情况
于 2013-08-12T14:22:08.493 回答
1

这里有两个问题:

  • 模式匹配不起作用。
  • child process exited abnormally找不到模式时grep 退出并出错

第一个问题是因为您没有将textToGrep内部double quotes括起来(而不是单引号)。所以你的代码应该是:

[catch {exec grep -n "$textToGrep" $fileName} lineNumber]

第二个问题是由于grep命令的退出状态。grep找不到模式时退出并出错。这是对 shell 的尝试:

# cat file
pattern
pattern with multiple spaces
# grep pattern file
pattern
pattern with multiple spaces
# echo $?
0
# grep nopattern file
# echo $?
1

编辑:

在您的情况下,您有特殊字符,例如<and >(在外壳上具有特殊含义)。

set textToGrep  "<BBB name=\"BBBRM\""
regsub -all -- {<} "$textToGrep" "\\\<" textToGrep
regsub -all -- {>} "$textToGrep" "\\\>" textToGrep
于 2013-08-12T10:58:24.513 回答
1
set textToGrep {\<BBB name="BBBRM"}
catch {exec grep -n $textToGrep $fileName} status 
if {![regexp "child process" $status]} {
puts $status
} else {
puts "no word found" 
} 

我认为您应该对子进程进行正则表达式。只需检查上面的代码是否有效。在 if 语句中,您可以根据需要处理状态命令。

对于给定的示例(在您的帖子中),上述代码仅适用于您需要对 textToGrep 变量中的“<”使用反斜杠

于 2013-08-12T10:43:06.947 回答