使用SED删除重复的字符串

时间:2014-07-07 13:42:54

标签: sed

我使用buildroot包将一些软件包移植到某些Linux嵌入式系统。某些软件包还会生成纯文本脚本和/或库控制文件,并引用暂存目录。在打包软件以供分发时,必须删除对暂存目录的引用。我没有问题使用SED删除这些引用。但是,这个处理留下了一些不希望的重复字符串模式,我摘录如下所示。我想知道是否可以使用SED来删除这些重复项。

注1 ' dependency_libs =' 被排除在外,现在进行了修改,如下所示。我试图简洁地摘录这里需要的东西,但没有包括' dependency_libs ='之前因为它不包含任何重复项。显然,它在下面的一些建议的解决方案中起着重要作用。因此,我在这里为后人修改了它。

Note2 我刚从@potong发现 sed 脚本的一个小错误。如果重复的字符串是最后一个没有空格的对象,则 sed 脚本将失败。在这种情况下,第一个' dependency_libs =' 行将部分失败 sed 脚本。第二个' dependency_libs =' 行在行尾(在单引号之前)包含一个空格,并且没有问题地通过 sed 脚本。我在这里修改了它以显示差异。

cppflags=-I/usr/include -I/include -I/usr/include -I/include -I${includedir}/mine
cxxflags=-I/usr/include -I/include -I/usr/include -I/include -I${includedir}/mine 
Cflags: -I/usr/include -I/include -I/usr/include -I/include -I${includedir}/mine 
Libs: -L/usr/lib -L/lib -L/usr/lib -L/lib -L${libdir} -lmine${suffix}
dependency_libs='-L/usr/lib -L/lib -L/usr/lib -L/lib -L/usr/lib/libiconv-full/lib -L/usr/lib/libintl-full/lib -L/usr/lib -L/lib -L/usr/lib -L/lib'
dependency_libs='-L/usr/lib -L/lib -L/usr/lib -L/lib -L/usr/lib/libiconv-full/lib -L/usr/lib/libintl-full/lib -L/usr/lib -L/lib -L/usr/lib -L/lib '

这样它就会变成:

cppflags=-I/usr/include -I/include -I${includedir}/mine
cxxflags=-I/usr/include -I/include -I${includedir}/mine                        
Cflags: -I/usr/include -I/include -I${includedir}/mine                         
Libs: -L/usr/lib -L/lib -L${libdir} -lmine${suffix}
dependency_libs='-L/usr/lib/libiconv-full/lib -L/usr/lib/libintl-full/lib'
dependency_libs='-L/usr/lib/libiconv-full/lib -L/usr/lib/libintl-full/lib'

3 个答案:

答案 0 :(得分:1)

这可能适合你(GNU sed):

sed -r ':a;s|((-[IL]/\S+\s).*)\2|\1|;ta' file

这将查找以-I/-L/开头的字符串,后跟一个或多个非空格和重复的空格,并删除第二次出现。如果发生替换,则重复该过程,直到不再发生替换为止。

答案 1 :(得分:0)

这可能对您有用:

awk -F- '
  {
    for(i = 2; i <= NF; ++i) a[$i] = 1;
    printf("%s", $1)
    for(x in a) printf("-%s ", x)
    print""
    delete a
  }
'

输出:

cppflags=-I${includedir}/mine -I/include  -I/usr/include
cxxflags=-I${includedir}/mine  -I/include  -I/usr/include
Cflags: -I${includedir}/mine  -I/include  -I/usr/include
Libs: -L${libdir}  -lmine${suffix} -L/lib  -L/usr/lib

请注意,它不会保留目录的顺序,并且会在此处添加额外的空间。

如果您需要保留目录的顺序,并且可以使用gawk,请尝试:

gawk -F- '
  BEGIN {PROCINFO["sorted_in"] = "@val_num_asc"}
  {
    for(i = 2; i <= NF; ++i)
      if (!($i in a))
        a[$i] = i;
    printf("%s", $1)
    for(x in a) printf("-%s ", x)
    print""
    delete a
  }
'

输出:

cppflags=-I/usr/include  -I/include  -I${includedir}/mine
cxxflags=-I/usr/include  -I/include  -I${includedir}/mine
Cflags: -I/usr/include  -I/include  -I${includedir}/mine
Libs: -L/usr/lib  -L/lib  -L${libdir}  -lmine${suffix}

或者你可以使用像这样的非gnu awk获得相同的输出:

awk -F- '
  {
    for(i = 2; i <= NF; ++i)
      if (!($i in a))
        a[$i] = i;
    printf("%s", $1)
    for(x in a) b[a[x]] = x
    for(x in b) printf("-%s ", b[x])
    print""
    delete a
    delete b
  }
'

当然,如果你需要摆脱额外的空间,你可以通过tr -s ' '管道输出。

答案 2 :(得分:0)

我不认为sed会起作用,因为您需要一个 field 定向的实用程序,它可以处理单行的相互关联的部分。

awkbash的使用是一种选择,但这是#!/usr/bin/env bash while read -r line; do # Split line into prefix, separator, options array. [[ $line =~ ^([^=:]+)([:=]\ *)(.*)$ ]] prefix=${BASH_REMATCH[1]} sep=${BASH_REMATCH[2]} read -ra optArray <<<"${BASH_REMATCH[3]}" # Loop over options array and build up a list without duplicates. dedupOptList='' for opt in "${optArray[@]}"; do [[ " $dedupOptList " == *" $opt "* ]] || dedupOptList+=" $opt" done # Finally, rebuild the line with the deduplicated options list and print. printf '%s%s%s\n' "$prefix" "$sep" "${dedupOptList:1}" done < file 解决方案

注意:

  • 出于性能原因,仅适用于小型输入文件。
  • 假设输入中没有选项嵌入空格。
  • 保留选项的输入顺序(选项之间的空格是规范化的)。
{{1}}