Что лучший способ состоит в том, чтобы преобразовать строку в ассоциативный массив

Ради осуществления я записал несколько строк ниже в сценарии удара для преобразования входа сообщения HTTP в ассоциативный массив. Выполняя на моем приобретении знаний, я задавался вопросом, если там отличаются, возможно, более изящны, способы сделать это. Я имею в виду, я знаю, что существуют различные пути. Я задаюсь вопросом, которые существуют и каковы преимущества и недостатки каждого методы.

Примечание: Я использую сообщение в качестве входа здесь. Осуществление однако должно создать массив из нескольких строка пары имя/значение. Вход мог прибывать из любых файлов или другого входного источника.

Note2: строка, с которой я имею дело, могла быть похожей на это: name=myName&age=myAge. Таким образом, существует 2 разделителя. Пары имя/значение разделения (&) и другое разделение значения с его имени (=).

#!/bin/bash
read post;
declare -A myArr;
IFS_bck=$IFS;
IFS="=";
while read name value; do
  myArr[$name]=$value;
done < <(sed -rn -e 's/&/\n/g p' <<<"$post");
IFS=$IFS_bck;

P.S. Я не готов запустить религиозную войну. Я просто задаюсь вопросом, как Вы сделали бы это и почему Вы будете, предпочел Ваше предложение моему.

1
задан 9 March 2016 в 18:27

4 ответа

Я сделал бы это просто использование bash само:

#!/bin/bash
read -e -p 'Enter the string: ' post
declare -A myArr

while IFS='&' read name age; do
    myArr["${name##*=}"]="${age##*=}"
done <<<"$post"

printf 'NAME: %s, AGE: %s\n' "${!myArr[@]}" "${myArr[@]}"

Вот то, что я вхожу в вывод:

