Script de shell, percorra pastas

1

Eu tenho uma grande série de subpastas no meu Mac com uma quantidade aleatória de arquivos csv em cada um. O que eu gostaria de fazer é mesclar esses arquivos em um único arquivo para cada diretório.

Até agora eu sei que posso mesclar esses arquivos com cat * > mergedfile.csv , mas estou tendo problemas para percorrer todas as pastas. De alguma forma eu consegui mesclar todos os tipos de coisas até agora, mas não consigo fazer isso fazer exatamente o que eu quero.

Alguma ideia sobre a melhor maneira de fazer isso?

for DIR in ./subfolder/*
do
    cat $dir/* > merged.csv 
done
    
por David M 01.12.2011 / 13:50

3 respostas

3

Com find , você pode listar recursivamente todos os arquivos que correspondem a um determinado critério, por exemplo, o nome do arquivo.

for file in $(find . -type f -name "*.csv"); do cat "$file" >> /path/to/output.csv; done

Quebrando, find . -name "*.csv" encontrará todos os arquivos CSV da pasta atual em que você está ( . ), e o loop apenas fará uma iteração sobre essa lista, anexando tudo ao arquivo output.csv .

Mas: Nomes de arquivos com espaços, caracteres globbing e novas linhas podem ser complicados aqui. Uma solução mais segura seria usar apenas exec para o comando find.

find . -name "*.txt" -exec cat '{}' >> /path/to/output.csv ';'

Aqui, '{}' será substituído pelo nome do arquivo. Por um longo Q & A sobre por que isso é e como contornar o problema pode ser encontrado aqui .

Agora, se você quiser criar um arquivo CSV para cada diretório - desculpe, não vi isso antes -, provavelmente faria algo assim:

for dir in $(find . -type d); do find $dir -maxdepth 1 -name "*.csv" -exec cat {} >> "$dir/out" ';'; mv "$dir/out" "$dir/merged.csv"; done

Embora a solução de Franck abaixo seja provavelmente mais eficiente.

Claro, preste atenção à diferença entre > e >> . O primeiro sempre truncará o arquivo com comprimento zero antes de gravar nele, enquanto o segundo apenas anexará ao arquivo.

A razão pela qual cat *.csv > merged.csv funcionou - e por que, no seu loop, não funcionará - é que o shell expandirá o curinga antes, então basicamente ele vê:

cat file1.csv file2.csv file3.csv > merged.csv

… o que obviamente não substituirá nada.

    
por 01.12.2011 / 13:54
1

Na pasta pai:

for dir in $(find . -type d); do
  cd $dir
  [[ $(ls *.csv|wc -l) -eq 0 ]] 2> /dev/null || { print "$dir.csv created";
                                                  cat *.csv > $dir.csv; }
  cd - > /dev/null
done
    
por 01.12.2011 / 15:51
1

Assumindo o bash 4+ (verifique com bash --version ), você pode ativar o globstar com shopt -s globstar e percorrer todos os diretórios (e apenas diretórios - o arrastamento / exclui os arquivos) recursivamente com **/

for f in **/; do cat "$f"/*.csv > "$f"/merged.csv; done

Se você realmente deseja usar todos arquivos em um diretório, em vez de apenas aqueles que terminam em .csv ,

for f in **/; do cat "$f"/* > "$f"/merged.csv; done

Se você deseja apenas descer um único nível, em vez de ser totalmente recursivo, use */ em vez de **/ .

O erro chave no script OP (além de esquecer que o bash faz distinção entre maiúsculas e minúsculas) é que ele tenta gravar o conteúdo de todos os arquivos em um único arquivo .csv , e de tal forma que cada iteração do loop iria sobrescrever o último.

Se você quisesse concatenar todos os arquivos .csv recursivamente em um único arquivo, você poderia usar novamente globstar

for f in **/*.csv; do cat "$f" > merged_all.csv
    
por 28.05.2013 / 04:56

Tags