Ради осуществления я записал несколько строк ниже в сценарии удара для преобразования входа сообщения 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. Я не готов запустить религиозную войну. Я просто задаюсь вопросом, как Вы сделали бы это и почему Вы будете, предпочел Ваше предложение моему.
Я сделал бы это просто использование 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
То, что Вы имеете, не выглядит плохо, но ни 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
, таким образом, можно использовать обоих &
и =
в 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 разделителя. Пары имя/значение разделения (и) и другое разделение значения формируют его имя (=).
Ну, мы можем разделить использование пар имя/значение &
как 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