Maneira de escrever um novo nome de arquivo em uma entrada curinga?

1

Eu tenho alguma experiência com terminais Inix de estágios de ciências dos quais já participei, principalmente usando alguns utilitários como grep , awk e sed , mas há uma coisa que venho tentando descobrir por um tempo que realmente me tornaria muito mais eficiente com o processamento de números que tenho que fazer.

Eu tenho um script run.awk que realiza algumas manipulações em uma grande coleção de arquivos de texto massivos. Como é, ele pegará o arquivo chloride.out , extrairá dados dele e escreverá chloride.cm .

Existe alguma maneira que eu posso fazer este script pegar *.out e escrever *.cm arquivos baseados na frase curinga inicial no shell?

A quantidade de scripts que escrevi para processar grandes quantidades de dados que precisei fazer centenas de iterações é simplesmente irritante.

Idealmente, gostaria de saber se existe uma maneira de fazer isso para todos os meus scripts com algo via shell. Se ele não puder ser automatizado no shell ou em um equivalente, posso pelo menos automatizar meus scripts awk de maneira semelhante à descrita anteriormente?

    
por user507974 01.10.2013 / 02:46

2 respostas

5

Você certamente pode fazer o awk lidar com vários arquivos por meio de curingas. Uma sugestão seria deixar o run.awk como uma "função" genérica que leva um único arquivo e produz um único arquivo de saída, e então o chama de outro script que poderia então assimilar os arquivos de entrada e saída. / p>

Exemplo

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

Eu fiz um diretório de exemplo com alguns arquivos de teste.

$ touch file{1..4}.out

Isso resultou em 4 arquivos sendo feitos:

$ 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 "executando ...", nosso script pode ser executado a partir daqui.

Arquivos em uma lista

Diga que, em vez de usar o curinga, *.out , em vez disso, temos um arquivo com uma lista de nomes de arquivo, 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 loop while em vez de um loop for . Agora vamos chamar essa variante do 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 versão do script lê a entrada do arquivo, filelist.txt :

done < filelist.txt

Em seguida, para cada vez do loop while , estamos usando o comando read para ler em uma linha do arquivo de entrada.

while read ifname; do

Em seguida, ele executa tudo da mesma maneira que o primeiro script no qual ele executará o awk script run.awk enquanto ele percorre cada linha do arquivo.

    
por 01.10.2013 / 03:14
1

Em vez de escrever um invólucro shell e criar uma nova instância awk para cada arquivo que você processa, 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á dizer se está trabalhando com file1 ou file2 usando FILENAME. Você também pode usar > on print / printf no awk. Então, se você tem um script awk como

/pattern/{ print $1,$3 }

você poderia facilmente fazer

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

ou use FNR=1 para 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 extensão .in 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 variável out para uma versão modificada de FILENAME quando FNR for alterado para 1 (portanto, estamos na linha 1 do arquivo) e, em seguida, redirecionando todas as impressões para out . Observe que isso é um pouco perigoso, já que a falha em corresponder à extensão não resultará em substituição, levando à substituição dos arquivos de entrada. Então, seria bom adicionar uma verificação de segurança para garantir que out != FILENAME ou algo assim também. Isso é deixado como um 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_file e o coloca na linha de comando.

    
por 06.10.2013 / 22:01