Busque un patrón y cree un archivo con el mismo nombre

Busque un patrón y cree un archivo con el mismo nombre

Aquí hay una línea simple de mi uso:

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

Estoy buscando un archivo grande con varios países y lo que me gustaría hacer es crear un archivo de texto con el nombre del país dinámicamente y almacenar todas las coincidencias del mismo país en ese archivo, es decir, country name.txtpara cada aparición.

algo como esto

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

Ejemplo de datos:

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

Entonces necesito crear 3 archivos de texto separados con nombres de países, por ejemplo, que united kingdom.txtcontengan:

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

Ya estoy usando scripts bash, así que no me importa. ¿Cómo podría lograr esto? Estoy usando una máquina Linux.

Respuesta1

Su archivo consta de un conjunto de objetos JSON. Cada objeto contiene una .location_countryclave. A partir de cada objeto podemos crear un comando de shell que escriba una copia serializada del propio objeto en un archivo denominado por el valor de la .location_countryclave. Estos comandos del shell podrían luego ser ejecutados por el shell.

Usando jq,

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

El objeto serializado se puede crear usando el @jsonoperador en jq, que emitirá una cadena codificada en JSON que contiene el documento de entrada, en este caso el objeto actual. Luego se envía esto @shpara citar correctamente la cadena del shell. El @shoperador también se utiliza para crear parte del nombre del archivo de salida a partir del valor de la .location_countryclave.

Básicamente, el comando crea un código de shell que invocaría printf, generando el objeto actual y redirigiendo la salida a un archivo específico.

Dados los datos de ejemplo en file.txt, esto emitiría lo siguiente:

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

Podrías redirigir esto a un archivo separado y ejecutarlo para shejecutar los comandos, o podrías usarlo evaldirectamente en el shell:

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

Dado que estamos usando un analizador JSON adecuado, jqlo anterior funcionaría incluso si el documento JSON de entrada no está formateado con un solo objeto por línea.

$ 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"}

Respuesta2

Usandoawk

Aporte

$ 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

Producción

$ 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"}

Respuesta3

Teniendo en cuenta sus comentarios a continuación, esto debería hacer lo que desea usando GNU awk para que el tercer argumento coincida() y el manejo de muchos archivos abiertos simultáneamente:

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

Para la velocidad de ejecución, probablemente sería mejor un enfoque de decorar/ordenar/usar/desdecorar, por ejemplo:

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-

y eso funcionará con cualquier tipo, awk y corte.


Respuesta original:

Si sus datos son siempre así de simples/regulares, entonces todo lo que necesita es esto con GNU awk (para manejar muchos archivos de salida abiertos simultáneamente):

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

o esto con cualquier awk:

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

Lo anterior funcionará sin importar el tamaño de su archivo de entrada, siempre que tenga espacio en disco disponible para crear los archivos de salida.

Puedes hacerlo de manera más eficiente ordenando primero el nombre del país si así lo deseas:

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

Ese último script funcionará con cualquier tipo y awk, pero podría reorganizar el orden de las líneas de entrada para cada país. Si eso le importa y tiene el tipo GNU, agregue el -sargumento. Si te importa y no tienes el tipo GNU, házmelo saber, ya que existe una solución bastante sencilla.

información relacionada