Precisa extrair 2 strings de linhas específicas de múltiplos arquivos e imprimir para um novo arquivo, separados por tabulação

3

Eu tenho algumas experiências com Unix e Python, mas estou um pouco enferrujado e não sei o suficiente para descobrir isso sozinho.

Eu tenho uma pasta contendo vários arquivos, dos quais eu preciso extrair 2 strings específicas e imprimi-las em um novo arquivo de texto com uma tabulação entre elas. Os arquivos são assim:

mlu +t*CHI +f
Thu Jan 12 16:27:40 2017
mlu (08-Jan-2016) is conducting analyses on:  
  ONLY dependent tiers matching: %MOR;
****************************************
From file <adam01.cha>
MLU for Speaker: *CHI:
  MLU (xxx, yyy and www are EXCLUDED from the utterance and morpheme counts):
    Number of: utterances = 1236, morphemes = 2735
    Ratio of morphemes over utterances = 2.213
    Standard deviation = 1.300

Eu preciso extrair o nome do arquivo e o valor de "Ratio of morphemes over utterances". Então eu quero puxar essas seqüências para criar um novo arquivo que se parece com isso:

adam01.cha    2.213
adam02.cha    2.547
...

Eu não consigo descobrir exatamente o que preciso fazer aqui. Não sei ao certo por onde começar - não consigo determinar se egrep, awk ou sed é minha melhor opção e como colocá-los em um loop for que seria capaz de percorrer os arquivos corretamente.

    
por Ed2122 12.01.2017 / 23:20

5 respostas

4

Você pode usar sed em um loop sobre cada arquivo da sua pasta atual. Você extrai as partes relevantes e as anexa via >> a um arquivo chamado file desta forma:

for files in *; \
do sed -n -e '/^From file/ H;' \
          -e '/Ratio of morphemes over utterances/ {H; x; s/\n//g; s/From file <\(.*\)>.*Ratio of morphemes over utterances = \([0-9]*\.[0-9]*\).*/:    /g; p;}' "$files";
done >>file
    
por 13.01.2017 / 00:08
1
perl -0nE 'say "$1\t$2" if /From file <(.*?)>.*over utterances = (\d\S*)/s' * > out
    
por 13.01.2017 / 00:18
1

Já que você mencionou que está familiarizado com o Python, aqui está um script python que pode fazer o trabalho:

#!/usr/bin/env python
from __future__ import print_function
import os,re,sys

def read_file(filepath):
    with open(filepath) as fd:
         for line in fd:
             clean_line = line.strip()

             if 'From file' in clean_line:

                 words = re.split('<|>| ', clean_line)
                 print(words[-2],end=" ")

             if 'Ratio of morphemes over utterances' in clean_line:
                 print(clean_line.split('=')[-1])



def find_files(treeroot):
    selfpath = os.path.abspath(__file__)
    for dir,subdirs,files in os.walk(treeroot):
         for f in files: 
             filepath = os.path.abspath(os.path.join(dir,f))
             if selfpath  ==  filepath: continue
             try:
                 read_file(filepath)
             except IOError:
                 pass
def main():
    directory = '.'
    if len(sys.argv) == 2:
       directory = sys.argv[1]
    find_files(os.path.abspath(directory))

if __name__ == '__main__': main()

Execução da amostra:

$ ./extract_data.py                                                                                               
adam02.cha  2.547
adam01.cha  2.213

A maneira como isso funciona é simples: usamos os.walk para percorrer recursivamente um diretório, localizando todos os arquivos e excluindo o próprio script, e para cada arquivo executamos read_file() function, que lê cada arquivo linha por linha e encontra os campos apropriados. re.split() é usado para dividir a cadeia de nomes de arquivos na lista de palavras de forma mais conveniente usando espaço e < e > como separadores para palavras. O script pode ter um argumento de linha de comando para um diretório, mas, se não for fornecido, o diretório de trabalho atual é assumido. Dessa forma, você pode executar o script dado um caminho ou do diretório onde os arquivos são armazenados. Quanto à criação de novo arquivo com todos os dados, é trivial - use o redirecionamento do shell como ./extract_data.py > /path/to/new_file.txt . Nota de cuidado - redirecione o script para um arquivo localizado em um diretório diferente, já que o novo arquivo pode ser enfileirado em os.walk() e quebrar o script. Melhoria adicional é que você poderia chamar o loop for para arquivos como for f in sorted(files): para ler arquivos de forma ordenada.

    
por 13.01.2017 / 01:29
1

Você não precisa de um loop. A maioria das ferramentas de processamento de texto aceita vários argumentos, por ex. com sed :

sed '/From file/{s/.*<\(.*\)>//;h
}
/Ratio of morphemes over utterances/!d
s/.*= //;H;x;s/\n/\t/' ./* > outfile

Isso extrai o nome do arquivo e o salva no buffer de retenção, exclui todas as linhas, exceto aquelas em que extrai a "proporção", que ele anexa ao nome do arquivo, troca buffers e substitui a nova linha por uma guia. Claro, \t é gnu sed específico, então substitua-o por uma tabulação literal (no terminal, pressione Ctrl + V então Tab ) se você não está em um gnu setup. Ainda mais rápido, com qualquer sed :

sed '/From file/{s/.*<\(.*\)>//;h
}
/Ratio of morphemes over utterances/!d
s/.*= //;H;x' ./* | paste - - > outfile

Se seus arquivos são enormes, você pode querer sair quando estiver na segunda partida (a linha com a "proporção") e ir para o próximo arquivo. É aqui que gawk nextfile é útil (acho que é POSIX, mas não tenho certeza de qual awk flavors o suporta ...):

awk '/From file/{printf("%s\t", substr($3, 2, length($3)-2))}
/Ratio of morphemes over utterances/{print $7; nextfile}' ./* > outfile
    
por 13.01.2017 / 13:54
0

você pode tentar com o comando awk

awk '/Ratio of morphemes over utterances/{print FILENAME,$NF;next}' *.cha

se você quiser extrair o nome do arquivo do padrão Do arquivo < adam01.cha >

depois, experimente o comando awk abaixo.

awk '/From file/{filename=$NF} filename && /Ratio of morphemes over utterances/{print FILENAME,$NF;filename="";next}' *.txt
    
por 13.01.2017 / 05:16