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

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

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

Примечание2: строка, с которой я имею дело, может выглядеть так: 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 в 19:27

3 ответа

То, что у вас есть, выглядит плохо, но ни 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; Разделяет записи по последовательностям амперстанформ / строк новой строки (разделение на строки новой строки - это трюк, чтобы предотвратить остановку цикла while в последней пустой записи); Разделяет поля на последовательностях равных знаков; Печать полей, разделенных разделителем по умолчанию, пробелом.
% 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
ответ дан 23 May 2018 в 12:59
  • 1
    kossy, почему не просто while IFS=\= .... и избавиться от ненужного разбора и замены процесса .. Кроме того, OP должен знать, что они должны указывать свои переменные. В любом случае у вас всегда есть мой +1. – heemayl 9 March 2016 в 17:44
  • 2
    @heemayl Я помню, как заметил комментарий, в котором упоминался вариант read -d. Почему никто из вас не использовал его, и комментарий исчез? – muru 9 March 2016 в 19:26
  • 3
    @muru duh..need кофе..kos, вперед..i вне .. – heemayl 9 March 2016 в 19:30
  • 4
    @muru Это было мое, но это нежизнеспособно, так как они читают с помощью read; если я использую & как разделитель, я также заставляю их вводить & в конце. – kos 9 March 2016 в 19:32
  • 5
    (если они копируют-вставить. В противном случае я заставляю их читать одну пару ключ / значение). – kos 9 March 2016 в 19:52

Другой способ, используя только 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, поэтому вы можете использовать как &, так и [ f10] в IFS, чтобы разделить на оба. Учитывая, что вход POST всегда имеет значение для ключа, это будет работать.

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

Заметка о IFS

У вас есть скопировал IFS и восстановил его. Но вам нужно только IFS для read, поэтому примените IFS только для READ:

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

Восстановление IFS затруднительно, так как unset IFS и пустой [ f25] влияют на оболочку по-разному, но одинаковы, когда вы принимаете значение самого 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, а затем попробовал старый трюк с резервным копированием и восстановлением IFS, вы можете получить неожиданные результаты. Лучше ограничивать изменения в IFS только теми командами, которые в этом нуждаются.

2
ответ дан 23 May 2018 в 12:59

Вы сказали:

Итак, есть 2 разделителя. Одна разделяющая пары имя / значение (& amp;), а другая, разделяющая значение, формирует его имя (=).

Ну, мы можем разделить пары имя / значение, используя & как 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
$> 

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

Итак, есть 2 разделителя. Одна из разделяющих пар имя / значение (& amp;), а другая, разделяющая значение, формирует его имя (=).

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

#!/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

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

$> ./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
ответ дан 23 May 2018 в 12:59

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

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