使用AWK替换一组文本的最后一行

时间:2011-10-02 02:57:24

标签: bash sed awk stdout

我通过执行各种命令获得此输出

d41d8cd98f00b204e9800998ecf8427e 1317522632  /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document.txt
d41d8cd98f00b204e9800998ecf8427e 1317522632 /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document - Copy.txt
d41d8cd98f00b204e9800998ecf8427e 1317522632 /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document - Copy (2).txt
d41d8cd98f00b204e9800998ecf8427e 1317506438 /home/evan/school_work/unix/Projects/Project2/finddups/.svn/tmp/tempfile.tmp

2430ffcf28e7ef6990e46ae081f1fb08 1317522636 /home/evan/school_work/unix/Projects/Project2/finddups/test/New folder/junk2 - Copy.txt
2430ffcf28e7ef6990e46ae081f1fb08 1317506569 /home/evan/school_work/unix/Projects/Project2/finddups/test/New folder/junk2.txt

我想通过awk管它以使它看起来像这样

Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document.txt
Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document - Copy.txt
Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document - Copy (2).txt
Original: /home/evan/school_work/unix/Projects/Project2/finddups/.svn/tmp/tempfile.tmp

Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New folder/junk2 - Copy.txt
Original: /home/evan/school_work/unix/Projects/Project2/finddups/test/New folder/junk2.txt

有什么想法吗?

一些澄清

换行符或EOF之前的最后一个文件将是原始文件,之前的所有内容都应标记为重复文件。

第一列是文件的md5sum,第二列是修改日期。您会注意到组中的最后一个文件将始终具有最早的时间戳,这是我用来确定哪个文件是“原始”文件的标准。

以下是使用the获取所有重复项列表的命令

find ${PWD} -type f -exec stat -c %Y {} \; -exec md5sum '{}' \; | sed -r 'N;s/([0-9]+)\n([^ ]+) /\2 \1/g' | sort -r | uniq -w 32 --all-repeated=separate

5 个答案:

答案 0 :(得分:1)

对行进行排序(使用sort),将哈希值存储在临时变量中,并使用if语句将其与当前值进行比较。另一个if语句应该删除可能的空行。

例如:

| sort | awk '{ if ($0) { if (TEMP != $1) { print "Original: " $0 } else { print "Duplicate:" $0 } TEMP = $1 } }'

修改

由于您提供了这些说明,您可以这样做:

| tac | awk '{ if ($0) { if (TEMP != $1) { print "Original: " $0 } else { print "Duplicate:" $0 } TEMP = $1 } else { print "" } }' | tac

tac反转了行顺序,实现了第一个示例中的排序。第二个tac恢复原始订单。

答案 1 :(得分:1)

这个sed oneliner可能有效:

sed '$G' source | # append a newline to source
sed -nr '$!{N;s/^([^ ]+ )[^ ]+ +(.*\n)\1/Duplicate: \2\1/;s/^[^ ]+ [^ ]+ +(.*\n)$/Original: \1/;P;D}'

通过在源文件中附加换行符,问题就变成了两个替换,否定了任何EOF不优雅。

我猜一个sed解决方案是可以接受的,因为你在源文件准备中使用了sed。

答案 2 :(得分:0)

你怎么知道什么是重复的,什么是副本?这将是我的问题。

如果重复项在名称中都有Copy,那将很容易,但是您的第一个示例,第一个重复项之一称为New Text Document.txt,原始文件位于.svn永远不应该看过的目录。

看起来你在第一列中有MD5哈希,这意味着你可以对它进行排序,然后使用awk循环输出并在哈希值发生变化时打印一个空行。这会将你的文件组合在一起。

原始与副本将变得更加困难。你必须为此制定一个好的标准。您可以选择最早的修改日期(mdate)。你也可以对此进行排序。当您打破哈希时,您可以简单地假设列表中的第一个文件(因为它具有最早的日期)是原始文件。

或者,您可以简单地假设文件名中嵌入了Copy字样的那些是副本。然后,它可能并不那么重要。您是否希望程序仅识别重复项或删除它们?如果程序只是识别重复项,则无需确定哪些是原始文件,哪些是重复文件。你可以用你的眼睛比任何算法更好地做到这一点。

顺便说一下,这三列究竟是什么。我假设第一个是has,最后一个是文件名,但是中间的是什么?

答案 3 :(得分:0)

如果在每个组的最后一行之后出现空白行,包括最后一个组,并且文件名永远不会包含空格,那么这可能会有效。它取决于是否存在空白行。

awk 'NF == 3 { if (save != "") { printf("Duplicate: %s\n", save); } save = $3; }
     NF == 0 { printf("Original:  %s\n", save); save = ""; }'

如果缺少最后一个空行,则不会打印最后一行。

由于文件名中的空白(因此大多数行不只有3个字段),这不起作用。 Awk并不是最合适的工具。当Awk不适合时,我倾向于使用Perl:

#!/usr/bin/env perl
use strict;
use warnings;
my $save = "";

while (<>)
{
    chomp;
    if ($_ =~ m/^ (?:[\da-fA-F]+) \s+ (?:\d+) \s+ (\S.*)/x)
    {
        print "Duplicate: $save\n" if $save ne "";
        $save = $1;
    }
    else
    {
        print "Original:  $save\n\n";
        $save = "";
    }
}

这会产生:

Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document.txt
Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document - Copy.txt
Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New Text Document - Copy (2).txt
Original:  /home/evan/school_work/unix/Projects/Project2/finddups/.svn/tmp/tempfile.tmp

Duplicate: /home/evan/school_work/unix/Projects/Project2/finddups/test/New folder/junk2 - Copy.txt
Original:  /home/evan/school_work/unix/Projects/Project2/finddups/test/New folder/junk2.txt

如果你必须使用Awk,那么你需要在$0时使用NF >= 3,删除散列和inode号(或数据行上的第二个值)以找到文件名。

答案 4 :(得分:0)

awk '{
  for (i = 0; ++i < NF;)
    print "Duplicate:", $i
  print "Original:", $NF    
  }' FS='\n' RS= infile