用于删除内嵌式欺骗的命令行

时间:2015-07-02 19:00:14

标签: regex awk sed command-line-interface

从一条线内删除欺骗的快速而简洁的方法是什么?

我有一个以下格式的文件:

alpha • a | b | c | a | b | c | d
beta • h | i | i | h | i | j | k
gamma •  m | n | o
delta • p | p | q | r | s | q

因此,第1列中有一个标题,然后是管道分隔的各种单词,并且有不可预测的重复数量。所需的输出删除了dupe,如:

alpha • a | b | c | d
beta • h | i | j | k
gamma •  m | n | o
delta • p | q | r | s 

我的输入文件是几千行。上面的希腊名字对应于类别名称(例如,"棒球");并且字母表对应英语词典单词(可能包含空格或重音),例如"球类游戏|击球手|捕手|捕手|指定击球手"。

这可以通过多种方式编程,但我怀疑这是一种聪明的方法。我经常遇到这种情况的变化,并想知道是否有简洁而优雅的方式来做到这一点。我正在使用MacOS,所以有一些奇特的unix选项不可用。

奖金的复杂性,我经常会在最后留下评论,例如,

zeta • x | y | x | z | z ; comment here

P.S。此输入实际上是先前StackOverflow问题的输出: Command line to match lines with matching first field (sed, awk, etc.)

3 个答案:

答案 0 :(得分:1)

BSD awk没有内置GNU sort的{​​{1}}函数,但我不确定它们是否必要。子弹,•(U + 2022),使awk引起一些悲伤。

我建议将子弹预处理为单字节字符。我选择awk,但如果您愿意,可以使用 Control-A 或其他内容。您的数据位于文件@中。我注意到data行中m之前有一个双倍空格;我认为这不重要。

gamma

运行此产生:

sed 's/•/@/' data |
awk -F ' *[@|] *' '
{
    delete names
    delete comments
    delete fields;
    if ($NF ~ / *;/) { split($NF, comments, / *; */); $NF=comments[1]; }
    j = 1;
    for (i = 2; i <= NF; i++)
    {
        if (names[$i]++ == 0)
            fields[j++] = $i;
    }
    printf("%s", $1);
    delim = "•"
    for (k = 1; k < j; k++)
    {
        printf(" %s %s", delim, fields[k]);
        delim = "|";
    }
    if (comments[2])
        printf(" ; %s", comments[2]);
    printf("\n");
}'

答案 1 :(得分:1)

使用bash,sort,xargs,sed:

while IFS='•;' read -r a b c; do
  IFS="|" read -ra array <<< "$b"
  array=( "${array[@]# }" )
  array=( "${array[@]% }" )
  readarray -t array < <(printf '%s\0' "${array[@]}" | sort -zu | xargs -0n1)
  SAVE_IFS="$IFS"; IFS="|"
  s="$a• ${array[*]}"
  [[ $c != "" ]] && s="$s ;$c"
  sed 's/|/ | /g' <<< "$s"
  IFS="$SAVE_IFS"
done < file

输出:

alpha • a | b | c | d
beta • h | i | j | k
gamma •  m | n | o
delta • p | q | r | s
zeta • x | y | z ; comment here

我认为“m”之前的两个空格是拼写错误。

答案 2 :(得分:1)

这可能适合你(GNU sed):

sed  'h;s/.*• \([^;]*\).*/cat <<\\! | sort -u |\1|!/;s/\s*|\s*/\n/2ge;s/\n/ | /g;G;s/^\(.*\)\n\(.*• \)[^;]*/\2\1/;s/;/ &/' file

这个想法的草图是:删除每行的头部和尾部,将数据变形为迷你文件,使用标准实用程序对重复项进行排序和删除,然后再将线重新组合在一起。

此处该行的副本保留在保留空间中。已删除ID和注释。使用cat和bash here-document语法将数据导入文件并通过排序进行管道传输(如果您的排序没有配备-u选项,则使用uniq)。通过将原始线附加到模式空间并使用正则表达式模式匹配来评估模式空间并重新组合线。