SED / AWK: adiciona uma string a vários arquivos correspondidos pelo valor regex de outro arquivo de origem

1

Eu tenho um único arquivo de origem que tem várias entradas deste formato:

some_id: some string

E um monte de arquivos .txt em pastas diferentes que possuem strings identificadas por id

id="some_id"

Como faço para analisar o arquivo de origem de forma que cada string no arquivo .txt que tem id correspondente a um arquivo de origem seja adicionada a um novo parâmetro como

id="some_id" some_param="some string"

que é obtido do valor do ID do arquivo de origem correspondente

Notas

  • Pode haver vários IDs no mesmo arquivo
  • Nunca há IDs duplicados em qualquer arquivo
  • Eles nunca estão localizados na mesma linha
  • some_param é um literal
  • não importa onde adicionar novo some_param="string" , desde que esteja na mesma linha e espaçado

Alguma amostra

source.yml

apples: Apples are very tasty
grapes: Grapes kind of ok
trees: Gain mass from CO2
strawberry: Yummy

fruits / foo.txt

This is some plain text
Toreplace id="apples" string contains
The most vitamin-rich berry is id="strawberry"

fruits / strange / grapes.txt

And id="grapes" the

questionável / trees.txt

Or id="tress"

Dado este exemplo, o script deve transformar fruits / foo.txt em:

This is some plain text
Toreplace id="apples" string contains some_param="Apples are very tasty"
The most vitamin-rich berry is id="strawberry" some_param="Yummy"
    
por knitevision 07.12.2017 / 22:04

2 respostas

1

Solução complexa Grep + GNU Awk :

O conteúdo do script awk crucial, digamos add_param_by_id.awk :

#!/bin/awk -f
BEGIN{ FS=":[[:space:]]+" }
NR==FNR{ a[$1] = $2; next }
match($0, /\<id="([^"]+)"/, b) && b[1] in a{ 
    sub(/\<id="[^"]+"/, "& some_param=2"a[b[1]]"2") 
}1

Os principais comandos:

export pat="($(cut -d':' -f1 source.yml | paste -s -d'|'))"
grep -ElZr "\<id=\"$pat\"" --include=*.txt | xargs -0 -I{} sh -c \
'awk -f add_param_by_id.awk source.yml "$1" > "/tmp/$$" && mv "/tmp/$$" "$1"' _ {} 
  • pat - variável contendo o grupo de alternância regex com identificadores, por exemplo, (apples|grapes|trees|strawberry)
  • grep -ElZr id=\"$pat\" --include=*.txt - imprime todos os nomes de arquivos que foram correspondidos por qualquer um dos identificadores especificados

Exemplo de conteúdo do arquivo após o processamento:

This is some plain text
Toreplace string contains id="apples" some_param="Apples are very tasty"
The most vitamin-rich berry is id="strawberry" some_param="Yummy"
    
por 07.12.2017 / 23:56
2

Com certas ressalvas, como "nenhum caractere estranho em nomes de arquivo" e "nenhum caractere / em valores de dados correspondentes ou de substituição", isso deve funcionar para você. Não é muito eficiente, pois faz com que se passe por cada arquivo que ele possa encontrar para cada linha no arquivo YAML, mas para corrigir isso, eu gostaria de mais informações sobre o tamanho relativo do arquivo YAML para o número de arquivos de destino. / p>

while IFS=: read -r id sp
do
    id="id=\"$id\"" sp="some_param=\"${sp:1}\""

    find . -type f -name '*.txt' |
        while IFS= read -r file
        do
            sed -i "/\<$id/s/$/ $sp/" "$file"
        done
done <source.yml
    
por 07.12.2017 / 23:22