use strict; use warnings; use Data::Dumper; my @paths = qw( This/is/the/full/path/file.abc.part-1.txt This/is/the/full/path/file.abc.part-2.txt This/is/the/full/path/file.abc.part-3.txt This/is/the/full/path/file.def.part-1.txt This/is/the/full/path/file.def.part-2.txt This/is/the/full/path/file.ghi.part-1.txt This/is/the/full/path/file.jkl.part-2.txt This/is/the/full/path/file.mno.part-5.txt ); my %combo; foreach my $pathfile ( @paths ) { if( $pathfile =~ m/^(.+file\.)(\w+?)\.(part-\d+)\.txt$/ ) { push( @{ $combo{$1}{$2} }, $3 ); } else { warn "$pathfile does not match expected format"; } } foreach my $path ( keys %combo ) { foreach my $type ( keys %{ $combo{$path} } ) { if( scalar @{ $combo{$path}{$type} } > 1 ) { my $newfile = join( '', $path, $type, '.MERGED.txt' ); print "These files go into $newfile:\n"; print ' ', join( ', ', @{ $combo{$path}{$type} } ), "\n"; } } } print Dumper( \%combo ); #### These files go into This/is/the/full/path/file.abc.MERGED.txt: part-1, part-2, part-3 These files go into This/is/the/full/path/file.def.MERGED.txt: part-1, part-2 $VAR1 = { 'This/is/the/full/path/file.' => { 'jkl' => [ 'part-2' ], 'abc' => [ 'part-1', 'part-2', 'part-3' ], 'mno' => [ 'part-5' ], 'def' => [ 'part-1', 'part-2' ], 'ghi' => [ 'part-1' ] } };