BASH: Agrupando arquivos por nome

3

Eu tenho mais de um milhão de arquivos. E eu tenho que prosseguir por eles.
Hierarquias de diretórios dos meus arquivos como abaixo

source=/opt/output/renamed/
target=/opt/output/combine
send=/opt/output/send/combined 

Primeiramente eu tenho que fazer um loop de 1000 arquivos a partir do diretório de origem (/ opt / output / renamed) e agrupá-los por nome de arquivo.

Nome do arquivo == > ORACLE_gprtcp_201209221454_312312.log.gz

primeira e segunda colun não é tão importante. Mas eu tenho que agrupá-los pelo terceiro campo, que é um timestamp.

E eles têm que ser agrupados por trinta minutos. Por exemplo, existem dois arquivos que gostam destes

1.ORACLE_gprtcp_201209231632_987546.log.gz 
2.ORACLE_gprtcp_201209231612_123876.log.gz 
3.ORACLE_gprtcp_201209231602_987546.log.gz 
4.ORACLE_gprtcp_201209231644_987546.log.gz  
5.ORACLE_gprtcp_201209231647_987546.log.gz 
6.ORACLE_gprtcp_201209231601_987546.log.gz 

o intervalo de tempo do primeiro grupo deve ser em trinta minutos

por exemplo, os primeiros arquivos goruped são

2, 3 e 6 arquivos (eles estão nos primeiros trinta minutos) 1,4 e 5 arquivos (eles estão nos últimos trinta minutos)

Eu tentei escrever um script como este

#!/bin/bash 
sourceFolder="/opt/rename/"
limitCount=10 # Limit for send file count for per process
renamed="/opt/combine"
target="/opt/send/combined/"

    for sf in ${sourceFolder}; do
    fileList=$(find ${sf} -type f -name "*.gz"  | sort -t '_' -k3 | head -${limitCount} ) 
    for filePath in $(echo "${fileList}"); do 
      fileName=$(basename ${filePath}) # source file name
      dirName=$(dirname ${filePath}) # source dir name
      #timeRef=$(echo ${fileName} | cut -d '_' -f 3 |  sed 's/\(.\{11\}\).*//') 
      timeRef=$(echo ${fileName} | cut -d '_' -f 3 |  cut -c-11) 
      #time ref : ORACLE_gprtcp_20120923012703_3431593.log.gz

        if [ "${sf}" == "/opt/rename/" ]; then #####  combine
        #Move files to under /opt/combine/ to process files in the fastest way
        mv ${filePath} ${renamed}
        timeRef30="${group} | cut -d '_' -f 3 |  sed 's/\(.\{10\}\).*//')"
        echo  $timeRef30
        for files in $(find ${renamed} -name "*${timeRef}*" | uniq)
        do
         fileGroup=$(echo $files | sort -t '_' -k 3 )
         first=$(echo ${fileGroup} | head -1 | cut -d '_' -f 4 | cut -d '.' -f 1)
         last=$(echo ${fileGroup}  | tail -1 | cut -d '_' -f 4 | cut -d '.' -f 1)           
          for group in ${fileGroup}
          do
           timeInt=$(echo ${group} | cut -d '_' -f 3 |  sed 's/\(.\{10\}\).*//')
           zcatBaseName=$(dirname ${group}) #/opt/rename/
           zcatName=$(basename ${group}) 
           zcatUniq=$(echo ${group}| cut -d '_' -f 4 | cut -d '.' -f 1)
           newName=$(echo ${targetNAT}/ORACLE_gprtcp_${timeInt}000_${first}${last}.log)
           sleep 1
           echo "starting to zcat all files ${fileGroup}"
           zcat -f $(echo ${fileGroup}) >> "/opt/combine/ORACLE_gprtcp_${timeInt}000_${first}${last}.log"
           gzip "/opt/infolog/output/iotest/24/combine/ORACLE_gprtcp_${timeInt}000_${first}${last}.log"
           rm -f $(echo ${fileGroup})
           sleep 4                              
          done
         done
         fi 
done 
done

Existe alguém que possa me dar uma sugestão sobre como posso obter sucesso para agrupar arquivos entre trinta minutos e zcat-los para um novo arquivo?

Obrigado antecipadamente

    
por icameto 02.10.2012 / 15:46

2 respostas

3

Infelizmente, não tenho tempo para lhe dar uma resposta completa, mas apenas algumas dicas que podem ajudar.

Eu iria apenas imprimir os arquivos relevantes e classificá-los de acordo com o tempo do Unix (descobri que funciona melhor do que o normal / tempo legível por humanos):

find $PWD -type f -printf '%T@ %p\n' | sort -nb

então você pode armazenar o tempo Unix do primeiro membro de um grupo de 30 min como ponto de referência para quando os 30 minutos começarem, calcular a diferença para o timestamp Unix do arquivo atual se > 1800 então crie um novo grupo else add to current group. Algo nesse sentido:

