【问题标题】:Insert text into line if that line doesn't contain another string using sed如果该行不包含另一个字符串,则使用 sed 将文本插入该行
【发布时间】:2018-06-05 10:41:49
【问题描述】:

我在一个 linux 服务器上合并了一些文本文件,但其中的行略有不同,我需要将它们统一起来。

例如一些文件会有类似的行

id='1244' group='american' name='fred',american

其他文件会这样

id='2345' name='frank', english

最后别人会喜欢

id='7897' group='' name='maria',scottish

我需要做的是,如果 group='' 或 group 根本不在字符串中,我需要在逗号之前的某处将其添加到逗号之后的文本中,因此在该行上方的第二个示例中变成:

id='2345' name='frank' group='english',english

和上一个例子中的一样

id='7897' name='maria' group='scottish',scottish

这将进入一个 bash 脚本。我实际上无法删除该行并添加到文件末尾,因为它与以下行相关。

我用过以下:

sed -i.bak 's#group=""##' file 

删除 group="" 字符串,因此行将包含 group='something' 或根本不包含它并且有效

然后我尝试使用以下方法添加该组(如果它不存在):

sed -i.bak '/group/! s#,(.*$)#group="\1",\1#' file

但这会引发错误

sed: -e expression #1, char 38: invalid reference \1 on `s' command's RHS

EDIT 由 Ed Morton 创建一个示例输入文件和预期输出:

示例输入:

id='1244' group='american' name='fred',american
foo
id='2345' name='frank', english
bar
id='7897' group='' name='maria',scottish

预期输出:

id='1244' group='american' name='fred',american
foo
id='2345' name='frank' group='english',english
bar
id='7897' name='maria' group='scottish',scottish

【问题讨论】:

  • 到目前为止你尝试过什么?让我们看看一些代码:-)
  • 抱歉,我已经编辑了我的问题以展示我的尝试。
  • 在 BRE(基本正则表达式)语法中,sed 的默认模式语法,捕获组的括号必须被转义:\(.*$\) 否则,它们被视为文字字符。
  • 我编辑了您的问题以尝试提供 1 个示例输入文件以及给定该输入的预期输出。如果是错误的,请修复它。一堆单独的输入行,一些具有预期的输出,一些没有,并且没有一个具有周围行的上下文,不如一个具有上下文的具体示例有用。

标签: regex bash sed


【解决方案1】:
sed -r "
    /group=''/ s///                                   # group is empty, remove it
    /group=/!  s/,[[:blank:]]*(.+)/ group='\\1',\\1/  # group is missing, add it
" file
id='1244' group='american' name='fred',american
foo
id='2345' name='frank' group='english',english
bar
id='7897'  name='maria' group='scottish',scottish

foo 和 bar 行没有被改变,因为 s/// 命令不匹配逗号后跟字符。

【讨论】:

  • 这为我指明了正确的方向,它似乎完全按照描述的那样做,但 EOL 似乎把它搞砸了。我得到像 id='7897' name='maria' group='scottish 这样的输出,所以缺少组周围的最后一个 ' 和逗号开始的文本
  • 可能是因为我已经在文件上使用了 sed -i -e "s/^M//g" 吗?#
  • 有点像留回车的声音。 s/\r$//
  • 使用回车,我希望你看到行尾覆盖了行首:',scottish name='maria' group='scottish
【解决方案2】:

类似

sed  '
    /^[^,]*group[^,]*,/ ! {
        s/, *\(.*\)/ group='\''\1'\'', \1/
    }
    /^[^,]*group='\'\''/ {
        s/group='\'\''\([^,]*\), *\(.*\)/group='\''\2'\''\1, \2/
    }
'

【讨论】:

    【解决方案3】:

    这个 GNU awk 可能会有所帮助:

    awk -v sq="'" '
      BEGIN{RS="[ ,\n]+"; FS="="; found=0}
      $1=="group"{
        if($2==sq sq) 
          {next}
        else
          {found=1}
      }
      NF>1{
        printf "%s=%s ",$1,$2
      }
      NF==1{
        if(!found)
          {printf "group=%s",$1}
        print ","$1
        found=0
      }
    ' file
    

    脚本依赖于记录分隔符RS,它设置为获取所有key='value' 对。

    如果没有找到键 group 或为空,则在到达只有一个字段的记录时打印。

    请注意,变量sq 包含单引号字符,用于检测空的group 字段。

    【讨论】:

      【解决方案4】:

      Sed 可能非常丑陋。而且您的数据格式似乎有些不一致。这可能对你有用:

      $ sed -e "/group='[a-z]/b e" -e "s/group='' *//" -e "s/,\([a-z]*\)$/ group='\1', /" -e ':e' input.txt
      

      为了便于阅读,我们正在这样做:

      • /group='[a-z]/b e - 如果该行包含有效的 group,则跳转到末尾。
      • s/group='' *// - 删除任何空组,
      • s/,\([a-z]*\)$/ group='\1', / - 根据您的规格添加一个新组
      • :e - 第一个命令的分支标签。
      • 然后默认操作是打印该行。

      我真的不喜欢以这种方式处理数据。它很容易出错,您将进一步将这些数据读入准确存储其数据结构的东西,然后根据新结构打印数据。更强大的解决方案可能会直接与生成或使用这些数据的任何事物相关联,并且不会像这样处于中间位置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多