Enter the string: name=Foo Bar&age=40
NAME: Foo Bar, AGE: 40
  • входной строке досаждают на & использование IFS переменная окружения

  • , имя и значения возраста анализируются с помощью шаблона расширения параметра ${var##*=}

  • , можно получить все ключи с помощью ${!myArr[@]} и все значения с помощью ${!myArr[@]}

На практике, я не думаю, что Вы просто сделали бы ассоциативный массив одного элемента. Если Вы имеете несколько элементов, заменяете последнее printf строка с простым for конструкция для цикличного выполнения по ключам:

for key in "${!myArr[@]}"; do
    printf 'NAME: %s, AGE: %s\n' "$key" "${myArr["$key"]}"
done
2
ответ дан 3 December 2019 в 06:22

То, что Вы имеете, не выглядит плохо, но ни Bash, ни sed игра слишком хорошо с разделением на нескольких разделителях; лично я использовал бы AWK и упростил бы сценарий немного (использующий AWK, изменяющийся IFS, становится избыточным; заметьте, что Вам не нужны точки с запятой в конце операторов в Bash):

#!/bin/bash
read post
declare -A myArr
while read name value; do
    myArr[$name]=$value
done < <(<<<"$post" awk -F= '{print $1,$2}' RS='&|\n')

команда AWK:

  • Чтения $post;
  • записи Разделений на последовательностях amperstands / новые строки (разделяющий на новых строках прием, чтобы препятствовать тому while цикл перестать работать на заключительной пустой записи);
  • поля Splits на последовательностях знаков "равно";
  • Печать поля, разделенные разделителем по умолчанию, пространством.
% cat script.sh 
#!/bin/bash
read post
declare -A myArr
while read name value; do
    myArr[$name]=$value
done < <(<<<"$post" awk -F= '{print $1,$2}' RS='&|\n')
printf '%s %s\n' "${myArr[name]}" "${myArr[age]}"
% bash script.sh 
name=myName&age=myAge
myName myAge
% 
3
ответ дан 3 December 2019 в 06:22

Еще один путь, с помощью просто IFS:

#!/bin/bash
declare -A myArr
IFS='&=' read -a post
for ((i = 0; i < ${#post[@]}; i += 2))
do
    myArr[${post[i]}]=${post[i + 1]}
done
for key in "${!myArr[@]}"
do
    printf "%s: %s\n" "$key" "${myArr[$key]}"
done

read разделяет входящую линию на слова на всех символах в IFS, таким образом, можно использовать обоих & и = в IFS разделять на обоих. Учитывая, что вход POST всегда имеет значение для ключа, это будет работать.

Однако этот метод не может проверить, существует ли строгое чередование между & и =. Так, например, age&myAge=name=myName будет проанализирован так, чтобы age=myAge и name=myName.


Примечание о IFS

Вы создали резервную копию IFS и восстановленный это. Но Вам только нужно IFS для read, поэтому подайте заявку IFS только для READ:

IFS='...' read ... # or
while IFS='...' read ...; ...

Восстановление IFS хитро, начиная со сброса IFS и пустое IFS влияйте на оболочку по-другому, но то же, когда Вы принимаете значение IFS самостоятельно. Это:

IFS=
IFS_BAK="$IFS"

даст то же значение для IFS_BAK как:

unset IFS
IFS_BAK="$IFS"

Пустое IFS точно что: пустая строка. Однако сброс IFS заставляет оболочку вести себя как будто значение по умолчанию IFS (пространство, вкладка, новая строка), использовался:

$ foo='a  b  c'
$ printf "|%s|\n" $foo
|a|
|b|
|c|
$ IFS=; printf "|%s|\n" $foo
|a  b  c|
$ unset IFS; printf "|%s|\n" $foo
|a|
|b|
|c|

Так, если Вы нашли себя со сбросом IFS и затем попробовали старый прием backup-and-restore-IFS, Вы могли бы получить неожиданные результаты. Лучше ограничить изменения в IFS быть только для тех команд, для которых нужен он.

2
ответ дан 3 December 2019 в 06:22

Вы сказали:

Таким образом, существует 2 разделителя. Пары имя/значение разделения (и) и другое разделение значения формируют его имя (=).

Ну, мы можем разделить использование пар имя/значение & как IFS в переменные и использование снабжают суффиксом/снабжают префиксом удаление для освобождения значений возраста и подлинного имени.

$> cat post-parse.sh                                                           
#!/bin/bash
IFS='&' read PAIR1 PAIR2
# if necessary use these as well
# name_key=${PAIR1%%=*}
# age_key=${PAIR2%%=*}
name_val=${PAIR1##*=}
age_val=${PAIR2##*=}
echo $name_val $age_val
$> ./post-parse.sh
name=Serg&age=25
Serg 25
$> 

Вы также сказали:

Осуществление однако должно создать массив из нескольких строка пары имя/значение. Вход мог прибывать из любых файлов или другого входного источника.

Если мы хотим сохранить несколько пар "ключ-значение", мы можем считать вход линию за линией (так никакая потребность использовать sed избавиться от \n там), и применяют то же понятие, которое я показал выше:

#!/bin/bash
declare -A myArray
while read input_line ; # read input line by line
do
echo $input_line
  IFS='&' read PAIR1 PAIR2 <<< $input_line # split in two with &
  # name_key=${PAIR1%%=*}
  # age_key=${PAIR2%%=*}
  name_val=${PAIR1##*=}
  age_val=${PAIR2##*=}
  myArray[$name_val]=$age_val
done
# print out the array
for key in "${!myArray[@]}" 
do
   echo ${myArray[$key]} is $key
done

Демонстрационный выполненный рев использует здесь документ, но это могло быть что-либо, даже канал. Точка, читайте, команда не заботится, где она вводится.

$> ./post-parse.sh << EOF                                                      
> name=John&age=25                                                             
> name=Jane&age=35                                                             
> EOF
name=John&age=25
name=Jane&age=35
25 is John
35 is Jane
1
ответ дан 3 December 2019 в 06:22

Другие вопросы по тегам:

Похожие вопросы: