Como adicionar coluna no início do arquivo usando perl?

3

Eu quero um one-liner Perl que verifique se os primeiros campos de um arquivo de entrada é o nome do arquivo e, se não for, adiciona o nome do arquivo como a primeira coluna em todas as linhas.

Exemplo escrito em shell:

for f in *file*.csv;
do 
  file_column='cat ${f} | awk -F',' '{print$1}''
  if [ $file_column != ${f} ]
  then
    sed -i "s/^/$f,/" $f 2>/dev/null;
  fi 
done

Mas a abordagem acima, que verifica se o nome do arquivo está presente na primeira coluna e o adiciona se não estiver, está levando ~ 3 Hours para arquivos de 4 Laks. Eu entendo que o Perl é mais rápido para operações de arquivos.

O comando Perl que eu tentei:

perl -p -i -e 's/^/Welcome to Hell,/' file*.csv

Por favor, ajude-me a adicionar a lógica para verificar se o campo já existe e só mudará se não existir.

Input : file1.csv 
col1,col2,col3 
data1,data2,dat3 

Output: file1.csv 
file1.csv,col1,col2,col3 
file1.csv,data1,data2,data3

ou se aqui estiver alguma maneira mais rápida, por favor sugira. Perl one liner porque é parte de outro script de shell, então uma chamada minúscula será melhor, eu acho (sugira, por favor)

    
por William R 24.06.2015 / 12:17

5 respostas

3

Aqui está o seu perl one-liner: funciona com vários argumentos de arquivo

perl -i -pe '/^$ARGV,/ or print "$ARGV,"' file1 file2 ...

$ARGV é a variável mágica que contém o nome do arquivo atual.
Consulte o link

O separador de campos (vírgula) é codificado. Você pode decidir se isso é um problema.

Melhoria de desempenho pequena:

perl -i -pe 'index($_, "$ARGV,") == 0 or print "$ARGV,"' file1 file2 ...
    
por 24.06.2015 / 16:20
2

Antes de falar sobre a velocidade do perl, tente acelerar o seu próprio script

for f in *file*.csv;
do 
    sed -i "/^$f,/! s/^/$f,/" "$f"
done
    
por 24.06.2015 / 12:48
2

Embora você possa fazer isso com o Perl, a sintaxe não é a mais simples (ou, pelo menos, não é com o melhor que posso fazer). Provavelmente será mais simples e rápido usar outras ferramentas. Por exemplo,

  1. sed

  2. gawk (versões relativamente recentes)

    for f in file*csv; do  
        awk -i inplace -F, '{
                              if($1==FILENAME){print} else{print FILENAME","$0}
                            }' "$f"; 
    done
    
por 24.06.2015 / 15:32
1

OK, o problema com um 'perl one liner' como você nota:

perl -p -i -e 's/^/Welcome to Hell,/' file*.csv

Isso aplica uma transformação ao arquivo corretamente, mas perl 'lida' abrindo o (s) arquivo (s) e transmitindo-os através de STDIN automagicamente. O que significa que você não sabe o nome do seu arquivo quando está fazendo isso.

A opção in place edit ( -i ) é uma conveniência, mas na verdade se torna um pouco mais difícil de usar efetivamente, já que você está potencialmente abrindo um arquivo para leitura e gravação simultaneamente.

De qualquer forma, eu abordaria seu problema assim:

#!/usr/bin/perl

use strict;
use warnings;

use Text::CSV;

my $csv = Text::CSV->new( { binary => 1 } );

foreach my $filename ( glob("*.csv") ) {
    open( my $output, ">", "new.$filename.csv" ) or warn $!;
    open( my $input, "<", "$filename.csv" ) or warn $!;
    while ( my $row = $csv->getline($input) ) {
        if ( not $row->[0] eq m/$filename/ ) {
            unshift( @{$row}, $filename );
        }
        $csv->print( $output, $row );
    }
}

Ele usa o módulo Text::CSV , porque na verdade o CSV é geralmente mais complicado do que apenas "dividido em vírgula" (pense em campos de várias linhas e vírgulas no texto).

    
por 24.06.2015 / 12:49
1

Não é possível gerenciar um único liner, mas aqui está um script perl . Coloque em um arquivo e torná-lo executável. Em seguida, atribua a ela *.csv nomes de arquivos como args. isto cria arquivos *.new . Se você está confiante de que funciona, descomente o Comando rename no final.

#!/usr/bin/perl
use strict;
foreach my $file(@ARGV){
    open(F,$file) or die "$file:$!";
    $_ = <F>;
    next if $_=~/^$file,/;
    open(OUT,">$file.new") or die;
    my $add = "$file,";
    print OUT $add,$_;
    while(<F>){
        print OUT $add,$_;
    }
    close OUT;
    close F;
    #rename("$file.new","$file");
}
    
por 24.06.2015 / 14:52

Tags