【问题标题】:Replace only the first matching line while preserving leading whitespace仅替换第一个匹配行,同时保留前导空格
【发布时间】:2016-01-28 02:33:15
【问题描述】:

我有一个文件,里面有一句话:

"cutoffTime": "20151024000000"

我想使用 sed 或 grep 查找以 cutoffTime 开头的行, 然后将整行替换为另一个字符串,例如:

"startTime" : "20151028000000"

编者注:

两个附加要求出现在问题 - 和几个答案 - 第一次发布后,标题现在反映了它们: p>

  • 在要替换的行首保留前导空格(或至少是制表符)。

  • 只替换第一个匹配的行。

【问题讨论】:

    标签: unix sed


    【解决方案1】:

    在行首保留前导空格

    sed 's/^\([[:blank:]]*\)"cutoffTime":.*$/\1"startTime" : "20151028000000"/' file
    
    • ^\([[:blank:]]*\) 在行首捕获空格和制表符(如果存在)的任何组合,并将其添加到替换行之前(通过\1,对正则表达式中第一个(也是唯一一个)捕获组的引用,有效保留原始行的前导空格。

    也只替换 first 匹配:解决方案的复杂性取决于使用的 sed 实现:

    请注意,在// (s//) 形式下方的两种解决方案中 - 即 省略 正则表达式 - 意味着最近应用的正则表达式被隐式重用。

    • GNU sed:
    sed '0,/^\([[:blank:]]*\)"cutoffTime":.*$/ s//\1"startTime" : "20151028000000"/' file
    

    0,/.../ 是一个 GNU sed 扩展,它允许正则表达式 终止 范围,即使它匹配非常 first 行(使用 1 是不行的,因为它总是 start 第一行的范围,然后只开始在 后续 行上寻找正则表达式)。实际上,这将范围限制在文件的开头,直到第一次出现正则表达式,无论它出现在哪里,并且只替换那个出现。

    • 仅限 POSIX 功能的 sed,例如 BSD sed(也用于 OS X):
    replacement='"startTime" : "20151028000000"'
    sed -e '1 s/^\([[:blank:]]*\)"cutoffTime":.*$/\1'"$replacement"'/; t' \
        -e '1,// s//\1'"$replacement"'/' file
    

    由于0,/.../ 特性不是POSIX 的一部分,匹配可能在first 行的情况必须单独处理,所以上面的first 只在1 行查找一场比赛和替补; 1,// 然后有效地从第 2 行查找直到第一个后续匹配和替换。请注意,第一次替换后的t 调用结束了脚本循环如果执行了替换,这意味着范围1,// 永远不会进入。最终效果是,替换只发生在正则表达式的第一次出现时,无论第一次出现在哪里。

    【讨论】:

      【解决方案2】:

      根据 OPs 更新查询使用 像这样只查找和替换第一次出现,如下所示;

      sed '0,/^"cutoffTime":.*/{s/^"cutoffTime":.*/"startTime" : "20151028000000"/}' file 
      

      这将改变上面规定的第一个出现的模式。

      在源文件中替换使用-i

      sed -i '0,/^"cutoffTime":.*/{s/^"cutoffTime":.*/"startTime" : "20151028000000"/}' file 
      

      说明:

      sed '0,/<pattern>/{s/<pattern>/<replacement>/}' file
      

      输出:

      $ sed '0,/^"cutoffTime":.*/{s/^"cutoffTime":.*/"startTime" : "20151028000
      000"/}' file1
      
      "startTime" : "20151028000000"
      "cutoffTime": "20151024000000"
      "cutoffTime": "20151024000000"
      

      【讨论】:

      • 0,/…/ 技术非常适合仅处理第一个匹配项,但值得注意的是,此功能是 GNU sed-特定的;此外,您可以通过使用s//s 调用中的 emtpy 正则表达式)来稍微优化它,而不是从地址中复制正则表达式。
      【解决方案3】:

      使用sed

      sed 's/^"cutoffTime":.*/"startTime" : "20151028000000"/' file
      

      并检查结果。如果要修改原文件

      sed -i .bak 's/^"cutoffTime":.*/"startTime" : "20151028000000"/' file
      
      • s 命令的第一部分将第一个表达式 (^"cutoffTime":.*) 替换为 "startTime" : "20151028000000"

      • 表达式匹配行的开头^,后跟您的文本"cutoffTime":,后跟任何重复0次或多次的字符(.)(*

      【讨论】:

      • 亲爱的 Matteo,感谢您的回复。当我将 cutoffTime 作为行的开头时,这种方法效果很好,如果我在它之前有一些选项卡,它就不起作用。你能帮我改一下吗,意思是它会找到cutoffTime的第一次出现,并从找到的cutoffTime位置替换整个句子,用“startTime”:“20151028000000”。非常感谢。
      【解决方案4】:

      当您的sed 不支持-i 选项时,您可以使用ed

      ed -s request  << EOF
      ,s/^"cutoffTime":.*/"startTime" : "20151028000000"/
      w
      q
      EOF
      

      编辑:来自 OP 的要求已更改,新的解决方案如下。
      您可以使用替换字符串。您可以使用cat request | sed 's/my_practicum/my_solution/' 之类的东西来练习正则表达式。
      当您不想在行首匹配时,只需删除特殊的^。当您只想更改第一个匹配项时,您应该从跳转到正确的行开始。在下面的解决方案中,我将重复搜索字符串,因此如果找不到匹配项,您将不会替换任何内容。

      ed -s request  << EOF
      /cutoffTime
      s/.*cutoffTime.*/"startTime" : "20151028000000"/
      w
      q
      EOF
      

      如果要保留前导字符,请更改替换行:

      ed -s request  << EOF
      /cutoffTime
      s/"cutoffTime.*/"startTime" : "20151028000000"/
      w
      q
      EOF
      

      【讨论】:

      • 亲爱的沃尔特,感谢您的回复。当我将 cutoffTime 作为行的开头时,这种方法效果很好,如果我在它之前有一些选项卡,它就不起作用。你能帮我改一下吗,意思是它会找到cutoffTime的第一个出现,并从找到的cutoffTime位置替换整个句子,用“startTime”:“20151028000000”。非常感谢。
      • 您应该编辑您的问题并将所有要求放在那里。
      猜你喜欢
      • 2014-02-04
      • 1970-01-01
      • 1970-01-01
      • 2023-03-16
      • 2022-12-02
      • 2023-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多