пробелы bash в пунктах меню whiptail/dialog

Я хочу создать простой диалог с помощью bash-dialog. Работаю с (X)DSL и bash-3.2. Последний (X)DSL основан на Linux 2.4.31 и поставляется с bash-2.05, однако bash-3.2 можно загрузить с MyDSL/Testing. Итак, мой скрипт работает под «#!/bin/bash-3.2/bin/bash».

Пункты меню, из которых пользователи могут выбирать, поступают из базы данных.

Пример файла базы данных 'armatures':

Внутреннее освещение|Внутреннее освещение

Наружное освещение|Уличное освещение

Я извлекаю данные в массив «options» из файла «armatures» с помощью:

options=($(awk -F| '{print $1,$2 }' каркасы)

и в терминале "эхо" массив:

эхо ${опции[@]}

который показывает:

Внутреннее освещение арматуры для внутреннего использования Наружное освещение арматуры для наружного использования

Это выглядит нормально для использования в качестве меню выбора с «whiptail», но это не так. Командная строка:

whiptail --clear --title Armatures --menu Выберите арматуру 50 80 10 ${options[@]}

показывает:

столбец1-столбец2

Внутренняя арматура

Освещение-для

Использование в помещении

Наружная арматура

Освещение-для

Наружное использование

вместо:

столбец1-столбец2

Внутренняя арматура-Освещение для внутреннего использования

Наружная арматура-Освещение для наружного применения

Кажется, что элементы массива с двойными кавычками игнорируются или не видны "хлыстохвосту". Я также попробовал ${options[@]}, но это всегда приводит к первому слову «Indoor».

Помимо «whiptail», я попробовал «dialog», но они одинаковы: информация о версии показывает «cdialog (ComeOn Dialog!) Версия 1.1-20080316» в обоих случаях.

У меня очень ограниченные ресурсы, и я не хочу (пока) рисковать в «xdialog», «zenity», «dzen» и тому подобное, даже если это решит эту проблему. Я также ограничен Linux 2.4.31 из-за XDSL (для XBOX).

Я много просматривал Интернет, но безрезультатно. Какое может быть решение с «хлыстом/диалогом»?


person linuph    schedule 03.06.2013    source источник


Ответы (2)


Основная проблема, с которой вы столкнулись, связана с порядком, в котором оболочка анализирует командные строки: она анализирует (и удаляет) кавычки и escape-последовательности до того, как заменяет переменные их значениями, и никогда не возвращается назад и повторно не анализирует любые кавычки или escape-последовательности внутри замененные значения. Рассмотрим этот пример:

var='"foo bar"'   # Note that the single-quotes will be removed, and the
                  # double-quotes will be treated as part of the variable value.
somecmd $var   # This runs cmd with 2 arguments: '"foo' and 'bar"'

В вашем случае я не уверен, откуда берутся двойные кавычки; их нет в предоставленном вами списке файлов, и команда awk не добавит их. Но в любом случае вы не хотите, чтобы они сохранялись как часть значения, вы хотите, чтобы они были вокруг ссылки на переменную:

var='foo bar'   # Again, the single-quotes are removed.
somecmd "$var"   # This runs cmd with a single argument: 'foo bar'

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

Итак, как решить эту проблему? Попробуй это:

options=()
while IFS="|" read col1 col2 || [ -n "$col1" ]; do
    options+=("$col1" "$col2")  # Note the double-quotes around the variable references
done <armatures
echo "options:"
printf "  '$s'\n" "${options[@]}"  # This prints each array element on a separate line
whiptail --clear --title "Armatures" --menu "Choose an armature" 50 80 10 "${options[@]}"  # Again, double-quotes around the reference

ОБНОВЛЕНИЕ: я добавил тест ([ -n "$col1" ]) для выполнения цикла для незавершенной последней строки в файле базы данных.

Если двойные кавычки действительно есть в файле базы данных, вам придется их удалить; самый простой способ справиться с этим, вероятно, удалить кавычки при добавлении строк в массив, используя способность bash к такой замене строк (заменяя '"' пробелом) при построении массива:

options=()
while IFS="|" read col1 col2 || [ -n "$col1" ]; do
    options+=("${col1//'"'/}" "${col2//'"'/}")
done <armatures
person Gordon Davisson    schedule 03.06.2013
comment
Спасибо! Разбор оболочки не прост для понимания, но ваше объяснение очень помогает. Однако с кодом, который вы предлагаете, есть одна проблема: последняя запись не отображается в «хлыстохвосте», т.е. в приведенном выше примере отображается только первая запись. При добавлении дополнительной записи с помощью символа | обе записи отображаются в виде «хлыстохвоста». Как мне избежать добавления дополнительного | записывать? - person linuph; 04.06.2013
comment
@linuph: последняя строка в базе данных, вероятно, не завершена (т. Е. В конце нет символа перевода строки); Я добавил тест для запуска цикла для любой непустой строки, даже если она не завершена. Кстати, это говорит о том, что файл базы данных не имеет стандартного формата текстового файла Unix, а вместо этого может быть в формате DOS, и в этом случае он будет иметь непечатаемый символ возврата каретки в конце каждой строки (и он будет наматывать в конце каждой записи col2). Если да, то есть способы его отфильтровать... - person Gordon Davisson; 04.06.2013
comment
У меня нет двойных кавычек в базе данных, так что все в порядке. Я проверю окончание последней строки. Спасибо еще раз! - person linuph; 05.06.2013
comment
Действительно, чтобы не пропустить последнюю запись, эта последняя запись должна заканчиваться кодом следующей строки, то есть '\n'. Это все. - person linuph; 10.06.2013

Основная проблема заключается в том, как bash (или любая другая оболочка) разбивает командные строки на «слова», как упоминалось в первом ответе. Разделение строк выполняется на основе IFS — внутреннего разделителя полей, который по умолчанию установлен на ‹space›‹tab›‹newline›. Умные парни (Стивен Борн и др.), разработавшие ранние оболочки, очевидно, считали хорошей идеей, чтобы пользователи могли изменить ее. Также приятно, что для него можно установить многосимвольное значение.

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

Это должно работать для bash:

IFS=$'\n'
options=($(awk -F"|" '{ print $1,$2 }' armatures)
IFS=$' \t\n'

Члены массива теперь правильно определены. Потом:

whiptail --clear --title "Armatures" --menu "Choose an armature" 50 80 10 "${options[@]}"

Для более примитивных оболочек, таких как Bourne Shell, вам, вероятно, потребуется установить IFS, введя "IFS=" (без кавычек), открывая одинарные кавычки, нажимая ‹Enter›, а затем закрывая одинарной кавычкой. Чтобы сбросить его снова, введите "IFS=" (без кавычек), откройте одинарные кавычки, введите ‹пробел›, заключите в кавычки (например, Ctrl-V) ‹tab›, нажмите ‹Enter›, затем закройте одинарной кавычкой.

person wanpelaman    schedule 30.01.2016