Sed只用较短的替换替换部分较长的匹配:

时间:2016-05-18 22:10:54

标签: regex bash shell replace sed

所以我正在测量c程序经过的总时间。通过这样做,我一直在运行这个shell脚本,它使用sed来替换我的c程序中某行中间某处定义的常量(低于:N)的值。

#define N 10 // This constant will be incremented by shell program

在你告诉我我应该使用变量并对使用它的函数进行计时之前,我必须在一次运行时从外部计算整个程序的执行时间(意味着没有重新分配N)。

我一直在shell脚本中使用以下内容来帮助:

tmp=$(sed "11s/[0-9][0-9]*/$INCREMENTINGVAR/" myprogram.c); printf "%s" "$tmp" > myprogram.c

用我的INCREMENTINGVAR(替换)替换3位数字。但是,当替换为2位数时,这对我来说似乎不适用。 Sed仅替换前两个字符,并保留上一次运行的前三个数字而不删除它。

TESTS=0
while [ $TESTS -lt 3 ]
do
    echo "This is test: $TESTS"
    INCREMENTINGVAR=10

    while [ "$INCREMENTINGVAR" -lt 10 ] 
    do
        tmp=$(sed "11s/[0-9][0-9]*/$INCREMENTINGVAR/" myprogram.c); printf "%s" "$tmp" > myprogram.c
        rm -f myprog.c.bak
        echo "$INCREMENTINGVAR"
        gcc myprogram.c -o myprogram.out; ./myprogram.out
        INCREMENTINGVAR=$((INCREMENTINGVAR+5))
    done
    TESTS=$((TESTS+1))
done

我应该做些什么吗?

编辑:添加整个shell脚本;改变了sed的模式。

2 个答案:

答案 0 :(得分:1)

您是否只想用新值替换第11行的任何数字字符串?如果是这样,你就写了:

sed -e "11s/[0-9][0-9]*/$INCREMENTINGVAR/"

查找一个或多个数字的序列,并用$INCREMENTINGVAR中的当前值替换它。这将从9翻转到10,从99到100,从999到1000,等等。事实上,没有什么可以阻止你从1跳到987,654,如果这是你想要做的。

使用sed的GNU和BSD(Mac OS X)版本,您可以自动覆盖该文件。可移植的方式(意思是,与sed的GNU和BSD变体一样工作)是:

sed -i.bak -e "11s/[0-9][0-9]*/$INCREMENTINGVAR/" myprog.c
rm -f myprog.c.bak

这会创建一个备份文件(并将其删除)。问题是GNU sed仅需要-i而BSD sed需要-i ''(两个参数)才能在没有备份的情况下进行原位更改。您可以确定可移植性不相关。

请注意,使用行号来识别必须更改的内容是微妙的;微不足道的变化(新标题,更多评论)可能会改变行号。使用上下文搜索可能会更好:

sed -i.bak -e "/^#define N [0-9]/ s/[0-9][0-9]*/$INCREMENTINGVAR/" myprog.c
rm -f myprog.c.bak

这假设defineN之间的空格和数字。如果你可能有空格或标签,那么你可以写:

sed -i.bak -e "/^[[:space:]]*#[[:space:]]*define[[:space:]]\{1,\}N[[:space:]]*\{1,\}[0-9]/ s/[0-9][0-9]*/$INCREMENTINGVAR/" myprog.c
rm -f myprog.c.bak

查找#之前的可选前导空格,#define之间的可选空格,define之间的强制空格(至少一个,可能很多) 1}}和N,以及N和数字的第一个数字之间的强制空格。但是,您的输入可能不是那么草率,而且更简单的搜索模式(如第一个选项)足以满足您的需求。您还可以编写代码来将偏心格式化的#define行标准化为规范表示 - 但同样,您很可能不需要。

如果您在同一文件中的其他位置包含以下内容:

#undef N
#define N 100000

你也不得不担心匹配这一行的模式。但是,很少有文件这样做;它在实践中可能不是一个问题(如果是这样的话,代码通常可能比这里可以处理的问题更多)。一种可能性是将范围限制为前30行,假设第一个#define N 123在某个范围内而第二个不在。

sed -i.bak -e "1,30 { /^[[:space:]]*#[[:space:]]*define[[:space:]]\{1,\}N[[:space:]]*\{1,\}[0-9]/ s/[0-9][0-9]*/$INCREMENTINGVAR/; }" myprog.c
rm -f myprog.c.bak

还有其他多种技巧可以用来限制伤害,并有不同程度的冗长。例如:

sed -i.bak -e "1,/^[[:space:]]*#[[:space:]]*define[[:space:]]\{1,\}N[[:space:]]*\{1,\}[0-9]\{1,\}/ \
                s/^[[:space:]]*#[[:space:]]*define[[:space:]]\{1,\}N[[:space:]]*\{1,\}[0-9]\{1,\}/#define N $INCREMENTINGVAR/; }" myprog.c
rm -f myprog.c.bak

使用正则表达式通常是特异性和详细程度之间的判断调用 - 您可以使事情非常安全但难以阅读,或者您可以冒一个小风险,使您的可读代码与无意识的内容相匹配。

答案 1 :(得分:0)

我不完全确定你为什么现在没有按预期工作。 - 它可能与您使用的sed版本有关。你用的是什么版本?您可以查看sed --version - 我已安装4.2.2。

我似乎无法自己复制这个问题 - 但是你总是可以试试这个,我觉得它有帮助。

11s/[0-9]\{1,3\}/$INCREMENTINGVAR/

如果我记得 - {n,m}表示"匹配至少N次,最多M次。"在你的情况下,它将匹配2或3位数,然后用增量var的任何内容替换它们 - 它不应该是替换它是什么。