Найдите шаблон и создайте файл с тем же именем.

Найдите шаблон и создайте файл с тем же именем.

Вот простая строка моего использования:

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

Я выполняю поиск в большом файле с несколькими странами и хотел бы динамически создать текстовый файл с названием страны и сохранить все совпадения из этой страны в этом файле, то есть country name.txtдля каждого случая.

что-то вроде этого

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

Пример данных:

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

Поэтому мне нужно создать 3 отдельных текстовых файла с названиями стран, например, united kingdom.txtсодержащие:

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

Я уже использую скрипты bash, поэтому меня это не волнует. Как мне этого добиться? Я использую машину Linux.

решение1

Ваш файл состоит из набора объектов JSON. Каждый объект содержит ключ .location_country. Из каждого объекта мы можем создать команду оболочки, которая записывает сериализованную копию самого объекта в файл, названный по значению ключа .location_country. Эти команды оболочки затем могут быть выполнены оболочкой.

С использованием jq,

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

Сериализованный объект может быть создан с помощью @jsonоператора в jq, который выдаст закодированную в JSON строку, содержащую входной документ, в данном случае текущий объект. Затем это передается в , @shчтобы правильно заключить строку в кавычки для оболочки. @shОператор также используется для создания части имени выходного файла из значения ключа .location_country.

По сути, команда создает код оболочки, который будет вызывать printf, выводя текущий объект и перенаправляя вывод в определенный файл.

Учитывая ваш пример данных в 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

Вы можете перенаправить это в отдельный файл и запустить его для shвыполнения команд, или вы можете использовать его evalнепосредственно в оболочке:

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

Поскольку мы используем правильный парсер JSON, jqприведенный выше код будет работать, даже если входной документ JSON не отформатирован с одним объектом на строку.

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

решение2

С использованиемawk

Вход

$ 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

Выход

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

решение3

Учитывая ваши комментарии ниже, это должно сделать то, что вам нужно, используя GNU awk для третьего аргумента match() и обработки множества одновременно открытых файлов:

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

Для скорости выполнения, вероятно, лучшим подходом будет декорирование/сортировка/использование/декорирование, например:

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-

и это будет работать с любым сортом, awk и cut.


Оригинальный ответ:

Если ваши данные всегда такие простые/регулярные, то все, что вам нужно, это сделать с помощью GNU awk (для обработки множества одновременно открытых выходных файлов):

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

или это с любым awk:

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

Вышеуказанный способ будет работать независимо от размера входного файла, если на диске достаточно места для создания выходных файлов.

Если вы хотите, вы можете сделать это более эффективно, сначала отсортировав данные по названию страны:

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

Последний скрипт будет работать с любой сортировкой и любым awk, но он может переставить порядок входных строк для каждой страны. Если вас это волнует и у вас есть сортировка GNU, то добавьте аргумент -s. Если вас это волнует и у вас нет сортировки GNU, дайте мне знать, так как есть довольно простое решение.

Связанный контент