Como ir para cada diretório e executar um comando em um arquivo específico?

1

Como escrevo um script bash que passa por cada diretório dentro de um parent_directory e executa um comando em um arquivo específico.

A estrutura de diretórios é a seguinte:

Parent_dir/
  dir1/
   acc.bam
  dir2/
   acc.bam
  dir3/
   acc.bam
... around 30 directories

Este é o comando que eu quero usar:

java8 -jar /picard.jar CollectRnaSeqMetrics REF_FLAT=/refFlathuman.refflat STRAND_SPECIFICITY=NONE I=acc.bam O=output
    
por user3351523 13.04.2016 / 19:40

5 respostas

7

O idioma usual é

for d in Parent_dir/*/
do
     (cd "$d" && $command)
done

O loop for é executado uma vez para cada diretório diretamente em Parent_dir . Para cada um desses diretórios, um sub-shell é gerado; no sub-shell, tentamos mudar para esse diretório (o que pode falhar, por exemplo, se tivermos permissão insuficiente) e, se tivermos sucesso, executar o comando. Quer tenhamos ou não conseguido, o cd não tem efeito no shell pai, por isso não precisamos nos preocupar em estar no lugar errado.

Se você quiser torná-lo mais robusto, você pode

    (cd "$d" && test -r acc.bam &&  $command)

para garantir que acc.bam exista e seja legível nesse diretório. Você também pode adicionar um test -w . para evitar a execução do comando em diretórios que não são graváveis.

P.S. Nenhuma das opções acima é específica da Bash; você pode usar / bin / sh para portável.

    
por 13.04.2016 / 23:32
2

Faça o loop sobre os arquivos no diretório pai com for FILE in *; do ...; done . Se o arquivo for um diretório, cd no diretório, execute seu comando e, em seguida, cd .. .

cd $parent_directory
for FILE in *; do
    if [ -d "$FILE" ]; then
        cd "$FILE"
        java8 -jar /picard.jar CollectRnaSeqMetrics REF_FLAT=/refFlathuman.refflat STRAND_SPECIFICITY=NONE I=acc.bam O=output
        cd ..
    fi
done

Você também pode usar o nome do arquivo em seu comando para evitar a alteração de diretórios como I="$FILE/acc.bam" .

    
por 13.04.2016 / 20:09
2

Primeiro, certifique-se de que, de fato, é necessário ter cada um desses diretórios como o diretório de trabalho para uma chamada do comando. Programas bem escritos não se importam com o local de seu diretório de trabalho, contanto que tenham caminhos de trabalho para seus argumentos (e, se necessário, adequados $TMPDIR etc.).

Se o seu comando funcionar com nomes de caminho reais, como

java8 -jar /picard.jar CollectRnaSeqMetrics \
    REF_FLAT=/refFlathuman.refflat STRAND_SPECIFICITY=NONE \
    I=subdir/acc.bam O=subdir/output

então você deve ser capaz de simplesmente substituir o diretório em cada iteração de um loop:

const_args='CollectRnaSeqMetrics REF_FLAT=/refFlathuman.refflat STRAND_SPECIFICITY=NONE'
for d in Parent_dir/*/
do
     java8 -jar /picard.jar $const_args I="${d}acc.bam" O="${d}output"
done

(note que, porque $d inclui um / à direita, não adicionei um em que é substituído no comando).

P.S. Nenhuma das opções acima é específica da Bash; você pode usar / bin / sh para portável.

    
por 13.04.2016 / 23:45
2

Tudo o que você precisa fazer é ir a todos os diretórios em seu parent_directory e executar o comando java. Para retornar ao uso do parent_directory:

cd ..

Para obter a lista de diretórios em parent_directory, você pode usar:

find -mindepth 1 -maxdepth 2 -type d

Ele listará apenas os diretórios e apenas o nível de aninhamento.

O loop é assim:

cmd="java8 -jar /picard.jar CollectRnaSeqMetrics REF_FLAT=/refFlathuman.refflat STRAND_SPECIFICITY=NONE I=acc.bam O=output"
for path in 'find -mindepth 1 -maxdepth 2 -type d' 
do
    cd "$path"
    $cmd
    cd ..
done

O comando Java foi colocado em uma variável separada para tornar o script mais legível.

    
por 13.04.2016 / 20:18
2

Com find implementações que suportam -execdir (BSD, GNU, sfind pelo menos):

find ParentDir -name acc.bam -execdir \
  java8 -jar /picard.jar CollectRnaSeqMetrics \
  REF_FLAT=/refFlathuman.refflat \
  STRAND_SPECIFICITY=NONE I=acc.bam O=output \;
    
por 14.04.2016 / 16:00

Tags