Question

I want to create a simple dialog with bash-dialog. I work with (X)DSL and bash-3.2. The latest (X)DSL is based on Linux 2.4.31 and comes with bash-2.05, however, bash-3.2 is downloadable from MyDSL/Testing. So, my script runs under '#!/bin/bash-3.2/bin/bash'.

The menu items the users can choose from come from a database.

Example database file 'armatures':

Indoor Lighting|Lighting for Indoor use

Outdoor Lighting|Lighting for Outdoor use

I retrieve the data into an array 'options' from the 'armatures' file with:

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

and in terminal 'echo' the array:

echo ${options[@]}

which shows:

"Indoor Armatures" "Lighting for Indoor use" "Outdoor Armatures" "Lighting for Outdoor use"

This looks OK to use as a selection menu with 'whiptail' but it isn't. The command line:

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

shows:

column1-column2

Indoor-Armatures

Lighting-for

Indoor-use

Outdoor-Armatures

Lighting-for

Outdoor-use

in stead of:

column1-column2

Indoor armatures-Lighting for Indoor use

Outdoor armatures-Ligthing for Outdoor use

It seems that array elements with double quotes are ignored or not seen by 'whiptail'. I also tried "${options[@]}" but that always results on the first word 'Indoor'.

Aside from 'whiptail' I tried 'dialog' but they are the same: version information shows 'cdialog (ComeOn Dialog!) version 1.1-20080316' in both cases.

I have very limited resources and don't want to venture (yet) into 'xdialog', 'zenity', 'dzen' and the like, even if that would solve this. I am also limited to Linux 2.4.31 due to XDSL (for XBOX).

I've been browsing the Internet a lot but to no avail. What could be the solution with 'whiptail/dialog'?

Was it helpful?

Solution

The basic problem you are having comes from the order in which the shell parses command lines: it parses (and removes) quotes and escapes before it replaces variables with their values, and it never goes back and re-parses for any quotes or escapes within the replaced values. Consider this example:

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"'

In your case, I'm not sure where the double-quotes are coming from; they're not in the file listing you provided and the awk command won't add them. But in any case, you don't want them stored as part of the value, you want them around the variable reference:

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

Your case is a little more complicated since you're using an array, but the same principle applies. Note that echoing a variable is highly misleading; if it shows what looks like proper quoting, that actually means there's something horribly wrong because it should show the arguments after quote removal.

So, how do you solve it? Try this:

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

UPDATE: I added a test ([ -n "$col1" ]) to execute the loop for an unterminated last line in the database file.

If the double-quotes are actually in the database file, you'll have to remove them; the easiest way to handle this probably to strip quotes while adding the strings to the array, using bash's ability to so string replacement (replacing '"' with blank) while building the array:

options=()
while IFS="|" read col1 col2 || [ -n "$col1" ]; do
    options+=("${col1//'"'/}" "${col2//'"'/}")
done <armatures

OTHER TIPS

The main problem is with the way bash (or any other shell) splits the command lines into "words", as mentioned in the first answer. The line splitting is done on the basis of the IFS - the Internal Field Separator - which is set to <space><tab><newline> by default. The clever chaps (Stephen Bourne etc) who developed the early shells obviously thought it was a good idea for users to be able to change it. It's also nice that it can be set to a multi-character value.

So all you really need to do is set the field separator to a newline, run the awk script, then set it back. You should also quote the array when you use it.

This should work for bash:

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

The array members are now properly defined. Then:

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

For more primitive shells like the Bourne Shell, you will probably need to set IFS by entering "IFS=" (without the quotes), opening single quotes, pressing <Enter>, then closing with a single quote. To reset it again, enter "IFS=" (without the quotes), open single quotes, enter a <space>, quote (e.g. Ctrl-V) a <tab>, hit <Enter>, then close with a single quote.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top