Executando um Comando em Muitos Arquivos

19

Eu tenho uma pasta com muitos arquivos (xyz1, xyz2, até xyz5025) e preciso executar um script em cada um deles, obtendo xyz1.faa, xyz2.faa e assim por diante.

O comando para um único arquivo é:

./transeq xyz1 xyz1.faa -table 11

Existe uma maneira de fazer isso automaticamente? Talvez uma combinação de tarefas a fazer?

Manuel
fonte

Respostas:

32
for file in xyz*
do
  ./transeq "$file" "${file}.faa" -table 11
done

Esse é um forloop simples que itera sobre todos os arquivos iniciados xyzno diretório atual e chama o ./transeqprograma com o nome do arquivo como o primeiro argumento, o nome do arquivo seguido por ".faa" como o segundo argumento, seguido pela "-table 11" .

Jeff Schaller
fonte
4
Ou, como um one-liner: for file in xyz*; do ./transeq "$file" "${file}.faa" -table 11; done. Eu digito esse tipo de coisa o tempo todo. E se você deseja verificar se os nomes dos arquivos etc. estão sendo expandidos da maneira que deseja, basta colocar um echologo após doa primeira vez e, em seguida, voltar ao histórico do shell e excluí-lo na segunda vez.
21417 Dave McMillan -
"$file".faaé um pouco mais fácil de digitar como parte de uma linha interativa e seguro, porque .faanão contém nenhum metacaractere de shell que precisa ser citado.
Peter Cordes
2
Como observação, se você terminar com uma execução parcial e quiser reiniciar o loop, o xyz*glob também coletará arquivos .faa. Para o bash, execute shopt -s extglob( referência ) e use for file in xyz!(*.faa) ...para excluir os arquivos .faa do envio pelo loop.
Jeff Schaller
24

Se você instalar o GNU Parallel, poderá fazê-lo em paralelo da seguinte maneira:

parallel ./transeq {} {}.faa -table 11 ::: xyz*

Se você programar com muita CPU, deve acelerar um pouco.

hschou
fonte
6

Você pode fazer algo assim em uma bashlinha de comando:

printf '%s\n' {1..5025} | xargs -l -I {} -t ./transeq xyz{} xyz{}.faa -table 11

Estamos gerando os números inteiros de 1 a 5025, um / linha, e os alimentando um a um para xargs, que encapsula o número inteiro {}e o transplanta na linha de comando ./transeq de maneira apropriada.

Se você não tiver o recurso de expansão de chaves {n..m}, poderá chamar o sequtilitário para gerar esses números.

Ou você sempre pode emular a geração numérica via:

yes | sed -n =\;5025q | xargs ...

fonte
11
Isso é muito complicado. for i in {1..5025}; do ./transeq "xyz$i" "xyz$i".faa -table 11; doneé muito mais fácil pensar e digitar. Se você deseja imprimir comandos antes de executá-los, use set -x.
Peter Cordes
Sim, está correto, mas a maneira como o OP formulou a pergunta me pareceu que apenas os arquivos com os nomes xyz1 .. xyz5025 eram interessantes. Então, pensei que, se fizermos isso usando o xyz *, precisamos de uma maneira de rejeitar os arquivos não conformes ... daí isso. Idealmente, se o OP deseja que todos os arquivos em um diretório sejam processados, por que trazer a coisa de 1 a 5025? Basta dizer que eu quero que todos os arquivos processados ​​da maneira prescrita sejam suficientes.
11
Veja o laço que escrevi. Ele usa for i in {1..5025}para obter exatamente o mesmo resultado que o seu. Você também pode escrever for ((i=1 ; i<=5025 ; i++)); do ./transeq "xyz$i" "xyz$i".faa -table 11; doneno bash, mas eu geralmente uso a {a..b}sintaxe de intervalo, porque é mais rápido digitar.
Peter Cordes
4

Usando find, útil quando seus arquivos estão espalhados dentro de diretórios

find -name "xyz*" -exec ./transeq {} {}.faa -table 11 \;
Pelle
fonte
4

Supondo que você tenha mais de um núcleo e que cada chamada possa ser executada independentemente do restante, você ganhará bastante aceleração com execuções paralelas.

Uma maneira relativamente simples de fazer isso é através do -Pparâmetro xargs- por exemplo, se você tiver 4 núcleos:

echo xyz{1..5025} | \
    xargs -n 1 -P 4 -I{} /path/to/transeq xyz{} xyz{}.faa -table 11

O comando -n 1diz xargspara escolher apenas um argumento da lista para cada invocação (por padrão, ele passaria bastante) e o comando -P 4para gerar 4 processos ao mesmo tempo - quando um morre, um novo é gerado.

IMHO, você não precisa instalar o GNU paralelo para este caso simples - xargsbasta.

ttsiodras
fonte
0

Você pode usar xarg

ls | xargs -L 1 -d '\n' your-desired-command

-L 1 faz passar 1 item de cada vez

-d '\n'make output of lsé dividido com base na nova linha.

Al Mamun
fonte