Скрипт KSH: как разделить на ',', когда значения экранированы запятыми?

Я пытаюсь написать KSH-скрипт для обработки файла, состоящего из пар имя-значение, по несколько в каждой строке.

Формат:

NAME1 VALUE1,NAME2 VALUE2,NAME3 VALUE3, etc

Допустим, я пишу:

read l
IFS=","
set -A nvls $l
echo "$nvls[2]"

Это даст мне вторую пару имя-значение, красиво и легко. Теперь предположим, что задача расширена так, что значения могут включать запятые. Они должны быть экранированы, например:

NAME1 VALUE1,NAME2 VALUE2_1\,VALUE2_2,NAME3 VALUE3, etc

Очевидно, мой код больше не работает, поскольку «чтение» удаляет все цитирование, а второй элемент массива будет просто «NAME2 VALUE2_1».

Я застрял со старым ksh, в котором нет "read -A array". Я пробовал различные уловки с "read -r" и "eval set -A ....", но безрезультатно. Я не могу использовать "read nvl1 nvl2 nvl3" для отмены экранирования и разделения внутри чтения, так как я не знаю заранее, сколько пар имя-значение находится в каждой строке.

У кого-нибудь есть для меня полезный трюк?

PS Я знаю, что мне пришлось сделать это в самый последний момент на Perl, Python, даже на awk. Однако я должен сделать это в ksh (... или умереть, пытаясь;)


person ADEpt    schedule 05.10.2008    source источник


Ответы (2)


Вы также можете изменить шаблон \, на какой-нибудь другой, который, как известно, не появляется ни в одной из ваших строк, а затем изменить его обратно после того, как вы разбили ввод на массив. Для этого вы можете использовать встроенный синтаксис подстановки шаблонов ksh, вам не нужно использовать sed, awk или что-то еще.

read l
l=${l//\\,/!!}
IFS=","
set -A nvls $l
unset IFS
echo ${nvls[2]/!!/,}
person Bill Karwin    schedule 30.10.2008
comment
Единственное предостережение здесь заключается в том, что более старый KSH (который все еще встречается, например, в SunOS) не имеет этой изящной функции подстановки. - person ADEpt; 04.01.2009

Как это часто бывает, я придумал ответ через несколько минут после того, как задал вопрос на публичном форуме :(

Я работал над проблемой цитирования / отмены цитирования, передавая входной файл по конвейеру с помощью следующего сценария sed:

sed -e 's/\([^\]\),/\1\
/g;s/$/\
/

Он преобразовал входные данные в:

NAME1.1 VALUE1.1
NAME1.2 VALUE1.2_1\,VALUE1.2_2
NAME1.3 VALUE1.3
<empty line>
NAME2.1 VALUE2.1
<second record continues>

Теперь я могу проанализировать этот ввод следующим образом:

while read name value ; do
  echo "$name => $value"
done

Значение будет иметь свои запятые, не заключенные в кавычки "прочитано", и я могу вставить "имя" и "значение" в некоторый ассоциативный массив, если захочу.

PS Поскольку я не могу принять свой ответ, мне следует удалить вопрос или ...?

person ADEpt    schedule 05.10.2008
comment
Используется ли счет sed? Вы также можете использовать awk, perl или ... для работы. Регулярное выражение sed меня немного удивляет; Я бы использовал две обратные косые черты внутри квадратных скобок, но я думаю, что в этом нет необходимости. - person Jonathan Leffler; 11.10.2008
comment
Что касается удаления вопроса - я не знаю, какова рекомендуемая процедура, но сомневаюсь, что они действительно хотят уничтожить ваши слова мудрости. Если произойдет худшее, я могу скопировать ваш ответ и позволить вам выбрать его, но это полный обман. - person Jonathan Leffler; 11.10.2008
comment
Ой. Я только что наткнулся на поток стека stackoverflow.com/questions/209329/. Похоже, лучше оставить как есть. Может быть, кто-то сочтет это полезным и проголосует за него :) - person ADEpt; 17.10.2008