¿Manera de escribir un nuevo nombre de archivo en la entrada comodín?

¿Manera de escribir un nuevo nombre de archivo en la entrada comodín?

Tengo algo de experiencia con terminales Inix gracias a las pasantías científicas en las que he participado, principalmente usando algunas utilidades como grep, awky, sedpero hay una cosa que he estado tratando de descubrir durante un tiempo que realmente me haría mucho más eficiente. con los números que tengo que hacer.

Tengo un script run.awkque realiza algunas manipulaciones en una gran colección de archivos de texto masivos. Tal como está, tomará el archivo chloride.out, extraerá los datos y lo escribirá chloride.cm.

¿Hay alguna forma de hacer que este script acepte *.outy escriba *.cmarchivos basándose en la frase comodín inicial en Shell?

La cantidad de scripts que he escrito para procesar grandes cantidades de datos y que he tenido que hacer más de cien iteraciones es simplemente molesta.

Idealmente, me gustaría saber si hay alguna manera de poder hacer esto para todos mis scripts con algo a través del shell. Si no se puede automatizar en el Shell o en un equivalente, ¿puedo al menos automatizar mis awkscripts de una manera similar a la que he descrito?

Respuesta1

Seguramente puedes hacer que awk maneje múltiples archivos mediante comodines. Una sugerencia sería dejarla run.awkcomo una "función" genérica que toma un solo archivo y produce un solo archivo de salida, y luego llamarla desde otro script que luego podría encargarse de asimilar los archivos de entrada y salida.

Ejemplo

Este sería un script Bash, podemos llamarlo awk_runner.bash.

#!/bin/bash

for ifname in *.out; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done

Ejecución de muestra

Creé un directorio de ejemplo con algunos archivos de prueba.

$ touch file{1..4}.out

Esto resultó en la creación de 4 archivos:

$ ls -1
file1.out
file2.out
file3.out
file4.out

Ahora ejecutamos nuestro script:

$ ./awk_runner.bash
IN: file1.out, OUT: file1.cm
running run.awk with file1.out & file1.cm

IN: file2.out, OUT: file2.cm
running run.awk with file2.out & file2.cm

IN: file3.out, OUT: file3.cm
running run.awk with file3.out & file3.cm

IN: file4.out, OUT: file4.cm
running run.awk with file4.out & file4.cm

Después de cada línea que comienza con "ejecutando..." nuestro script podría ejecutarse desde aquí.

Archivos en una lista

Digamos que en lugar de usar el comodín, *.outteníamos un archivo con una lista de nombres de archivo, digamos:

$ cat filelist.txt 
file1.out
file2.out
file3.out
file4.out

Podríamos usar esta versión modificada de nuestro script que usaría un whilebucle en lugar de un forbucle. Ahora llamemos a esta variante del script awk_file_runner.bash:

#!/bin/bash

while read ifname; do 
  ofname=${ifname/.out/.cm}
  printf "IN: %s, OUT: %s\n" $ifname $ofname
  printf "running run.awk with %s & %s\n\n" $ifname $ofname

  run.awk $ifname $ofname
done < filelist.txt

Esta versión del script lee la entrada del archivo filelist.txt:

done < filelist.txt

Luego, para cada vuelta del whilebucle, usamos el readcomando para leer una línea del archivo de entrada.

while read ifname; do

Luego realiza todo de la misma manera que el primer script, donde ejecutará el awkscript run.awkmientras recorre cada línea del archivo.

Respuesta2

En lugar de escribir un contenedor de shell y generar una nueva instancia de awk para cada archivo que procese, puede hacerlo directamente en awk. Si ya tiene un script awk, puede acceder al archivo actual usando la variable FILENAME. Entonces, si ejecuta awk 'some commands' file1 file2, podrá saber si está trabajando con el archivo1 o el archivo2 usando FILENAME. También puedes usar >on print/ printfin awk. Entonces, si tienes un script awk como

/pattern/{ print $1,$3 }

podrías hacer fácilmente

/pattern/{ print $1,$3 > FILENAME".processed" }

o utilícelo FNR=1para saber cuándo está en un archivo nuevo y cree una variable para realizar una manipulación más compleja en el nombre del archivo. Como reemplazar una .inextensión con .out, como en

sauer@humpy:/tmp$ grep . file*.in
file1.in:a
file1.in:b
file2.in:c
sauer@humpy:/tmp$ awk 'FNR=1{out=FILENAME;sub("\.in$",".out",out)} {print "processed"$0 > out}' file*.in
sauer@humpy:/tmp$ grep . file*.out
file1.out:processeda
file1.out:processedb
file2.out:processedc

Estoy usando grep .aquí para mostrar el nombre del archivo y el contenido de varios archivos, lo cual también es un truco divertido. Pero lo importante es establecer el valor de la outvariable en una versión modificada de FILENAMEcuando FNRcambia a 1 (por lo que estamos en la línea 1 del archivo) y luego redirigir todas las impresiones a out. Tenga en cuenta que esto es ligeramente peligroso, ya que si no se coincide con la extensión, no se realizará ninguna sustitución, lo que provocará la sobrescritura de los archivos de entrada. Por lo tanto, sería bueno agregar una verificación a prueba de fallas para asegurarse de eso out != FILENAMEo algo así también. Esto se deja como ejercicio para el lector. ;)

Si necesita un archivo que contenga una lista de nombres de archivos, es más fácil ejecutarlo como

awkscript $(< /path/to/filename_list_file )

Que toma el contenido de filename_list_filey lo coloca en la línea de comando.

información relacionada