【问题标题】:Replacing the last lines of a group of text using AWK使用 AWK 替换一组文本的最后一行
【发布时间】:2011-10-02 02:57:24
【问题描述】:

我通过执行各种命令得到了这个输出

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,第二列是修改日期。您会注意到组中的最后一个文件总是有最旧的时间戳,这是我用来确定哪个文件是“原始”文件的标准,最旧的文件。

这是我用来获取所有重复项列表的命令

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

【问题讨论】:

  • 你保证每组最后一行之后出现空行吗?这是否包括“在最后一组之后”(所以最后一行输入总是一个空行)?你保证文件名不包含空格(或制表符)吗?
  • 哦 - 废话;讨厌水平滚动条...数据在文件名中显然有空格。所以 awk 不是一个合适的工具;您无法可靠地拆分行(它将拆分空格上的字段,并且相邻的空格将是一个分隔符)。遗憾;这种方式更难。

标签: bash sed awk stdout


【解决方案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恢复原来的顺序。

【讨论】:

  • 我实际上想保持行的顺序,包括空行。虽然我明白你在说什么,并认为我可以从这里开始工作 =) 谢谢
  • 好吧,你可以修改第一个if语句。没有它,它将产生Original: 的空白行。排序是将具有相同哈希的文件组合在一起,并按第二列(我认为是日期)对它们进行排序。
  • 我不知道tac,谢谢!颠倒顺序是明智的,因为您不必“向前看”来查看下一行是否为空行。谢谢
【解决方案2】:

这个 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。

【讨论】:

  • 我真的很喜欢这个解决方案,你觉得你能解释一下这里发生了什么吗?
  • 对于除最后一行之外的所有内容,下一行都附加到模式空间。重复文件将具有正则表达式“md5sum 时间戳文件名\nmd5sum”。原始文件将具有正则表达式“md5sum 时间戳文件名\n$”。用文字替换 md5sum 和时间戳,然后打印模式空间中的第一行,然后将其删除,为下一次迭代做好准备。
【解决方案3】:

您如何知道什么是重复,什么是副本?那将是我的问题。

如果重复项的名称中都包含Copy,那将很容易,但是您的第一个示例,第一个重复项中的一个称为New Text Document.txt,而原始项位于.svn 目录中,而该目录不应该是看了看。

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

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

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

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

【讨论】:

  • 我已经编辑了原始问题并进行了一些澄清。
【解决方案4】:

如果每个组的最后一行之后出现空行,包括最后一个组,并且文件名从不包含空格,这可能会起作用。它取决于空行的存在。

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,那么您需要在 NF &gt;= 3 时处理 $0,删除哈希和 inode 编号(或数据行上的任何第二个值)以查找文件名。

【讨论】:

    【解决方案5】:
    awk '{
      for (i = 0; ++i < NF;)
        print "Duplicate:", $i
      print "Original:", $NF    
      }' FS='\n' RS= infile
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-04-25
      • 2013-08-14
      • 2013-11-17
      • 1970-01-01
      • 2016-10-20
      • 2021-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多