Maneira de escrever um novo nome de arquivo na entrada curinga?

Maneira de escrever um novo nome de arquivo na entrada curinga?

Tenho alguma experiência com terminais Inix em estágios científicos dos quais participei, principalmente usando alguns utilitários como grep, awk, e sedmas há uma coisa que venho tentando descobrir há algum tempo que realmente me tornaria muito mais eficiente com a análise de números que tenho que fazer.

Eu tenho um script run.awkque realiza algumas manipulações em uma grande coleção de arquivos de texto enormes. Como está, ele pegará o arquivo chloride.out, extrairá os dados dele e gravará chloride.cm.

Existe alguma maneira de fazer com que esse script receba *.oute grave *.cmarquivos com base na frase curinga inicial no shell?

A quantidade de scripts que escrevi para processar grandes quantidades de dados e dos quais tive que fazer mais de cem iterações é simplesmente irritante.

Idealmente, gostaria de saber se existe uma maneira de fazer isso para todos os meus scripts com algo por meio do shell. Se não puder ser automatizado no shell ou equivalente, posso pelo menos automatizar meus awkscripts de maneira semelhante à descrita?

Responder1

Você certamente pode fazer com que o awk lide com vários arquivos por meio de curingas. Uma sugestão seria deixar o run.awkcomo uma "função" genérica que recebe um único arquivo e produz um único arquivo de saída, e então chamá-lo de outro script que poderia então cuidar da assimilação dos arquivos de entrada e saída.

Exemplo

Este seria um script Bash, podemos chamá-lo de 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

Execução de amostra

Criei um diretório de exemplo com alguns arquivos de teste.

$ touch file{1..4}.out

Isso resultou na criação de 4 arquivos:

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

Agora executamos nosso 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

Após cada linha que começa com "running..." nosso script pode ser executado a partir daqui.

Arquivos em uma lista

Digamos que em vez de usar o curinga, *.outtínhamos um arquivo com uma lista de nomes de arquivos, digamos:

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

Poderíamos usar esta versão modificada do nosso script, que usaria um whileloop em vez de um forloop. Agora vamos chamar essa variante do script awk_file_runner.bashde:

#!/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 versão do script lê a entrada do arquivo filelist.txt:

done < filelist.txt

Então, para cada volta do whileloop, estamos usando o readcomando para ler uma linha do arquivo de entrada.

while read ifname; do

Em seguida, ele executa tudo da mesma maneira que o primeiro script, onde executará o awkscript run.awkà medida que percorre cada linha do arquivo.

Responder2

Em vez de escrever um wrapper de shell e gerar uma nova instância do awk para cada arquivo processado, você pode fazer isso diretamente no awk. Se você já possui um script awk, pode acessar o arquivo atual usando a variável FILENAME. Portanto, se você executar awk 'some commands' file1 file2, poderá saber se está trabalhando com arquivo1 ou arquivo2 usando FILENAME. Você também pode usar >on print/ printfin awk. Então, se você tiver um script awk como

/pattern/{ print $1,$3 }

você poderia facilmente fazer

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

ou use FNR=1para saber quando você está em um novo arquivo e crie uma variável para fazer uma manipulação mais complexa no nome do arquivo. Como substituir uma .inextensão por .out, como em

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

Estou usando grep .para mostrar o nome do arquivo e o conteúdo de vários arquivos aqui, o que também é um truque divertido. Mas o importante é definir o valor da outvariável para uma versão modificada de FILENAMEquando FNRmuda para 1 (então estamos na linha 1 do arquivo), e então redirecionar todos os prints para out. Observe que isso é um pouco perigoso, pois a falha na correspondência da extensão resultará em nenhuma substituição, levando à substituição dos arquivos de entrada. Portanto, seria bom adicionar uma verificação à prova de falhas para ter certeza disso out != FILENAMEou algo parecido também. Isso fica como exercício para o leitor. ;)

Se você precisar de um arquivo contendo uma lista de nomes de arquivos, é mais fácil executá-lo como

awkscript $(< /path/to/filename_list_file )

Que pega o conteúdo do filename_list_filee o coloca na linha de comando.

informação relacionada