Ejecutar un comando en muchos archivos

19

Tengo una carpeta con muchos archivos (xyz1, xyz2, hasta xyz5025) y necesito ejecutar un script en cada uno de ellos, obteniendo xyz1.faa, xyz2.faa, etc. como salidas.

El comando para un solo archivo es:

./transeq xyz1 xyz1.faa -table 11

¿Hay alguna manera de hacer eso automáticamente? Tal vez un combo para hacer?

Manuel
fuente

Respuestas:

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

Este es un forbucle simple que iterará sobre cada archivo que comienza xyzen el directorio actual y llamará al ./transeqprograma con el nombre de archivo como primer argumento, el nombre de archivo seguido de ".faa" como segundo argumento, seguido de "-table 11" .

Jeff Schaller
fuente
44
O, como una sola línea: for file in xyz*; do ./transeq "$file" "${file}.faa" -table 11; done. Escribo este tipo de cosas todo el tiempo. Y si desea verificar que los nombres de archivo, etc., se expanden de la manera que desea, simplemente coloque un echoderecho después de dola primera vez, y luego regrese a su historial de shell y elimínelo la segunda vez.
Dave Tweed
"$file".faaes un poco más fácil de escribir como parte de una línea interactiva y segura, ya .faaque no contiene metacaracteres de shell que necesitan ser citados.
Peter Cordes
2
Como nota, si termina con una ejecución parcial y desea reiniciar el bucle, el xyz*globo también recogerá archivos .faa. Para bash, ejecute shopt -s extglob( referencia ), luego use for file in xyz!(*.faa) ...para excluir que los archivos .faa se envíen a través del bucle.
Jeff Schaller
24

Si instala GNU Parallel , puede hacerlo en paralelo de esta manera:

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

Si su programa consume mucha CPU, debería acelerarse un poco.

hschou
fuente
6

Puede hacer algo como esto en una bashlínea de comando:

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

Estamos generando los enteros del 1 al 5025, uno / línea, luego los alimentamos uno por uno a xargs, que encapsula el entero en {}y luego lo trasplanta a la línea de comando ./transeq de manera apropiada.

Si no tiene la función de expansión de llaves, {n..m}puede invocar la sequtilidad para generar esos números.

O bien, siempre puede emular la generación numérica a través de:

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

fuente
1
Eso es demasiado complicado. for i in {1..5025}; do ./transeq "xyz$i" "xyz$i".faa -table 11; donees mucho más fácil pensar y escribir. Si desea que imprima comandos antes de ejecutarlos, use set -x.
Peter Cordes
Sí, eso es correcto, pero la forma en que el OP formuló la pregunta me pareció que solo los archivos con los nombres xyz1 .. xyz5025 eran de interés. Así que pensé que si lo hacemos usando xyz *, entonces necesitamos una forma de rechazar los archivos no conformes ... de ahí esto. Idealmente, si el OP quiere que se procesen todos los archivos en un directorio, entonces ¿por qué mostrar la cosa de 1 a 5025? Simplemente diga que quiero que todos los archivos procesados ​​de una manera prescrita hubieran sido suficientes.
1
Mira el bucle que escribí. Se utiliza for i in {1..5025}para lograr exactamente el mismo resultado que el tuyo. También podría escribir for ((i=1 ; i<=5025 ; i++)); do ./transeq "xyz$i" "xyz$i".faa -table 11; doneen bash, pero generalmente uso la {a..b}sintaxis de rango porque es más rápido de escribir.
Peter Cordes
4

Usando find, útil cuando sus archivos están dispersos dentro de directorios

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

Suponiendo que tiene más de un núcleo, y cada invocación puede ejecutarse independientemente del resto, obtendrá una aceleración considerable con ejecuciones paralelas.

Una forma relativamente simple de hacerlo es a través del -Pparámetro de xargs, por ejemplo, si tiene 4 núcleos:

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

El -n 1le dice xargsque elija solo un argumento de la lista para cada invocación (por defecto pasaría mucho) , y -P 4le dice que genere 4 procesos al mismo tiempo: cuando uno muere, se genera uno nuevo.

En mi humilde opinión, no es necesario instalar GNU en paralelo para este caso simple - es xargssuficiente.

ttsiodras
fuente
0

Puedes usar xarg

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

-L 1 causa pasar 1 artículo a la vez

-d '\n'la salida de lsmake se divide en función de la nueva línea.

Al Mamun
fuente