Procure o padrão e crie o arquivo com o mesmo nome

Procure o padrão e crie o arquivo com o mesmo nome

Aqui está uma linha simples do meu uso:

grep -i '"location_country":"country name"' file.txt >> sample.txt

Estou pesquisando um arquivo grande com vários países e o que gostaria de fazer é criar um arquivo de texto com o nome do país dinamicamente e armazenar todas as correspondências do mesmo país nesse arquivo, o que significa country name.txtpara cada ocorrência.

algo assim

grep -i '"location_country":"(.+)"' file.txt >> \1.txt

Exemplo de dados:

{"full_name":"name1","location_country":"united kingdom"}
{"full_name":"name2","location_country":"united states"}
{"full_name":"name3","location_country":"china"}

Portanto, preciso criar três arquivos de texto separados com nomes de países, por exemplo, united kingdom.txtcontém:

{"full_name":"name1","location_country":"united kingdom"}

Já estou usando scripts bash, então não me importo. Como posso conseguir isso? Estou usando uma máquina Linux.

Responder1

Seu arquivo consiste em um conjunto de objetos JSON. Cada objeto contém uma .location_countrychave. A partir de cada objeto podemos criar um comando shell que grava uma cópia serializada do próprio objeto em um arquivo nomeado pelo valor da .location_countrychave. Esses comandos do shell poderiam então ser executados pelo shell.

Usando jq,

jq -r '"printf \"%s\\n\" \(. | @json | @sh) >\(.location_country|@sh).txt"' file.txt

O objeto serializado pode ser criado usando o @jsonoperador in jq, que emitirá uma string codificada em JSON contendo o documento de entrada, neste caso o objeto atual. Isso é então alimentado @shpara citar corretamente a string do shell. O @shoperador também é usado para criar parte do nome do arquivo de saída a partir do valor da .location_countrychave.

O comando essencialmente cria um código shell que invocaria printf, gerando o objeto atual e redirecionando a saída para um arquivo específico.

Dados seus dados de exemplo em file.txt, isso emitiria o seguinte:

printf "%s\n" '{"full_name":"name1","location_country":"united kingdom"}' >'united kingdom'.txt
printf "%s\n" '{"full_name":"name2","location_country":"united states"}' >'united states'.txt
printf "%s\n" '{"full_name":"name3","location_country":"china"}' >'china'.txt

Você seria capaz de redirecionar isso para um arquivo separado e executá-lo para shexecutar os comandos, ou você poderia usar evaldiretamente no shell:

eval "$( jq ...as above... )"

Como estamos usando um analisador JSON adequado, jqo procedimento acima funcionaria mesmo se o documento JSON de entrada não estivesse formatado com um único objeto por linha.

$ cat file.txt
{
  "full_name": "name1",
  "location_country": "united kingdom"
}
{
  "full_name": "name2",
  "location_country": "united states"
}
{
  "full_name": "name3",
  "location_country": "china"
}
$ jq -r '"printf \"%s\\n\" \(. | @json | @sh) >\(.location_country|@sh).txt"' file.txt
printf "%s\n" '{"full_name":"name1","location_country":"united kingdom"}' >'united kingdom'.txt
printf "%s\n" '{"full_name":"name2","location_country":"united states"}' >'united states'.txt
printf "%s\n" '{"full_name":"name3","location_country":"china"}' >'china'.txt
$ eval "$( jq -r '"printf \"%s\\n\" \(. | @json | @sh) >\(.location_country|@sh).txt"' file.txt )"
$ ls
china.txt           file.txt            united kingdom.txt  united states.txt
$ cat 'united kingdom.txt'
{"full_name":"name1","location_country":"united kingdom"}

Responder2

Usandoawk

Entrada

$ cat input_file
{"full_name":"name1","location_country":"united kingdom"}
{"full_name":"name2","location_country":"united states"}
{"full_name":"name3","location_country":"china"}
{"full name":"name12","location":"china"}
{"full name":"name11","location":"china"}
awk -F"[\"|:]" '$10~/[A-Za-z]/ {print > $10".txt"}' input_file

Saída

$ cat china.txt
{"full_name":"name3","location_country":"china"}
{"full name":"name12","location":"china"}
{"full name":"name11","location":"china"}

$ cat united\ kingdom.txt
{"full_name":"name1","location_country":"united kingdom"}

$ cat united\ states.txt
{"full_name":"name2","location_country":"united states"}

Responder3

Dados seus comentários abaixo, isso deve fazer o que você deseja usando GNU awk para o terceiro argumento para match() e manipulação de muitos arquivos abertos simultaneamente:

awk 'match($0,/"location_country":"([^"]+)"/,a) { print > (a[1] ".txt") }' file

Para velocidade de execução, uma abordagem decorar/classificar/usar/desdecorar provavelmente seria melhor, por exemplo:

awk -v OFS='"' 'match($0,/"location_country":"[^"]+"/) { print substr($0,RSTART+20,RLENGTH-21), $0 }' file |
sort -t'"' -k1,1 |
awk -F'"' '$1!=prev { close(out); out=$1 ".txt"; prev=$1 } { print > out }' |
cut -d'"' -f2-

e isso funcionará com qualquer tipo, awk e cut.


Resposta original:

Se seus dados são sempre tão simples/regulares, então tudo que você precisa é isso com o GNU awk (para lidar com muitos arquivos de saída abertos simultaneamente):

awk -F'"' '{ print > ($5 ".txt") }' file

ou isso com qualquer awk:

awk -F'"' '{
    out = $5 ".txt"
    if ( !seen[out]++ ) {
        printf "" > out
    }
    print >> out
    close(out)
}' file

O procedimento acima funcionará independentemente do tamanho do seu arquivo de entrada, desde que você tenha espaço em disco disponível para criar os arquivos de saída.

Você pode fazer isso de forma mais eficiente classificando primeiro o nome do país, se desejar fazer isso:

sort -t'"' -k5,5 file |
awk -F'"' '$5 != prev{ close(out); out=$5 ".txt"; prev=$5 } { print > out }'

Esse último script funcionará com qualquer tipo e qualquer awk, mas poderá reorganizar a ordem das linhas de entrada para cada país. Se você se preocupa com isso e tem a classificação GNU, adicione o -sargumento. Se você se importa e não tem o tipo GNU, me avise, pois há uma solução bastante simples.

informação relacionada