admin管理员组

文章数量:1122846

I'm trying to build a simple dialog where the user is asked to make a choice (with a menu widget) and then confirm it (with a yesno widget). The separate confirmation is required (preferred, anyway) because any following actions format your disk, which could lead to complaints.

Chaining widgets is easy enough, but passing selections from a menu to downstream widgets is non-trivial. Can anyone suggest a way to do this? In this attempt, the menu output goes to fd 3 (--output-fd won't take a pipe name), and I attempt to read fd 3 for the second widget:

#!/bin/bash

exec 3<> /tmp/fubar
confirm=$(cat /dev/fd/3 &)

res=$(dialog --clear \
       --output-fd 3 \
       --backtitle "Backtitle"  \
       --title "Title" \
       --menu "Please select:" 15 30 4 \
       "1" "Foo" \
       "2" "Bar" \
       --clear   \
       --stderr  \
       --yesno "$confirm" 15 30 \
       2>&1 >/dev/tty)

This doesn't work, of course, not least because $confirm is expanded early. I also need to build a confirmation string instead of just displaying an integer in the yesno box. Any ideas?

EDIT

Minor variation of Gilles' answer below, to use a var instead of a file:

#!/bin/bash

choice=$(dialog --clear \
    --backtitle "Backtitle" \
    --title "Title" \
    --menu "Please select:" 15 30 4 \
    "1" "Foo" \
    "2" "Bar" \
    2>&1 >/dev/tty)

case $choice in
    1) label="foo" ;;
    2) label="bar"  ;;
    *) echo >&2 "ERR $choice"; exit 1 ;;
esac

dialog --clear \
    --backtitle "Backtitle" \
    --title "Confirm" \
    --yesno "You selected option $label. Do you want to proceed?" 10 40

I'm trying to build a simple dialog where the user is asked to make a choice (with a menu widget) and then confirm it (with a yesno widget). The separate confirmation is required (preferred, anyway) because any following actions format your disk, which could lead to complaints.

Chaining widgets is easy enough, but passing selections from a menu to downstream widgets is non-trivial. Can anyone suggest a way to do this? In this attempt, the menu output goes to fd 3 (--output-fd won't take a pipe name), and I attempt to read fd 3 for the second widget:

#!/bin/bash

exec 3<> /tmp/fubar
confirm=$(cat /dev/fd/3 &)

res=$(dialog --clear \
       --output-fd 3 \
       --backtitle "Backtitle"  \
       --title "Title" \
       --menu "Please select:" 15 30 4 \
       "1" "Foo" \
       "2" "Bar" \
       --clear   \
       --stderr  \
       --yesno "$confirm" 15 30 \
       2>&1 >/dev/tty)

This doesn't work, of course, not least because $confirm is expanded early. I also need to build a confirmation string instead of just displaying an integer in the yesno box. Any ideas?

EDIT

Minor variation of Gilles' answer below, to use a var instead of a file:

#!/bin/bash

choice=$(dialog --clear \
    --backtitle "Backtitle" \
    --title "Title" \
    --menu "Please select:" 15 30 4 \
    "1" "Foo" \
    "2" "Bar" \
    2>&1 >/dev/tty)

case $choice in
    1) label="foo" ;;
    2) label="bar"  ;;
    *) echo >&2 "ERR $choice"; exit 1 ;;
esac

dialog --clear \
    --backtitle "Backtitle" \
    --title "Confirm" \
    --yesno "You selected option $label. Do you want to proceed?" 10 40
Share Improve this question edited Nov 22, 2024 at 15:56 QF0 asked Nov 22, 2024 at 14:16 QF0QF0 5291 gold badge4 silver badges18 bronze badges 2
  • Your variation is less readable, less maintenable and you introduced a bug ¹. That was all the point to keep the things simple as in my answer.¹ 2>&1 is after file, and better use & shortcut: &>file in bash. – Gilles Quénot Commented Nov 23, 2024 at 6:31
  • @GillesQuénot: dialog outputs to stderr; curses outputs to stdout. This invocation allows the dialog output to be returned by $(), while keeping the curses output on /dev/tty. This is pretty much the canonical way to use dialog while avoiding temporary files. – QF0 Commented Nov 23, 2024 at 14:33
Add a comment  | 

2 Answers 2

Reset to default 3

Keep it simple stupid!

#!/bin/bash

trap 'rm -f /tmp/$$_temp' EXIT

dialog --clear \
    --output-fd 1 \
    --backtitle "Backtitle" \
    --title "Title" \
    --menu "Please select:" 15 30 4 \
    "1" "Foo" \
    "2" "Bar" >/tmp/$$_temp

case $(< /tmp/$$_temp) in
    1) label=yes ;;
    2) label=no  ;;
    *) echo >&2 "ERR $choice"; exit 1 ;;
esac

dialog --clear \
    --backtitle "Backtitle" \
    --title "Confirm" \
    --yesno "You selected option $label. Do you want to proceed?" 10 40
[...]

@GillesQuénot already answered your question so please do not accept this instead as this is not a different answer just a suggestion that's too long and formatted for a comment of a way to implement the existing solution without redundancy:

$ cat tst.sh
#!/usr/bin/env bash

prompt() {
    dialog \
        --clear \
        --backtitle 'Backtitle' \
        "$@" \
        2>&1 >/dev/tty
}

declare -A labels=(
    ['1']='Foo'
    ['2']='Bar'
)

titleArgs=(
    --title 'Title'
    --menu 'Please select:' 15 30 4
)

while IFS= read -r idx; do
    titleArgs+=( "$idx" "${labels[$idx]}" )
done < <(printf '%s\n' "${!labels[@]}" | sort -n)

choice=$( prompt "${titleArgs[@]}" )

if [[ -v labels["$choice"] ]]; then
    label="${labels[$choice]}"
else
    printf 'ERR %s\n' "$choice" >&2
    exit 1
fi

confirmArgs=(
    --title 'Confirm'
    --yesno "You selected option $choice ($label). Do you want to proceed?" 10 40
)

prompt "${confirmArgs[@]}"; confirmed=$?

printf 'confirmation result: %s\n' "$confirmed" >&2

本文标签: Bash dialog how do I chain widgetspassing data from one to the nextStack Overflow