如何在unix中的目录中查找重复的文件名

时间:2014-08-07 09:41:02

标签: perl shell unix

以下是我目录中的几个文件。

**$pwd
/opt/offline/**

1  -rw-r--r--. 1 root root  40513 Aug  7 10:02 TN_DAY0OFFER8047_07082014100213_processed
2  -rw-r--r--. 1 root root  32335 Aug  7 10:02 TN_DAY0OFFER8204_07082014100217_processed
3  -rw-r--r--. 1 root root  20126 Aug  7 10:02 TN_DAY0OFFER8047_07082014100221_processed
4  -rw-r--r--. 1 root root 205175 Aug  7 10:02 TN_DAY0OFFER7027_07082014100225_locked
5  -rw-r--r--. 1 root root  15776 Aug  7 10:02 TN_DAY0OFFER7020_07082014100229_locked
6  -rw-r--r--. 1 root root      0 Aug  7 10:02 TN_DAY0OFFER7020_07082014100233_locked

现在第1&第三个文件具有相同的名称(不考虑时间戳),类似于第五个&第6个文件具有相同的名称。 现在我想要获取重复的文件(即第3和第6个)并将其附加到第1个和第2个文件中。分别为5,这样就不会有重复的文件和数据丢失......(最好使用perl或shell)。

4 个答案:

答案 0 :(得分:1)

使用Bash 4.0。

#!/bin/bash

error_exit() {
    echo "$1" >&2
    exit 1
}

[ -n "$BASH_VERSION" ] && [[ BASH_VERSINFO -ge 4 ]] || error_exit "Script requires Bash 4.0."

[[ -z $1 || ! -d $1 ]] && error_exit "Directory not specified or doesn't exist: $1"

pushd "$1" || error_exit "Unable to change directory to $1."

declare -A MAP

shopt -s nullglob

for F in *_*_*_*; do
    [[ -f $F ]] || continue
    IFS=_ read -ra A B C D __ <<< "$F"
    BASE=${MAP["$A|$B|$D"]}
    if [[ -n $BASE ]]; then
        cat "$F" >> "$BASE"
        rm -f -- "$F"
    else
        MAP["$A|$B|$D"]=$F
    fi
done

用法:

bash script.sh dir

注意:如果您不希望删除或更改错误的文件,请先使用复制的文件对其进行测试。

cp -a dir /tmp/dir.copy
bash script.sh /tmp/dir.copy

说到操作文件,shell更合适。它也可以与awk一起使用,但awk仍然依赖于/bin/sh,并且有时候参数的卫生很难或很苛刻。

答案 1 :(得分:1)

这是一个执行您想要的Perl脚本。它在当前目录中查找以“TN”开头的文件,并构建一个数组哈希,将具有相似名称的文件组合在一起。然后它通过哈希并连接文件,删除旧文件。

毋庸置疑,在使用此脚本之前对原始文件进行备份

use strict;
use warnings;

my %merges;
for my $file (glob "TN*") {
    if ($file =~ /(.*)_\d+_(.*)/) {
        push @{$merges{"$1$2"}}, "'$file'";
    }
}

for (keys %merges) {
    my @files = @{$merges{$_}}; 
    my $target = shift @files;  
    if (@files) {
        print "concatenating @files to $target\n";
        `cat @files >> $target && rm @files`;
    }
}

答案 2 :(得分:1)

使用Perl:

#!/usr/bin/env perl
use strict;
use warnings;
use File::Glob;
my $dir = $ARGV[0];
die "No argument was passed." if not defined $dir;
die "Argument is not a directory: $dir" if not -d $dir;
chdir "$dir" or die "Unable to change directory to $dir.";
my @files = <*_*_*_*>;
my $map = {};
foreach my $f (@files) {
    next if not -f $f;
    my ($a, $b, $c, $d) = split(/_/, $f);
    my $key = "$a|$b|$d";
    my $base = $map->{$key};
    if (defined $base) {
        open(A, '>>', $base) or die "Unable to open file $base for reading.";
        open(B, '<', $f) or die "Unable to open file $f for reading.";
        while (my $line = <B>) {
            print A $line;
        }
        close(A);
        close(B);
        unlink $f;
    }
    $map->{$key} = $f;
}

用法:

perl script.pl dir

答案 3 :(得分:0)

我认为这里有一些锤子破解坚果......

#! /bin/sh -
# Concatenate files sharing a common prefix (before '_').
# The files are concatenated to a file named by the prefix.

curr=XXX

ls *_* | sort | while read fn
do
    pfx=`expr $fn : '\([^_]*\).*'`
    if test $pfx = $curr; then
        # another in this group of files, sharing a prefix
        cat $fn >> $pfx
    else
        # new group of files with prefix $pfx
        cp $fn $pfx
        curr=$pfx
    fi
done

这不是正好你提出的问题,但似乎与你想要的相符(并且它不涉及*shudder* Perl。)