简体   繁体   中英

How to wait for results from a subshell in Bash?

I'm writing a helper function in Bash, whose task is to read some values from STDIN (one per line, like in ls -1 ) and then ask the user to pick one interactively. Before, I would use dmenu , but I wanted to make it more robust and provide for cases where there's no X11 present. I also preferred not to rely on some external tool, so I decided to make the shell built-in select my fallback mechanism. I wrote the following function:

function menu-selector {
    if ! xhost &> /dev/null; then
        cat | dmenu -l 10
    else
        select choice in $(cat); do
            echo $choice
            break
        done
    fi
}

Now, both branches work fine, when I use menu-selector in a pipeline, eg:

ls -1 | menu-selector

In both cases the appropriate mechanisms get invoked ( dmenu or select ) and the chosen value is returned by the function. Trouble begins when I want to use my function somewhere in a subshell. Let's say I now want something like this:

my_dir=$(ls -1 | menu-selector)
echo $my_dir

In the X11 case dmenu pops up and waits for my action, upon which the selected value gets passed back to my code and gets printed. But when I go for the no-X11 option, select prints all options, but instead of waiting for my choice returns an empty value. What's the difference here and, more importantly, how can I make select work the same way, ie block execution of my script until it gets an answer from me. My guess is that the difference lies in the IO devices the two cases use - dmenu using the X root window and select using STDIN/OUT, perhaps being treated differently inside a subshell.

It's because the standard input of the subshell running your menu function is filled with output from ls -1 , so there's nothing for select to read.

Try using arguments instead of stdin to supply options so you don't mixup options and choice:

function menu-selector {
    if ! xhost &> /dev/null; then
        echo "$@" | dmenu -l 10
    else
        select choice in "$@"; do
            echo $choice
            break
        done
    fi
}

and use it like

response=$(menu-selector $(ls -1))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM