#bash #shell #text #cut #collect
Вопрос:
У меня есть файл txt.ini с содержимым (я не могу изменить структуру этого файла):
txt.ini
[person_0:public] name=john groups=0,1,2 age=30 [person_0:private] married=false weight=190 height=100 [person_1:public] name=mark groups=0,4 age=28 [person_1:private] married=false weight=173 height=70 [person_2:public] name=tony groups=3,4 age=30 [person_3:private] married=true weight=202 height=120
У меня есть переменная «человек», которая принимает значение: person_0, person_1, person_3 в цикле, и я хотел бы собирать данные о человеке, такие как возраст и группы для каждого «человека» по одному.
Моя идея состоит в том, чтобы вырезать часть между $person:public и $person:private, а затем собрать
например, установите переменную person=person_1 вывод: группы=0,4 возраст=28
Я подготовил код в bash (persons-это список лиц_0, лиц_1, лиц2):
for person in ${persons[@]}; do file="txt.ini" echo "$person" a=$(awk -v a=$person":private" -v b=$person":public" '/a/{found=0} {if(found) print} /b/{found=1}' $file) IFS=
Список групп и возраст пусты. Выход:
person_0 Group list = Age = person_1 Group list = Age = person_2 Group list = Age =
Expected:
person_0 Group list =0,1,2 Age =30 person_1 Group list =0,4 Age =28 person_2 Group list =3,4 Age =30
Я буду использовать эти данные "на человека" в другой части моего кода. Я работаю над файлами с разным количеством "людей".
Я действительно не знаю, что не так.
Я тоже пытался:
#export person="person_0" #a=$(awk '/ENVIRON["person"]:private/{found=0} {if(found) print} /ENVIRON["person"]:public/{found=1}' $file)
и
private=$person":private" public=$person":public" echo "private=$private" echo "public=$public" a=$(awk -v a=$private" -v b=$public '/a/{found=0} {if(found) print} /b/{found=1}' $config_file)
но результат был тот же самый:
person_0 private=person_0:private public=person_0:public Group list = Age =
Что для меня странно - когда я жестко кодирую диапазон резки, он работает правильно:
a=$(awk '/person_0:private/{found=0} {if(found) print} /person_0:public/{found=1}' $file)
или
a=$(awk '/person_1:private/{found=0} {if(found) print} /person_1:public/{found=1}' $file)
У вас есть какие-либо идеи о том, как я могу собрать эти данные разумным способом?
Комментарии:
1. Каков ожидаемый результат?
2. для человека=персона_0 =gt; группы=0,1,2 возраст=30 и для человека=персона_1 =gt;gt; группы=0,4 возраст 28 и для человека=персона_2 =gt;gt;gt; группы=3,4 возраст=30
3. исправлен переданный код "для человека в ${лиц[@]}; do" лица-это список лиц_0, лиц_1, лиц_2
4. верно, я изменил это
Ответ №1:
Не могли бы вы, пожалуйста, попробовать следующее:
awk -v RS='' ' # split the records on the blank lines /public/ { # "public" record split($1, a, /[[:]/); print a[2] # extract the "person_xx" substring for (i = 2; i lt;= NF; i ) { # iterate over the lines of the record split($i, a, /=/) if (a[1] == "groups") print "Group list =" a[2] else if (a[1] == "age") print "Age =" a[2] } print "" # insert a blank line } ' txt.ini
Выход:
person_0 Group list =0,1,2 Age =30 person_1 Group list =0,4 Age =28 person_2 Group list =3,4 Age =30
- При установке
awk
переменнойRS
в нулевую строку записи разделяются пустыми строками, а поля разделяются символом новой строки. - Предполагая, что нужные данные включены в
public
блок, мы можем анализировать поляpublic
записи одно за другим.
[Править]
Согласно комментарию ОП, вот обновленная версия:
#!/bin/bash persons=("person_0") # list of desired person(s) for person in "${persons[@]}"; do # loop over the bash array awk -v RS='' -v person="$person" ' # assign awk variables $1 ~ person ":public" { # "public" record of the person split($1, a, /[[:]/); print a[2] # extract the "person_xx" substring for (i = 2; i lt;= NF; i ) { # iterate over the lines of the record split($i, a, /=/) if (a[1] == "groups") print "Group list =" a[2] else if (a[1] == "age") print "Age =" a[2] } } ' txt.ini echo # insert a blank line done
- Вы можете назначить
persons
массив любому, кому захотите. - Шаблон
$1 ~ person ":public"
проверяет, соответствует ли 1-е поле записи$1
(например[person_0:public]
) переменной awkperson
(переданной с-v
опцией), за которой следует строка ":public".
Очевидно, что сценарий awk повторяет чтение txt.ini
файла в несколько раз больше, чем элементов #в persons
массиве. Если text.ini
файл длинный и/или persons
массив содержит много элементов, цикл будет неэффективным. Вот еще один вариант:
#!/bin/bash persons=("person_0" "person_1") # bash array just for an example awk -v RS='' -v persons_list="${persons[*]}" ' # persons_list is a blank separated list of persons BEGIN { split(persons_list, a) # split persons_list back to an array for (i in a) persons[a[i]] # create a new array indexed by person } /public/ { # "public" record split($1, a, /[[:]/) # extract the "person_xx" substring if (a[2] in persons) { # if the person exists in the list print a[2] for (i = 2; i lt;= NF; i ) { # iterate over the lines of the record split($i, a, /=/) if (a[1] == "groups") print "Group list =" a[2] else if (a[1] == "age") print "Age =" a[2] } print "" # insert a blank line } } ' txt.ini
Пожалуйста, обратите внимание, что предполагается, что строка person не содержит пробелов. Если это так, измените разделитель при назначении persons_list
неиспользуемому символу, такому как запятая.
Комментарии:
1. Как я могу собирать данные только от одного человека? Например, person_0 и не печатать все. Я имею в виду, что в верхней части кода у меня есть переменная, которая выбирает одного человека, например. в это время x="person_0", и как я могу установить переменные группы и возраста?
2. Спасибо вам за обратную связь. Я обновил свой ответ. Надеюсь, это соответствует вашим требованиям.
Ответ №2:
Допущения:
- для данного человека (например,
person_0
) отобразите указанного человека вместе с соответствующими (public
) полями дляgroups
иage
- не было дано никаких указаний на то, что мы должны делать с этими данными, поэтому предположим, что на данный момент нам просто нужно распечатать в stdout
- список лиц, подлежащих обработке, находится в
bash
массивеpersons[]
- строки
:public
и:private
отображаются только в заголовках блоков
Одна awk
идея, в которой мы используем split()
функцию для анализа строки на основе разных разделителей:
awk ' FNR==NR { persons[$1] next } /:private/ { printme=0 } /:public/ { printme=0 split($1,arr,"[]:[]") person=arr[2] if (person in persons) { printme=1 printf "%s%sn", pfx, person pfx="n" } } printme { split($1,arr,"=") if (arr[1] == "groups") print "Group list =" arr[2] if (arr[1] == "age") print "Age =" arr[2] } ' lt;(printf "%sn" "${persons[@]}") txt.ini
Вариация на эту тему с использованием разделителя полей ввода из нескольких символов:
awk -F"[]:=[]" ' FNR==NR { persons[$1] next } $3=="private" { printme=0 } $3=="public" { printme=0 if ($2 in persons) { printme=1 printf "%s%sn", pfx, $2 pfx="n" } } printme amp;amp; $1=="groups" { print "Group list =" $2 } printme amp;amp; $1=="age" { print "Age =" $2 } ' lt;(printf "%sn" "${persons[@]}") txt.ini
С:
$ typeset -p persons declare -a persons=([0]="person_0" [1]="person_1" [2]="person_2")
Оба набора awk
кода генерируют:
person_0 Group list =0,1,2 Age =30 person_1 Group list =0,4 Age =28 person_2 Group list =3,4 Age =30
ПРИМЕЧАНИЕ: это можно было бы сделать более динамичным ( public
и/или private
? в разных областях?) но это повлечет за собой немного больше кодирования
Комментарии:
1. Как я могу собирать данные только от одного человека? Например, person_0 и не печатать все. Я имею в виду, что в верхней части кода у меня есть переменная, которая выбирает одного человека, например. в это время x="person_0", и как я могу установить переменные группы и возраста?
n' lines=($a) IFS='=' read grouplist grouplist_values lt;lt;lt; ${lines[1]} IFS='=' read age age_values lt;lt;lt; ${lines[4]} echo "Group list = $grouplist_values" echo "Age = $age_values" Список групп и возраст пусты. Выход:
Expected:
Я буду использовать эти данные «на человека» в другой части моего кода. Я работаю над файлами с разным количеством «людей».
Я действительно не знаю, что не так.
Я тоже пытался:
и
но результат был тот же самый:
Что для меня странно — когда я жестко кодирую диапазон резки, он работает правильно:
или
У вас есть какие-либо идеи о том, как я могу собрать эти данные разумным способом?
Комментарии:
1. Каков ожидаемый результат?
2. для человека=персона_0 =gt; группы=0,1,2 возраст=30 и для человека=персона_1 =gt;gt; группы=0,4 возраст 28 и для человека=персона_2 =gt;gt;gt; группы=3,4 возраст=30
3. исправлен переданный код «для человека в ${лиц[@]}; do» лица-это список лиц_0, лиц_1, лиц_2
4. верно, я изменил это
Ответ №1:
Не могли бы вы, пожалуйста, попробовать следующее:
Выход:
- При установке
awk
переменнойRS
в нулевую строку записи разделяются пустыми строками, а поля разделяются символом новой строки. - Предполагая, что нужные данные включены в
public
блок, мы можем анализировать поляpublic
записи одно за другим.
[Править]
Согласно комментарию ОП, вот обновленная версия:
- Вы можете назначить
persons
массив любому, кому захотите. - Шаблон
$1 ~ person ":public"
проверяет, соответствует ли 1-е поле записи$1
(например[person_0:public]
) переменной awkperson
(переданной с-v
опцией), за которой следует строка «:public».
Очевидно, что сценарий awk повторяет чтение txt.ini
файла в несколько раз больше, чем элементов #в persons
массиве. Если text.ini
файл длинный и/или persons
массив содержит много элементов, цикл будет неэффективным. Вот еще один вариант:
Пожалуйста, обратите внимание, что предполагается, что строка person не содержит пробелов. Если это так, измените разделитель при назначении persons_list
неиспользуемому символу, такому как запятая.
Комментарии:
1. Как я могу собирать данные только от одного человека? Например, person_0 и не печатать все. Я имею в виду, что в верхней части кода у меня есть переменная, которая выбирает одного человека, например. в это время x=»person_0″, и как я могу установить переменные группы и возраста?
2. Спасибо вам за обратную связь. Я обновил свой ответ. Надеюсь, это соответствует вашим требованиям.
Ответ №2:
Допущения:
- для данного человека (например,
person_0
) отобразите указанного человека вместе с соответствующими (public
) полями дляgroups
иage
- не было дано никаких указаний на то, что мы должны делать с этими данными, поэтому предположим, что на данный момент нам просто нужно распечатать в stdout
- список лиц, подлежащих обработке, находится в
bash
массивеpersons[]
- строки
:public
и:private
отображаются только в заголовках блоков
Одна awk
идея, в которой мы используем split()
функцию для анализа строки на основе разных разделителей:
Вариация на эту тему с использованием разделителя полей ввода из нескольких символов:
С:
Оба набора awk
кода генерируют:
ПРИМЕЧАНИЕ: это можно было бы сделать более динамичным ( public
и/или private
? в разных областях?) но это повлечет за собой немного больше кодирования
Комментарии:
1. Как я могу собирать данные только от одного человека? Например, person_0 и не печатать все. Я имею в виду, что в верхней части кода у меня есть переменная, которая выбирает одного человека, например. в это время x=»person_0″, и как я могу установить переменные группы и возраста?