Adicionando o conteúdo do find ao array

0

Atualmente, tenho um script que gera comandos R com base na saída de find

#!/bin/bash
PATHX="/path/to/my/files"
find "${PATHX}" -maxdepth 1 -type f -name "*.csv" | while read d; do
FN=$(echo -n "${d}" | cut -d/ -f5 | cut -d. -f1)
echo "${FN}<-read.csv(\"${PATHX}/${FN}.csv\",header=TRUE)"
# <snip> etc .etc. etc.
echo "${FN}_2y<-tail(${FN}_log,730)"
done

Isso funciona muito bem. Mas eu tenho um problema com um comando R:

df<-data.frame(list,of,columns,goes,here)

Eu não consigo descobrir como posso integrar isso ao find / while acima, ou seja, eu preciso mostrar uma lista de $ {FN} _2y na função data.frame ().

Por exemplo, suponha que meu script seja o resultado:

  • a_2y
  • b_2y
  • c_2y

Eu preciso terminar com df < -data.frame (a_2y, b_2y, c_2y)

Esclarecendo ainda mais a questão nos comentários, eu só preciso de uma instância de data.frame, bem no final assim que todas as entradas csv tiverem sido analisadas.

    
por Little Code 12.07.2016 / 13:52

2 respostas

1

Esse tipo de coisa é mais fácil de fazer em awk ou perl do que em um script de shell (embora se você estiver usando um sh como bash which suporta matrizes, é um pouco mais fácil do que usar um sh sem arrays.Você ainda tem muito mais complicações com aspas e globbing ou expansão onde você não quer em um shell script do que em perl ou awk )

Por exemplo:

#!/usr/bin/perl

use strict;

my $pathx='/path/to/my/files';

my $dh;
my @frames=();

# get list of .csv files from $pathx
opendir($dh, $pathx) || die "can't open directory '$pathx': $!\n";
my @csvfiles = grep { /\.csv$/ && -f "$pathx/$_" } readdir($dh);
closedir($dh);

foreach my $f (@csvfiles) {
   my @fields=split(/\./,$f);
   my $fn=$fields[@fields-2];   # perl array indices start from 0, not 1.

   printf '%s<-read.csv("%s",header=TRUE)'."\n", $fn, "$pathx/$f";
   # <snip> etc .etc. etc.
   printf '%s_2y<-tail(%s_log,730)'."\n", $fn, $fn;

   push @frames,"${fn}_2y";
}

print "df-<data.frame(", join(',',@frames), ")\n";

NOTA: você pode usar o módulo File::Find em vez de um simples readdir() se precisar de uma recursão de diretório.

Exemplo de saída (com os arquivos a.csv , b.csv e c.csv ):

a<-read.csv("/path/to/my/files/a.csv",header=TRUE)
a_2y<-tail(a_log,730)
b<-read.csv("/path/to/my/files/b.csv",header=TRUE)
b_2y<-tail(b_log,730)
c<-read.csv("/path/to/my/files/c.csv",header=TRUE)
c_2y<-tail(c_log,730)
df-<data.frame(a_2y,b_2y,c_2y)

ou com awk :

NOTA: o awk não tem uma função join() , então eu tive que escrever um. awk também não tem uma função readdir() , então é mais fácil apenas enviar a saída de find para ela (escreva um script wrapper sh para fazer isso se necessário).

#!/usr/bin/awk -f

BEGIN {
  FS="[./]";
  delete A; # has side-effect of defining A as an array
};   

# i isn't an argument to this function, it's a local variable.
# in awk, extra whitespace separates function args from declaration
# of local variable(s)

function join(array,sep,       i) {     
  result=array[1];     # awk array indices start from 1
  for (i=2;i<=length(array);i++) result = result sep array[i];
  return result;
};

# main code block, run on every input line
{
  fn=$(NF-1);
  printf "%s<-read.csv(\"%s\",header=TRUE)\n", fn, $0;
  # <snip> etc .etc. etc.
  printf "%s_2y<-tail(%s_log,730)\n", fn, fn;
  A[length(A)+1] = sprintf("%s_2y",fn);
};

END {
  print "df-<data.frame(" join(",",A) ")";
}

salve como, por exemplo, myscript.awk , torne-o executável com chmod e execute como:

find "${PATHX}" -maxdepth 1 -type f -name "*.csv" | ./myscript.awk

A saída é idêntica à versão perl .

Finalmente, o mesmo algoritmo no bash:

#!/bin/bash

PATHX="/path/to/my/files"

declare -a frames=()
# get list of .csv files and store in array csvfiles.
csvfiles=( $(find "$PATHX" -maxdepth 1 -type f -name '*.csv' ) )

function join() {
  local sep result i
  sep="$1" ; shift
  result="$1" ; shift

  for i in "$@" ; do result="$result$sep$i" ; done
  printf '%s' "$result"
}

for f in "${csvfiles[@]}" ; do
  fn=$(basename "$f" '.csv')

  printf "%s<-read.csv(\"%s\",header=TRUE)\n" $fn $f;
  # <snip> etc .etc. etc.
  printf "%s_2y<-tail(%s_log,730)\n" $fn $fn;

  frames+=( "${fn}_2y" )
done

echo 'df-<data.frame('$( join ',' "${frames[@]}" )')';

Isso evita um loop while read , que é quase sempre a pior maneira possível de processar uma série de linhas em um shell script. Use o laço awk ou perl ou sed ou for em torno de uma matriz - qualquer coisa para evitar o uso de um loop while read .

    
por 13.07.2016 / 05:31
2

Você pode coletar os nomes em uma variável fns e fazer eco disso no final. Como você tem um pipe, precisa manter a variável no mesmo subshell que o while / do / done. ${fns:1} é uma subseqüência da variável, perdendo a vírgula extra inicial.

#!/bin/bash
PATHX="/path/to/my/files"
find "${PATHX}" -maxdepth 1 -type f -name "*.csv" |
(   fns=
    while read d; do
        FN=$(echo -n "${d}" | cut -d/ -f3 | cut -d. -f1)
        echo "${FN}<-read.csv(\"${PATHX}/${FN}.csv\",header=TRUE)"
        # <snip> etc .etc. etc.
        echo "${FN}_2y<-tail(${FN}_log,730)"
        fns+=",${FN}_2y"
    done
    echo "df<-data.frame(${fns:1})"
)
    
por 12.07.2016 / 14:08

Tags