如何使用sed替换文件中多次出现的变量?

时间:2017-07-03 10:57:03

标签: shell sed makefile

我已经在编写一个脚本来替换变量的值" SUBDIRS"在shell脚本的Makefile中。 我使用下面的命令,它工作正常,但在第一次出现" SUBDIRS"和makefile不完整。

 sed -z -i "s/\(SUBDIRS = \).*/\1$(tr '\n' ' ' < changed_fe_modules.log)\n/g" Makefile

现在我想保留我的Makefile,并且只替换3次&#34; SUBDIRS = abcdefgh&#34;并正确更新Makefile。

请建议如何替换所有3次出现并保持Makefile也以原始方式结尾。

Makefile输入样本:

enter image description here

Makefile所需的输出样本:

enter image description here

现在,当前命令给我低于输出:它在第一次替换后退出,文件不完整。

enter image description here

1 个答案:

答案 0 :(得分:0)

这很难做到。

您看到此行为的原因是您在sed中使用-z选项。 -z选项用NUL字符分隔行,而不是换行符。这意味着整个文件(直到第一个NUL字符,这里没有一个)被视为单个“行”,用于sed的模式匹配。

所以这个正则表达式:

\(SUBDIRS = \).*

此处的.*匹配第一个SUBDIRS =匹配后的整个文件的其余部分。然后用changed_fe_modules.log文件的内容替换文件的其余部分。之后没有什么可以匹配,所以sed就完成了。

如果您的原始makefile在一行中列出了所有SUBDIRS,而不是使用反斜杠/换行符分隔符,那么它很简单;你可以使用:

sed -i "s/^SUBDIRS = .*/SUBDIRS = $(tr '\n' ' ' < changed_fe_modules.log)/" Makefile

如果你必须使用反斜杠/换行符,你可能无法使用sed进行此更改。你需要使用像Perl这样功能更强大的东西,它具有非贪婪的匹配功能。

ETA

您也可以在普通shell中编写它:

new_subdirs=$(tr '\n' ' ' < changed_fe_modules.log)
line_cont=false
in_subdirs=false

while read -r line; do
    if $line_cont; then
        case $line in
            (*\\) : still continuing ;;
            (*) line_cont=false ;;
        esac
        $in_subdirs || printf '%s\n' "$line"
        continue
    fi

    case $line in
        (SUBDIRS =*)
            echo "SUBDIRS = $new_subdirs"
            in_subdirs=true ;;
        (*) printf '%s\n' "$line"
            in_subdirs=false ;;
    esac

    case $line in
        (*\\) line_cont=true ;;
    esac

done < Makefile > Makefile.new

mv -f Makefile.new Makefile

(注意,完全未经测试)