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.txt
para 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.txt
contengan:
{"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_country
clave. 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_country
clave. 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 @json
operador 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 @sh
para citar correctamente la cadena del shell. El @sh
operador también se utiliza para crear parte del nombre del archivo de salida a partir del valor de la .location_country
clave.
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 sh
ejecutar los comandos, o podrías usarlo eval
directamente en el shell:
eval "$( jq ...as above... )"
Dado que estamos usando un analizador JSON adecuado, jq
lo 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 -s
argumento. Si te importa y no tienes el tipo GNU, házmelo saber, ya que existe una solución bastante sencilla.