apaga linhas em um arquivo csv com mais de 7 dias

2

Eu tenho um csv que preciso remover todas as linhas com mais de 7 dias. Este é o formato do csv

Person ID  VIP  CS  SS  LT  FTLT  PS  Modified Datestamp
T001028    1    1   1   0   0     0   05-07-2013
T001250    1    1   1   0   0     0   08-05-2012
T001261    1    1   1   0   0     1   04-04-2013
T001345    1    1   1   0   0     0   04-03-2013
T078503    1    1   1   0   0     0   04-03-2013
T079819    1    1   1   0   0     1   3/22/2013
T080119    1    1   1   0   0     1   04-02-2013
T090574    1    1   1   0   0     0   11/15/2012
T091106    1    1   1   0   0     1   3/22/2013

O formato da coluna Data Modificada é MM / DD / AAAA - Qualquer Idéia ... está procurando executar algo em uma caixa linux RedHat 5.

    
por Simon Ellis 05.07.2013 / 04:22

5 respostas

2

Aqui está um método usando tail , date -d ... , awk e apenas a funcionalidade do Bash.

tail -n+2 file.csv | {
  while read line ; do
    tmstmp=$(echo "$line" | awk '{print $8}');
    [ $(( $(date -d "now" +%s) - $(date -d "$tmstmp" +%s) )) -lt $(( 60*60*24*7 )) ] && echo "$line";
  done;
}

Como funciona?

O texto acima analisa as linhas de um arquivo, file.csv , e obtém a oitava coluna (a data) e, em seguida, calcula o delta entre o número de segundos desde a época para agora e a data analisada. Se tiverem menos de 7 dias em segundos, a linha será impressa.

Depuração

Você pode colocar essa linha para depurar o que está acontecendo. Coloque logo após a linha tmpstmp=... :

echo "TMSTMP: $tmstmp" "TMDELTA: $(( $(date -d "now" +%s) - $(date -d "$tmstmp" +%s) ))" "TMWINDOW: $(( 60*60*24*7 ))"

Exemplo

Para simplificar, coloquei o código acima em um script e chamei-o de rprttime.bash :

#!/bin/bash

tail -n+2 file.csv | {
  while read line ; do
    tmstmp=$(echo "$line" | awk '{print $8}');
    echo "TMSTMP: $tmstmp" "TMDELTA: $(( $(date -d "now" +%s) - $(date -d "$tmstmp" +%s) ))" "TMWINDOW: $(( 60*60*24*7 ))"
    [ $(( $(date -d "now" +%s) - $(date -d "$tmstmp" +%s) )) -lt $(( 60*60*24*7 )) ] && echo "$line";
  done;
}

Agora, quando executamos:

$ ./rprttime.bash
TMSTMP: 05/07/2013 TMDELTA: 5157421 TMWINDOW: 604800
TMSTMP: 08/05/2012 TMDELTA: 28917421 TMWINDOW: 604800
TMSTMP: 04/04/2013 TMDELTA: 8008621 TMWINDOW: 604800
TMSTMP: 04/03/2013 TMDELTA: 8095021 TMWINDOW: 604800
TMSTMP: 04/03/2013 TMDELTA: 8095021 TMWINDOW: 604800
TMSTMP: 3/22/2013 TMDELTA: 9131821 TMWINDOW: 604800
TMSTMP: 04/02/2013 TMDELTA: 8181421 TMWINDOW: 604800
TMSTMP: 11/15/2012 TMDELTA: 20101021 TMWINDOW: 604800
TMSTMP: 3/22/2013 TMDELTA: 9131821 TMWINDOW: 604800

Se você alterar a janela de tempo de 7 para 60 dias e desativar a linha de depuração, verá algumas das linhas impressas:

$ date
Fri Jul  5 16:49:19 EDT 2013

$ ./rprttime.bash
T001028    1    1   1   0   0     0   05/07/2013
    
por 05.07.2013 / 22:47
2

Ainda outra resposta com garfos limitados

Como há muitos forks para eliminar, há uma <-href="/ questions / tagged / bash" class="post-tag" title="show questions tagged ' bash '"> bash maneira de fazer isso usando sed e apenas 1 fork para /bin/date :

sedstr=""
{
    i=1;
    read now;
    while read line;do
        ((i++));
        [ $(( (now-line) /86400 )) -gt 143 ] && sedstr="${i}d;$sedstr"
      done
}< <(
    sed -ne $'s/^.*[ \t,]//g;y|-|/|;/[0-9]$/p;1inow' < file.tsv |
        date -f - +%s
)
sed -e "$sedstr" < file.tsv
Person ID   VIP CS  SS  LT  FTLT    PS  Modified Datestamp
T001028     1   1   1   0   0   0   05-07-2013
T001261     1   1   1   0   0   1   04-04-2013
T001345     1   1   1   0   0   0   04-03-2013
T078503     1   1   1   0   0   0   04-03-2013

