Como ler um arquivo de entrada de strings, combinar e alterar as correspondências no local?

Como ler um arquivo de entrada de strings, combinar e alterar as correspondências no local?

Eu tenho um arquivo de texto com strings/nomes de arquivos em linhas separadas, por exemplo. filename.txt. Existem centenas de nomes de arquivos

ABC123_S386_R1_001
JKL345_S441_R1_001
filename9000_S587_R1_001

e outro arquivo de texto com string/nomes de arquivos e dados adicionais, por exemplo. results.txt:

>ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

Agora, nem todos os nomes de arquivos filename.txtestão presentes results.txt, nem estão em ordem. Quero inserir um prefixo em todos os nomes de arquivos, filename.txtmas results.txtnão nos outros.

Como leio um arquivo de entrada de strings, combino com outro arquivo e altero as correspondências?

Anteriormente, eu costumava combinar nomes de arquivos individuais com sequence.txt, obter seu número de linha e usá-lo sedcom números de linha para alterar uma única linha ou um bloco de linhas.

Minha saída desejada seria semelhante

>h-19/US/CA-ABC123_S386_R1_001 
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-JKL345_S441_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>abc7890_S387_R1_001  
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
>h-19/US/CA-filename9000_S587_R1_001
NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN

onde h-19/US/CA-está o sufixo que gostaria de adicionar a todas as correspondências.

Editar: >é o primeiro caractere de todas as strings que precisam ser alteradas, não há caracteres antes >nem espaços em branco à direita após o nome do arquivo.

Responder1

Supondo que as linhas relevantes results.txtnão contenham espaços em branco após o nome do arquivo, o seguinte awkprograma funcionará:

awk -v prefix="h-19/US/CA-" 'NR==FNR{fnames[$1]; next} \
    /^>/{name=substr($0,2); if (name in fnames) {sub(/^>/, ">" prefix)} }1' filenames.txt results.txt
  • Isso irá primeiro analisar filenames.txte depois results.txt.
  • Durante a análise filenames.txt(onde FNRo contador de linha por arquivo é igual ao NRcontador de linha global), ele registrará todos os nomes de arquivos (que são os únicos campos na linha) em uma matriz fnames, mas pulará a execução imediatamente para a próxima linha.
  • Durante a análise, results.txtele verificará se uma linha começa com >. Nesse caso, ele verificará se a substring após esse caractere (armazenada temporariamente em name) foi encontrada entre os "índices de array" de fnames. Se for esse o caso, será utilizado sub()para substituir o inicial >por >+ o prefixo, passado awkcomo variável prefix(por meio da -vdiretiva).
  • O aparentemente "perdido" 1irá instruir awka imprimir a linha atual, incluindo todas as modificações possíveis (mas apenas porque results.txtdurante o processamento do primeiro arquivo não alcançamos essa parte).

Observe que awkpor si só não é possível modificar arquivos no local, portanto você precisaria trabalhar com um arquivo temporário. Se você tiver uma versão suficientemente nova do GNU Awk (> 4.1.0), poderá usar a inplaceextensão; claro, você precisará desativar a opção do filenames.txtarquivo:

awk -i inplace -v prefix=" ... " ' ... ' inplace=0 filenames.txt inplace=1 results.txt

Isso desativará filenames.txte ativará novamente a edição no local para results.txt.

Responder2

Com sedvocê pode coletar os nomes dos arquivos no espaço de espera e, em seguida, verificar todas as linhas em results.txtbusca de correspondências para filtrar quais linhas alterar:

sed -e '1,/^$/{H;1h;d;}' -e 'G;/^>\(.*\).*\n\1\n/s_^>_>h-19/US/CA-_;P;d' filename.txt <((echo)) results.txt
  • Você vê que eu passo uma linha vazia <((echo))entre os arquivos, então 1,/^$/endereça todas as linhas do primeiro arquivo (e a linha vazia)
  • Essas linhas são anexadas ao espaço de retenção e depois excluídas H;1h;d( 1hevita iniciar o espaço de retenção com uma nova linha)
  • Ganexa o espaço de espera a todas as linhas result.txte /^>\(.*\).*\n\1\n/corresponde às linhas que começam com >e uma string que é um nome de arquivo (entre novas linhas no espaço de espera)
  • s_^>_>h-19/US/CA-_faz a substituição dessas linhas
  • P;dimprime apenas a primeira linha sem o lixo anexado. Você poderia fazer s/\n.*//em vez disso

Responder3

Use perlpara edições locais no arquivo de entrada:

pfx='h-19/US/CA-' \
perl -pi -e '
  BEGIN { %h = map { tr/\n//dr => $ENV{pfx}} <STDIN>}
  s/^>\K(?=(.*))/$h{$1}/;
' results.txt < filename.txt

informação relacionada