Для упражнения я написал несколько строк ниже в сценарии 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. Я не хочу начинать религиозную войну. Мне просто интересно, как вы это сделаете, и почему вы выбрали свое предложение по моему.
То, что у вас есть, выглядит плохо, но ни 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
%
Другой способ, используя только 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 для 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 разделителя. Одна разделяющая пары имя / значение (& 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