O último comando sed pode ser usado com -i para modificação inplace em vez de saída para console.

echo $sedstr 
10d;9d;8d;7d;3d;

sed -e $sedstr -i file.tsv 
cat file.tsv 
Person ID   VIP CS  SS  LT  FTLT    PS  Modified Datestamp
T001028     1   1   1   0   0   0   05-07-2013
T001261     1   1   1   0   0   1   04-04-2013
T001345     1   1   1   0   0   0   04-03-2013
T078503     1   1   1   0   0   0   04-03-2013
    
por 24.08.2013 / 08:59
1

Se você calcular a data até a qual as linhas serão ignoradas antes de chamar o awk, você poderá fazer isso:

awk -v cmpdate=20130628 '{line=$0; dateval=$8;FS="/"; $0=dateval; 
  thisdate=$3*10000+$1*100+$2; if (thisdate>cmpdate) print line; FS=" ";}' file

Editar 1:

Redefina FS para seu valor original no final. Eu testei meu código com apenas uma linha de entrada para que não fizesse diferença ...

    
por 05.07.2013 / 04:42
1

Como não parece haver uma resposta melhor do que isso (todas as soluções propostas farão um fork para /bin/date para cada linhas), existe uma maneira limpa e eficiente de fazer o trabalho, mas usando perl.

Questão para csv (valor separado por coma) e presente tsv (tab sep vals), assim meu script funcionará para qualquer tipo de tab , coma ou espaço valores separados (basta procurar pelo último campo).

Nenhum fork, perl fará a data se analisar:

#!/usr/bin/perl -w

use strict;
use Date::Parse;

my $day=7;
if ($ARGV[0] && $ARGV[0] > 0) { $day=$ARGV[0]; shift; };

while (<>) {
    my @fields=split(/[ \t,]/);
    print if /^Person/ || str2time($fields[$#fields]) > time()-$day*86400;
};

U poderia rodar isto sem argumento, como um filtro ou com um nome de arquivo como argumento:

./dropOlderThan.pl < file.tsv
Person ID   VIP CS  SS  LT  FTLT    PS  Modified Datestamp

./dropOlderThan.pl file.tsv
Person ID   VIP CS  SS  LT  FTLT    PS  Modified Datestamp

Se o primeiro argumento for um número, eles serão interpretados como o número de dias para manter na saída:

./dropOlderThan.pl 144 file.tsv
Person ID   VIP CS  SS  LT  FTLT    PS  Modified Datestamp
T001028     1   1   1   0   0   0   05-07-2013
T001261     1   1   1   0   0   1   04-04-2013
T001345     1   1   1   0   0   0   04-03-2013
T078503     1   1   1   0   0   0   04-03-2013

E, finalmente, se você quiser modificar o arquivo:

perl -i dropOlderThan.pl 144 file.tsv 
cat file.tsv 
Person ID   VIP CS  SS  LT  FTLT    PS  Modified Datestamp
T001028     1   1   1   0   0   0   05-07-2013
T001261     1   1   1   0   0   1   04-04-2013
T001345     1   1   1   0   0   0   04-03-2013
T078503     1   1   1   0   0   0   04-03-2013
    
por 24.08.2013 / 08:34
0

Eu faria isso com o Perl (execute isso no terminal):

$ perl -lane 'BEGIN{$date='date +%s'; chomp($date)}    
            if($.==1){print}                         
            else{
                 $F[$#F]=~s/-/\//g; 
                 $fdate='date -d "$F[$#F]" +%s';     
                 chomp($fdate);                      
            print if $date-$fdate<604800;     
          }' file.csv

Esse script funciona calculando a data de hoje em segundos desde a época e traduzindo a data de cada linha para a mesma formato, subtraindo-o da data de hoje e imprimindo somente se tiver menos de 7 dias de idade (7 * 24 * 60 * 60 = 604800).

NOTAS

  • O script funciona com segundos, isso pode ser um pouco preciso demais para o que você tinha em mente. Deixe-me saber se sim, e vou modificá-lo para trabalhar no nível de dias.

  • Também estou convertendo MM-DD-YYYY para MM/DD/YYYY em tempo real, isso pode não ser necessário se o arquivo de entrada for formatado de forma homogênea, mas necessário para o exemplo que você publicou.

por 05.07.2013 / 15:14