#!/bin/bash
#1800 s = 30 min
#unix time 86400s = 1 day

fileList=$(find $PWD -type f -printf '%T@ %p\n' | sort -nb)
## for debugging:
# fileList=$(find $PWD -type f -printf '%T@ %t %p\n' | sort -nb)

org_IFS=$IFS
IFS=$'\n'
group_start_time=0
for line in $fileList; do
    current_time=$(echo $line | awk '{print $1}')
    if [ $group_start_time -eq 0 ] ; then
        group_start_time=$current_time
    else
        delta=$(($current_time - $group_start_time))
        #echo $delta
        if [ $delta -lt 1801 ] ; then
            echo $line
        else
            echo -e "\nnew group:\n$line"
            group_start_time=$current_time
        fi
    fi
done
IFS=$org_IFS

de lá você pode apenas fazer um redirecionamento do caminho do arquivo para qualquer arquivo que você quiser (usando > >). e depois execute mv nessa lista de arquivos para seus respectivos diretórios.

Espero que ajude de alguma forma. :)

Editar: modifiquei o script de modo que ele grava os grupos de arquivos log.gz nos arquivos na origem (seu /opt/rename/ ) no diretório de destino (que eu supus era seu diretório /opt/send/combined/ ). Abaixo está o código modificado:

#!/bin/bash
#1800 s = 30 min
#unix time 86400s = 1 day

sourceFolder="/opt/rename/"
target="/opt/send/combined/"

path_to_file=$target
current_file="ORACLE_gprtcp_000.log.gz"

fileList=$(find $sourceFolder -type f -name '*.log.gz' -printf '%T@ %p\n' | sort -nb)
## for debugging:
# fileList=$(find $PWD -type f -printf '%T@ %t %p\n' | sort -nb)

echo ${fileList[0]}

org_IFS=$IFS
IFS=$'\n'
group_start_time=0

for line in $fileList; do
    current_time=$(echo $line | awk '{print $1}')
    if [ $group_start_time -eq 0 ] ; then
        group_start_time=$current_time
        hr_time=$( date -d @$current_time +%F_%0k%0M )
        current_file="ORACLE_gprtcp_"$hr_time".log.gz"
    else
        delta=$(($current_time - $group_start_time))
        #echo $delta
        if [ $delta -lt 1801 ] ; then
            # just append file path to current_file
            echo $line | awk '{print $2}' >> $path_to_file"/"$current_file
            echo $line
        else
            # construct new filename based on time of the first member of the group
            hr_time=$( date -d @$current_time +%F_%0k%0M )
            current_file="ORACLE_gprtcp_"$hr_time".log.gz"

            # create file, append file path to current_file
            echo $line | awk '{print $2}' >> $path_to_file"/"$current_file
            echo -e "\nnew group:\n$line"

            group_start_time=$current_time
        fi
    fi
done

IFS=$org_IFS
    
por 02.10.2012 / 18:28
3

Supondo que não exista nenhum caractere "\ n" nos nomes dos arquivos:

find . -name '*_*_[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_*.gz' | perl -le '
    use strict;
    use warnings;
    my %hash;
    while(<>) {
        chomp;
        my($group)=/^([^_]+_[^_]+_[0-9]{11})/;
        $group=~s/[0-2]$/00/;
        $group=~s/[3-5]$/30/;
        push @{$hash{$group}},$_;
    }
    while(my($group,$files_arr_ref)=each%hash) {
        print "processing group $group";
        for my$file (sort @{$files_arr_ref}) {
            print "processing file $file";
            # do system command calls here; for example
            # system "gzip -cd \"$file\" >> $group.txt";
        }
    }
' 

Editar: algumas alterações após as sugestões de Craig. A primeira ideia foi apenas usar o perl para matrizes e hashes, e finalmente é mais claro fazer tudo. @ARGV é a lista de caminhos a serem percorridos para encontrar. Por exemplo, se o nome do script for script.pl:

script.pl ${sourceFolder}

#!/usr/bin/perl

use strict;
use warnings;
use File::Find;

my %hash;

sub wanted {
    return unless /^([^_]+_[^_]+_[0-9]{11})/;
    my$group=$1;
    $group=~s/[0-2]$/00/;
    $group=~s/[3-5]$/30/;
    push @{$hash{$group}},$_;
}

File::Find::find(\&wanted, @ARGV);

while(my($group,$files_arr_ref)=each%hash) {
    print "processing group $group\n";
    ### do system command calls here; for example
    # system "rm $group.txt";
    ### or just use perl
    # unlink $group.'.txt';
    for my$file (sort @{$files_arr_ref}) {
         print "processing file $file\n";
         ### and other system command calls here; for example
         # system "gzip -cd $file >> $group.txt";
    }
    ### and here; for example
    # system "gzip $group.txt";
}
    
por 03.10.2012 / 11:22