用脚本中的 sed 将行替换为文件的内容

Posted

技术标签:

【中文标题】用脚本中的 sed 将行替换为文件的内容【英文标题】:Replace lines with the content of a file with sed in a script 【发布时间】:2020-10-31 01:05:02 【问题描述】:

我想使用 sed(在 MacOs 上)将文件中的某些行替换为另一个文件的内容。

假设文件如下,我想替换所有包含<iframe src=...0></iframe> 的行。

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Paupertas si malum est, mendicus beatus esse nemo

<iframe src="https://test.com/bar/11d75d1c627c017299e4fe35e" frameborder=0></iframe>

oportunitatis esse beate vivere. Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum

<iframe src="https://test.com/bar/9e4e1b69131bf5d718452aca6" frameborder=0></iframe>

vivere. Quasi vero, inquit, perpetua oratio rhetorum solum, non etiam philosophorum

在命令行上,以下命令运行良好:

$ sed -i '' -e "/$line/r $replacementFile" -e "//d" $originalFile

在哪里

$line 是要更改的行
line="\<iframe src=\"https:\/\/test\.com\/bar\/11d75d1c627c017299e4fe35e\" frameborder=0\>\<\/iframe\>"
$replacementFile指向内容必须替换$lines的文件,其内容如下:
LOREM IPSUM DOLOR
AMET, CONSECTETUR
ADIPISCING ELIT.
$originalFile 是必须更改 $line 的文件的路径

我有几个文件要修改,每个文件都包含几行要更改。所以我写了一个脚本,其中&lt;iframe src=...0&gt;&lt;/iframe&gt; 类型的所有行都用正则表达式找到,然后我应用在命令行上工作的命令。

以下是我写的原始脚本:

function replaceTextWithFile() 
    # File to modify (in production the name is passed as an argument)
    local originalFile="./file.txt"

    # Regex to find the line to change
    local regex="<iframe src=\"(https:\/\/test.*)\" frameborder"
    # Regex on "normal" line does not work neither
    #local patternToReplace="^oportu.*"

    # The file whose content must replace the line
    local replacementFile="./new_content"

    exec 4<"$originalFile"
    while read line <&4 ; do
        if [[ $line =~ $regex ]];then
            echo "PATTERN FOUND"
            sed -i '' -e '/$line/r $replacementFile' -e '//d' $originalFile
        fi

        # Following command changes the 9th line successfully
        sed -i '' -e "12s/.*/TEST TEST/" $originalFile
    done
    exec  4<&-


replaceTextWithFile

但文件没有改变。

我认为没有发生任何事情,因为我要更改的行有一些特殊字符(&lt;/、..),但脚本在“正常”行上也不起作用。

我还认为问题是因为文件是用描述符文件以读取模式打开的,但是我可以使用 sed 更改文件的内容,例如使用以下命令:

sed -i '' -e "9s/.*/TEST TEST/" $originalFile

我尝试了几种语法都没有成功,你可以找到我尝试过的脚本和不同的语法here。

有人知道我做错了什么吗?

*** 上有几个问题可以解决某种问题,但都没有帮助。

如果我的第一个意图是用 sed 替换这些行,任何其他解决方案都将不胜感激。

【问题讨论】:

''sed -i '' ... 中的作用是什么? 哇,$line =~ $regex/$line/ 的意义何在?不,sed 使用正则表达式,而不是精确行。还有为什么要反向引用(https....)?你打算匹配()吗?为什么https:\/\/foo - 输入中没有foo。 @LxerLx 指定备份后缀 @KamilCuk,我的意图是如果该行与正则表达式($line =~ $regex)匹配,则更改所有行。 sed 不适用于确切的行?好的,我会朝这个方向搜索,谢谢 $line =~ $regex), then change the all line. 毫无意义。 sed 已经适用于文件中的所有行。就像您在每一行上循环行号一样。 【参考方案1】:

您的脚本有一些错误:

正则表达式是错误的,它与输入不匹配。 sed 适用于正则表达式,而不适用于确切的字符。所以/$line/ 会被解析为正则表达式。 变量不扩展单引号。 '/$line/' 匹配 字面意思 5 个字符 $line。要扩展变量,请使用双引号。 *** difference between single and double quotes in bash。 在执行sed 时循环每一行是没有意义的。 sed 已经在每一行执行... bash 支持function func() 作为支持奇怪语法的扩展,不应该使用它。只需 func() 来定义一个函数。 bash-hackers wiki deprecated and obsolete syntax。 Following command changes the 9th line successfully 后跟一行更改第 12 行。 because the lines that I want to change have some particular characters &lt;&gt; 在正则表达式中没有特殊含义,但是 \&lt; \&gt; 是 GNU sed extension。通过regex crosswords 学习正则表达式。 要读取一行输入,请使用IFS= read -r line。 bashfaq how to read a file line by line。

只是:

replaceTextWithFile() 
    local originalFile="./file.txt"
    local regex="<iframe src=\"https:\/\/.*\" frameborder"
    local replacementFile="./new_content"
    # note -i '' on macos I think
    sed -i -e "/$regex/r $replacementFile" -e '//d' "$originalFile"

请注意,正则表达式可以更简单地写在单引号内,这样" 就不需要转义:

    local regex='<iframe src="https:\/\/.*" frameborder'

或者您甚至可以在 sed 中使用不同的正则表达式分隔符来使其变得简单:

    local regex='<iframe src="https://.*" frameborder'
    sed "\~$regex~r $replacementFile"

Tested on repl.

【讨论】:

我在写我的问题时在我的正则表达式中犯了一个错误。但是,您的回答@KamilCuk 是正确的,可以解决我的问题。我也非常感谢所有有趣的建议!谢谢!!

以上是关于用脚本中的 sed 将行替换为文件的内容的主要内容,如果未能解决你的问题,请参考以下文章

用 Bash 脚本中的 sed 替换文件中的版本号

如何使用 sed 或 awk 命令用变量(变量保存 Unix 脚本)查找和替换文本

用文件的内容替换某些标记(使用 bash 脚本)

shell脚本替换A、B两个文件里面的内容

shell脚本替换文本内容

shell脚本里怎么替换掉某个文件的最后一行的最后一个字符