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.txt
estão presentes results.txt
, nem estão em ordem. Quero inserir um prefixo em todos os nomes de arquivos, filename.txt
mas results.txt
nã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 sed
com 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.txt
não contenham espaços em branco após o nome do arquivo, o seguinte awk
programa 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.txt
e depoisresults.txt
. - Durante a análise
filenames.txt
(ondeFNR
o contador de linha por arquivo é igual aoNR
contador de linha global), ele registrará todos os nomes de arquivos (que são os únicos campos na linha) em uma matrizfnames
, mas pulará a execução imediatamente para a próxima linha. - Durante a análise,
results.txt
ele verificará se uma linha começa com>
. Nesse caso, ele verificará se a substring após esse caractere (armazenada temporariamente emname
) foi encontrada entre os "índices de array" defnames
. Se for esse o caso, será utilizadosub()
para substituir o inicial>
por>
+ o prefixo, passadoawk
como variávelprefix
(por meio da-v
diretiva). - O aparentemente "perdido"
1
irá instruirawk
a imprimir a linha atual, incluindo todas as modificações possíveis (mas apenas porqueresults.txt
durante o processamento do primeiro arquivo não alcançamos essa parte).
Observe que awk
por 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 inplace
extensão; claro, você precisará desativar a opção do filenames.txt
arquivo:
awk -i inplace -v prefix=" ... " ' ... ' inplace=0 filenames.txt inplace=1 results.txt
Isso desativará filenames.txt
e ativará novamente a edição no local para results.txt
.
Responder2
Com sed
você pode coletar os nomes dos arquivos no espaço de espera e, em seguida, verificar todas as linhas em results.txt
busca 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ão1,/^$/
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
(1h
evita iniciar o espaço de retenção com uma nova linha) G
anexa o espaço de espera a todas as linhasresult.txt
e/^>\(.*\).*\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 linhasP;d
imprime apenas a primeira linha sem o lixo anexado. Você poderia fazers/\n.*//
em vez disso
Responder3
Use perl
para 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