Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a bash inputmenu dialog start with rename automatically clicked?

Tags:

bash

dialog

menu

I am trying to make a bash inputmenu dialog handle different types, such as, files, dates, regular text. Clicking the edit button, will simply send the user to the correct dialog to retrieve the input. For regular text, I simply want to use the rename feature of inputmenu. I cannot have the user manually select rename because I only want the rename action to be used for text inputs. Having an identical looking dialog load up with the rename action automatically selected, would allow me to solve this problem.

What I'm trying to do

I've tried to achieve this by changing the file descriptor and passing in , \n and \r characters as inputs but with no luck.

#!/bin/bash

list=(aaa bbb ccc)
selected=1

function menu1()
{
    count=0
    declare -a items
    for item in "${list[@]}"; do
        items+=($((++count)) "$item")
    done
    echo -n '2 ' > tmp.txt
    exec 4<tmp.txt
    cmd=(dialog
        --input-fd 4
        --print-size
        --print-maxsize
        --extra-button --extra-label "Edit"
        --default-button extra
        --default-item "$selected"
        --inputmenu "Select action:" 22 76 20)
    exec 3>&1
    #choices=$("${cmd[@]}" "${items[@]}" <<< ' ' 2>&1 1>&3)
    choices=$("${cmd[@]}" "${items[@]}" 2>&1 1>&3)
    retVal=$?
    exec 3>&-
    readarray -t choices <<< "${choices}"
    choices="${choices[2]}"
    echo "choices=$choices"
    echo "retVal=$retVal"
    menuAction "$retVal" "${choices[0]}"
}

function menu()
{
    count=0
    declare -a items
    for item in "${list[@]}"; do
        items+=($((++count)) "$item")
    done
    cmd=(dialog
        --print-size
        --print-maxsize
        --extra-button --extra-label "Edit"
        --default-button extra
        --default-item "$selected"
        --inputmenu "Select action:" 22 76 20)
    exec 3>&1
    choices=$("${cmd[@]}" "${items[@]}" 2>&1 1>&3)
    retVal=$?
    exec 3>&-
    readarray -t choices <<< "${choices}"
    choices="${choices[2]}"
    echo "choices=$choices"
    echo "retVal=$retVal"
    menuAction "$retVal" "${choices[0]}"
}

function menuAction()
{
    retVal="$1"
    choice="$2"
    declare -a choice="${choice[0]}"
    if [[ "$retVal" -eq 3 ]]; then
        choice=(${choice[0]})
        if [[ "${choice[0]}" == "RENAMED" ]]; then
            let selected=choice[1]
            let index=choice[1]-1
            unset choice[0]
            unset choice[1]
            list[$index]="${choice[@]}"
        fi
    fi
    [[ "$retVal" -ne 1 ]] && menu
}

menu1

Edit

I've nearly got it working using expect. Unfortunately, after expect has sent input to the dialog, it returns back to the terminal:

#!/bin/bash

/usr/bin/expect <<EOD
    set timeout 30
    spawn ./dialog1 >/dev/tty
    sleep 1
    send " "
    expect eof
EOD

NB

I am fully aware that I am trying to circumvent dialogs limitations and this is usually a bad thing, as it results in poorer code that's harder to maintain and may have unnecessary dependencies. This question should be considered more of a learning exercise. Practically, it's only worthwhile in exceptional circumstances or as an optional extra. I am creating an api maker and I will be giving the client the choice of optional enhancements, such as this, that require a hackish solution.

like image 207
Dan Bray Avatar asked Aug 31 '25 17:08

Dan Bray


1 Answers

I agree with other commenters that it's probably a very bad idea but here you go:

I haven't figured how to do that with expect, but it's possible with its alternative called empty (sudo apt install empty-expect in ubuntu/debian)

It is also possible to detect or ignore Ctrl+C but not both - see comments.

#!/bin/bash

#temporary files
export MYTMP=/tmp/dialog_$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM
export MYPID=${MYTMP}_pid.tmp
export MYOUT=${MYTMP}_out.tmp
export MYINP=${MYTMP}_inp.tmp
export MYRSL=${MYTMP}_rsl.tmp

#record "real" TTY just in case (this should not be necessary in most cases)
export MYTTY=`tty`

#replace command between `dialog` and `;` with your desired command or shell script
empty -f -i ${MYINP} -o ${MYOUT} /bin/bash -c 'dialog --extra-button --extra-label "Edit" --default-button extra --inputmenu "Select action:" 22 76 20 x "xx" y "yy" >${MYTTY} 2>${MYRSL} ; kill -SIGINT `cat ${MYPID}`'

#send "ENTER" key
sleep 0.1
echo -n -e '\n' >${MYINP}

# How to input keystrokes:
# \n - enter
# \e\e - ESC
# \t - tab (next button)
# \x0e - up     (or \e[A on some terminals)
# \x10 - down   (or \e[B on some terminals)

##optional: delete whatever was in the input box by pressing "DEL" a bunch of times
#echo -n -e '\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~\e[3~' >${MYINP}

## to detect Ctrl+C uncomment next line of code
#trap "export CTRL_C_PRESSED=1" INT
## ...and replace -SIGINT with -SIGTERM on line that starts with:   empty -f ...
##(you cannot both detect and ignore the signal)
## you could then test if ${CTRL_C_PRESSED} is equal to 1

#save PID to a file and redirect user input into 'dialog'
/bin/bash -c 'echo $$ >${MYPID} ; exec cat >${MYINP}'
## to ignore Ctrl+C insert:   ; trap "" INT ;   in place of semicolon (;) on previous line
## ...and replace -SIGINT with -SIGTERM on line that starts with:   empty -f ...

##If you trap/ignore Ctrl+C the script may screw up the terminal
##in that case please run    reset    command to fix, it also clears the screen


# check result in the file as you normally do
echo "----Result: `cat ${MYRSL}` "


#remove temporary files/pipes
sleep 0.1
if [ -e ${MYPID} ]; then rm ${MYPID}; fi;
if [ -e ${MYINP} ]; then rm ${MYINP}; fi;
if [ -e ${MYOUT} ]; then rm ${MYOUT}; fi;
if [ -e ${MYRSL} ]; then rm ${MYRSL}; fi;

How it works:

  • Actual command is executed in another shell but output is piped directly to the tty
  • empty intercepts input and output and attaches them to pipes
  • necessary commands are output to the 'input' pipe
  • then TTY's actual STDIN is redirected to that same pipe
  • command result is saved in a temporary file
  • when the command finishes executing it sends Ctrl+C to cat to stop the redirection
    • (otherwise cat does not realize that the pipe no longer exists so user has to press ENTER one more time)

It may be possible to avoid temporary files but that would complicate the code even more

like image 64
Jack White Avatar answered Sep 02 '25 10:09

Jack White