【问题标题】:Replace whole line containing a string using Sed使用 Sed 替换包含字符串的整行
【发布时间】:2012-06-30 00:04:02
【问题描述】:

我有一个文本文件,其中包含类似的特定行

sometext sometext sometext TEXT_TO_BE_REPLACED sometext sometext sometext

我需要用

替换上面的整行
This line is removed by the admin.

搜索关键字是TEXT_TO_BE_REPLACED

我需要为此编写一个 shell 脚本。如何使用sed 实现这一目标?

【问题讨论】:

    标签: string shell sed


    【解决方案1】:

    你需要在前后使用通配符(.*)来替换整行:

    sed 's/.*TEXT_TO_BE_REPLACED.*/This line is removed by the admin./'
    

    【讨论】:

    • 谢谢,我的工作开始了:sed 's/.*.*/SE_LABEL = ABC/g' MYR2.xml > test.txt
    • 这适用于 Mac OS X Yosemite,但我使用的 -i 和 -e 标志如下:sed -i -e "s/.*search_string.*/Replacement_line/' file_being_searched.txt
    • @KentJohnson 我认为您的命令中的引号不匹配。
    • @MBarnett 你是对的,我应该有两个双引号。
    • 仅供参考。要使其就地,可以添加-i 选项
    【解决方案2】:

    您可以使用 change 命令替换整行,并使用 -i 标志进行就地更改。例如,使用 GNU sed:

    sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
    

    【讨论】:

    • 请注意,c\之前需要一个空格。我刚刚编辑添加了这个。
    • @MarcusDowning GNU sed 需要空间;它就像最初发布的那样工作得很好。如果您的特定 sed 需要空间,那么一定要注意哪个 sed 不兼容,并添加必要的调用作为注释。但是,请不要更改已接受答案中的工作代码。
    • 如何使用变量而不是文本“This...”?如果我用 $variable 替换它,它不会打印它的内容,而是打印变量名。
    • c\ 后面直接跟一个变量有问题:…c\$VAR… 反斜杠会转义美元。在这种情况下,我(Ubuntu 15.10 上的 bash/sed)必须写 …c\\$VAR…
    • 在mac上使用:sed -i '' '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo; (当第一个参数为空时,它会在文件中编辑,否则会创建备份)
    【解决方案3】:

    和上面的一样..

    sed 's/[A-Za-z0-9]*TEXT_TO_BE_REPLACED.[A-Za-z0-9]*/This line is removed by the admin./'
    

    【讨论】:

    • 这会将FOO=TEXT_TO_BE_REPLACED 更改为FOO=This line ...,因此不符合规范。
    • Yes.. 我们的要求是将整行替换为“此行已被管理员删除。”如果我们找到关键模式“TEXT_TO_BE_REPLACED”。上面的命令是令人满意的。如果我的理解有误,请纠正我。@Jens
    • @AnnapureddyHari 如果搜索字符串之前或之后的文本中包含除 A-Za-z0-9 之外的任何内容,则此答案不起作用。正如 Jens 指出的那样,如果有等号,它就会失败。 "FOO=" 部分将保留;您还没有更换整条生产线。这段代码对文件中可能包含的内容是短视的。如果你的意思是通配符,你应该放通配符,正如托尔的回答所示。
    【解决方案4】:

    由于以下几个原因,接受的答案对我不起作用:

    • 我的 sed 版本不喜欢具有零长度扩展名的 -i
    • c\ 命令的语法很奇怪,我无法让它工作
    • 我没有意识到我的一些问题来自未转义的斜杠

    所以这是我想出的解决方案,我认为它应该适用于大多数情况:

    function escape_slashes {
        sed 's/\//\\\//g' 
    }
    
    function change_line {
        local OLD_LINE_PATTERN=$1; shift
        local NEW_LINE=$1; shift
        local FILE=$1
    
        local NEW=$(echo "${NEW_LINE}" | escape_slashes)
        # FIX: No space after the option i.
        sed -i.bak '/'"${OLD_LINE_PATTERN}"'/s/.*/'"${NEW}"'/' "${FILE}"
        mv "${FILE}.bak" /tmp/
    }
    

    所以解决问题的示例用法:

    change_line "TEXT_TO_BE_REPLACED" "This line is removed by the admin." yourFile
    

    【讨论】:

      【解决方案5】:

      我经常使用正则表达式从文件中提取数据,我只是用它来替换文字引用 \"// 什么都没有 :-)

      cat file.csv | egrep '^\"([0-9]{1,3}\.[0-9]{1,3}\.)' | sed  s/\"//g  | cut -d, -f1 > list.txt
      

      【讨论】:

        【解决方案6】:

        在我的 makefile 中,我使用了这个:

        @sed -i '/.*Revision:.*/c\'"`svn info -R main.cpp | awk '/^Rev/'`"'' README.md
        

        PS:不要忘记 -i 实际上会更改文件中的文本...因此,如果您定义为“修订”的模式将发生变化,您还将更改要替换的模式.

        示例输出:

        John Doe 编写的 Abc-Project

        修订:1190

        因此,如果您将模式设置为“Revision: 1190”,显然与您将它们定义为“Revision:”不一样...

        【讨论】:

          【解决方案7】:

          以上答案:

          sed -i '/TEXT_TO_BE_REPLACED/c\This line is removed by the admin.' /tmp/foo
          

          如果替换字符串/行不是变量,则可以正常工作。

          问题是在 Redhat 5 上,c 之后的 \ 转义了 $。双重 \\ 也不起作用(至少在 Redhat 5 上)。

          通过点击和试用,我发现如果您的替换字符串/行只有一行,c 之后的\ 是多余的。所以我没有在c之后使用\,而是使用一个变量作为单个替换行,这很高兴。

          代码如下所示:

          sed -i "/TEXT_TO_BE_REPLACED/c $REPLACEMENT_TEXT_STRING" /tmp/foo
          

          注意使用双引号而不是单引号。

          【讨论】:

          • 你仍然可以像这样使用单引号:sed -i '/TEXT_TO_BE_REPLACED/c '"$VARIABLE"'' /tmp/foo
          • 此变体适用于 Ubuntu/Debian:sed -i "/TEXT_TO_BE_REPLACED/c\\$REPLACEMENT_TEXT_STRING" /tmp/foo
          【解决方案8】:
          bash-4.1$ new_db_host="DB_HOSTNAME=good replaced with 122.334.567.90"
          bash-4.1$ 
          bash-4.1$ sed -i "/DB_HOST/c $new_db_host" test4sed
          vim test4sed
          '
          '
          '
          DB_HOSTNAME=good replaced with 122.334.567.90
          '
          

          效果很好

          【讨论】:

            【解决方案9】:

            到目前为止提供的所有答案都假设您对要替换的文本有所了解,这是有道理的,因为这就是 OP 所要求的。我提供的答案假设您对要替换的文本一无所知,并且文件中可能有一个单独的行,其中包含您不想被替换的相同或相似内容。此外,我假设您知道要替换的行的行号。

            以下示例演示了通过特定行号删除或更改文本:

            # replace line 17 with some replacement text and make changes in file (-i switch)
            # the "-i" switch indicates that we want to change the file. Leave it out if you'd
            #   just like to see the potential changes output to the terminal window.
            # "17s" indicates that we're searching line 17
            # ".*" indicates that we want to change the text of the entire line
            # "REPLACEMENT-TEXT" is the new text to put on that line
            # "PATH-TO-FILE" tells us what file to operate on
            sed -i '17s/.*/REPLACEMENT-TEXT/' PATH-TO-FILE
            
            # replace specific text on line 3
            sed -i '3s/TEXT-TO-REPLACE/REPLACEMENT-TEXT/'
            

            【讨论】:

              【解决方案10】:
              cat find_replace | while read pattern replacement ; do
              sed -i "/${pattern}/c ${replacement}" file    
              done 
              

              find_replace 文件包含 2 列,c1 带有要匹配的模式,c2 带有替换,sed 循环替换包含变量 1 的模式之一的每一行

              【讨论】:

              • 不,这在几个方面都是错误的。使用包含您要执行的所有替换的脚本文件运行一次sed。在同一个文件上重复运行sed -i 是一种可怕的反模式。
              【解决方案11】:

              用于操作配置文件

              我在skensell answer的启发下提出了这个解决方案

              configLine [searchPattern] [replaceLine] [filePath]

              它会:

              • 如果文件不存在则创建文件
              • 替换searchPattern匹配的整行(所有行)
              • 如果找不到模式,则在文件末尾添加 replaceLine

              功能:

              function configLine {
                local OLD_LINE_PATTERN=$1; shift
                local NEW_LINE=$1; shift
                local FILE=$1
                local NEW=$(echo "${NEW_LINE}" | sed 's/\//\\\//g')
                touch "${FILE}"
                sed -i '/'"${OLD_LINE_PATTERN}"'/{s/.*/'"${NEW}"'/;h};${x;/./{x;q100};x}' "${FILE}"
                if [[ $? -ne 100 ]] && [[ ${NEW_LINE} != '' ]]
                then
                  echo "${NEW_LINE}" >> "${FILE}"
                fi
              }
              

              疯狂的退出状态魔法来自https://stackoverflow.com/a/12145797/1262663

              【讨论】:

                【解决方案12】:

                以下命令对我有用。哪个正在使用变量

                sed -i "/\<$E\>/c $D" "$B"
                

                【讨论】:

                • 但我的新要求是在替换时跳过注释(以#开头)行。当我们替换完整行时,这也将替换注释行,您最终会得到重复的属性。如果有人对此有解决方案,请告诉我。
                • “重复属性”是什么意思?要否定匹配地址,请使用! address
                【解决方案13】:

                要在不依赖任何 GNUism 的情况下执行此操作,例如不带参数的 -i 或不带换行符的 c

                sed '/TEXT_TO_BE_REPLACED/c\
                This line is removed by the admin.
                ' infile > tmpfile && mv tmpfile infile
                

                在这种(符合 POSIX 标准的)命令形式中

                c\
                text
                

                text 可以包含一行或多行,并且应该成为替换的一部分的换行符必须被转义:

                c\
                line1\
                line2
                s/x/y/
                

                其中s/x/y/是模式空间被两行替换后的新sed命令

                line1
                line2
                

                【讨论】:

                  【解决方案14】:

                  将包含指定字符串的整行替换为该行的内容

                  文本文件:

                  Row: 0 last_time_contacted=0, display_name=Mozart, _id=100, phonebook_bucket_alt=2
                  Row: 1 last_time_contacted=0, display_name=Bach, _id=101, phonebook_bucket_alt=2
                  

                  单个字符串:

                  $ sed 's/.* display_name=\([[:alpha:]]\+\).*/\1/'
                  output:
                  100
                  101
                  

                  由空格分隔的多个字符串:

                  $ sed 's/.* display_name=\([[:alpha:]]\+\).* _id=\([[:digit:]]\+\).*/\1 \2/'
                  output:
                  Mozart 100
                  Bach 101
                  

                  调整正则表达式以满足您的需求

                  [:alpha] 和 [:digit:] 是Character Classes and Bracket Expressions

                  【讨论】:

                    猜你喜欢
                    • 2021-12-30
                    • 1970-01-01
                    • 2020-09-07
                    • 2014-03-30
                    • 2018-01-29
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多