2

I'm trying to create a dialog menu based on the results of the lsblk command. My goal is to read the first word as the menu item and the remaining words as the menu item description.

For example:

$ dialog --menu "Choose one:" 0 0 0 $(lsblk -lno name,size | grep sda)

This command works, because the dialog --menu option expects two arguments per menu item, in this case "name" and "size".

However, if I try the same command with a multi-word description (using the "read" built-in to write to stdout as two variables), word-splitting occurs in the second variable, even if quoted.

#commmand 1: unquoted variable
$ dialog --menu "Choose one:" 0 0 0 $(lsblk -lno name,type,size | grep sda |
> while read name desc; do
> echo $name $desc
> done)                      #results in output like below without quotes

#command 2: quoted variable 
$ dialog --menu "Choose one:" 0 0 0 $(lsblk -lno name,type,size | grep sda |
> while read name desc; do
> echo $name "$desc"
> done)                      #results in output like below without quotes

#command 3: escape quoted variable
$ dialog --menu "Choose one:" 0 0 0 $(lsblk -lno name,type,size | grep sda |
> while read name desc; do
> echo $name \"$desc\"
> done)                      #results in output below

command 3 output

I don't understand why word splitting is occurring in the quoted variable. Can anyone explain and/or suggest a workaround? I've tried writing the output of lsblk to a file and reading the file with the same results. The desired output is:

Desired output

EDIT: I looked at command quoting as a possible solution, but it results in the lsblk command output being passed to dialog as one argument when two are required.

Thanks.

  • 1
    Quoting the command results in only one argument being passed to dialog. I forgot to mention that in my initial post. – slo.sleuth Oct 17 '16 at 23:23
  • Easier for you maybe if you do smth. like while read -r name desc; do args+=($name "$desc"); done < <(lsblk -lno name,type,size | grep sda) and then dialog --menu "Choose one:" 0 0 0 "${args[@]}" – don_crissti Oct 18 '16 at 00:38
  • Thanks, that works with BASH 4+, which I may need to use. I've been working in a SH environment, which I failed to mention. – slo.sleuth Oct 18 '16 at 18:10
  • The way you populate the array is up to you though... Here's an alternative with zsh (and gnu sed) that doesn't use while ... read: args=(${(f)"$(lsblk -lno name,type,size | sed -E '/sda/!d;s/ +/\n/')"}) then again dialog --menu "Choose one:" 0 0 0 "${args[@]}" – don_crissti Oct 18 '16 at 18:30
  • Come to think, you can also build up the command line as a string and pipe it to sh (kinda ugly though...) - this one should be more portable: printf %s%s\\n 'dialog --menu "Choose one:" 0 0 0 '"$(lsblk -lno name,type,size | sed '/sda/!d;s/[[:blank:]]\{1,\}/&"/;s/$/"/' | tr '\n' ' ')" | sh – don_crissti Oct 18 '16 at 18:32
  • If you don't mind the underscores you can do without the above and simply run dialog --menu "Choose one:" 0 0 0 $(lsblk -lno name,type,size | sed '/sda/!d;s/[[:blank:]]\{1,\}/\n/;s/[[:blank:]]/_/g;s/\n/ /') (this is gnu sed syntax though, to make it work with other seds replace the 1st \n with a backslash followed by a literal newline - I can't use that in a comment block as it won't be preserved...) – don_crissti Oct 19 '16 at 00:33

1 Answers1

1

You need to do this in 2 parts:

# 1. read the output of lsblk, 2 words per line, into an array
parts=()
while read -r disk data; do 
    parts+=("$disk" "$data")
done < <(lsblk -lno name,type,size | grep sda)

# 2. send the elements of the array to the dialog command
dialog --menu "Choose one:" 0 0 0 "${parts[@]}"

The read command will take the first whitespace-separated work into the disk variable, and then the rest of the line into data. It's very important to quote all the variables to avoid word splitting

glenn jackman
  • 85,964
  • You were kind of on the right track with quoting, but your "outer" command substitution was not quoted which undid all your efforts. – glenn jackman Oct 18 '16 at 21:40
  • Thanks. I tried quoting the outer command, but it passed only one argument to dialog. It appears that I'll need to install and use BASH for arrays. The system on which I was planning to use this command (Slitaz) uses busybox sh by default so arrays of this sort are not available to me. – slo.sleuth Oct 18 '16 at 23:18