开发者

Select case doesn't work as expected

开发者 https://www.devze.com 2023-02-28 06:49 出处:网络
I wrote this case select statement which refuses to work and I am unable to figure out what I am doing wrong. Any help is appreciated. Thanks.

I wrote this case select statement which refuses to work and I am unable to figure out what I am doing wrong. Any help is appreciated. Thanks.

echo; echo "Did you see a display?"
select YN in "yes" "no" ; do
    case $YN in
        yes)
            echo "$2 at $X x $Y @$REF Hz">>/root/ran.log
            break   
        ;;
        no)
            echo "$2 at $X x $Y @$REF Hz">>/root/didntrun.log
            break   
        ;;
    esac
done

This is the bash -x output of this part of the code. #? repeats many times on the screen and skips prompting the user and jumps to the next part of the code.

+ echo 'Did you see a display?'
Did you see a display?
+ select yn in '"yes"' '"no"'
1) yes
2) no
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? + case $yn in
#? 

EDIT: Figured this happens because the select code is being called in between a loop that reads lines from a file. The change in stdin to the file causes sel开发者_如何学编程ect to skip user prompt and tries to read from file. Now I need a way to sort this out.

EDIT 2: This is the code that makes the calls the fnUseResolution function inside which is the select statement.

res.log has resolution in each line in the format e.g. 1920 1080 60

FILE=res.log
BAKIFS=$IFS
IFS=$(echo -en "\n\b")
exec 3<&0
exec 0<$FILE
while read -r LINE
do
    fnUseResolution $LINE app1
    fnUseResolution $LINE app2
    fnUseResolution $LINE app3
done
exec 0<&3
IFS=$BAKIFS


That works fine for me, it stops and lets me enter either 1 or 2.

See the following transcript:

pax$ cat qq.sh
echo "Did you see a display?"
select YN in "yes" "no" ; do
    case $YN in
        yes)
            echo YES
            break   
        ;;
        no)
            echo NO
            break   
        ;;
    esac
done

pax$ ./qq.sh
Did you see a display?
1) yes
2) no
#? 1
YES

pax$ ./qq.sh
Did you see a display?
1) yes
2) no
#? 2
NO

pax$ _

Are you sure that the standard input is connected to your terminal at that point?


Since you've confirmed my suspicions that your standard input isn't connected to the terminal, there is a somewhat tricky way to fix it by fiddling with file handles.

Consider the following code which does what you indicated in your comment (calling a function accepting standard input from within a loop that has redirected standard input):

fn() {
    echo "Did you see a display?"
    select YN in "yes" "no" ; do
        case $YN in
            yes)
                echo YES $YN $REPLY
                break
            ;;
            no)
                echo NO $YN $REPLY
                break
            ;;
        esac
    done
}

echo 'A
B
C
D' | while read ; do
    echo $REPLY
    fn
    echo ====
done

If you run this code, you'll see:

A
Did you see a display?
1) yes
2) no
#? #? #? #? 
=====

This indicates that the input was intermixed among the two readers, while read and select.

The trick to solving this is to disassociate the two readers by changing the code as follows:

(echo 'A
B
C
D' | while read ; do
    echo $REPLY
    fn <&4
done) 4<&0

The output for this is:

A
Did you see a display?
1) yes
2) no
#? 1                         <- my input.
YES yes 1
=====
B
Did you see a display?
1) yes
2) no
#? 2                         <- my input.
NO no 2
=====
C
Did you see a display?
1) yes
2) no
#? 1                         <- my input.
YES yes 1
=====
D
Did you see a display?
1) yes
2) no
#? 2                         <- my input.
NO no 2
=====

What this does (in its own hideous fashion) is start a subshell to run your whole thing, with the trick being that the subshell first connects the current standard input (terminal, probably) to file handle 4 for later use.

Inside the subshell, you run your normal while read loop which changes the standard input (file handler 0) to read from the echo statement.

But here's the trick: when you call your function, you tell it to take its standard input from file handle 4 rather than the current file handle 0. Since this is connected to the saved, original, standard input, it won't get anything from the while read standard input (the output of echo).

This may take some time to wrap your head around (and I recall having to pick my brains off the floor the first time I saw this, since my head exploded). But, once you understand how it works, you'll see its elegance :-)

The output for this is:

A
Did you see a display?
1) yes
2) no
#? 1
YES yes 1
=====
B
Did you see a display?
1) yes
2) no
#? 2
NO no 2
=====
C
Did you see a display?
1) yes
2) no
#? 1
YES yes 1
=====
D
Did you see a display?
1) yes
2) no
#? 2
NO no 2
=====

And, based on your code, probably a good starting point is to surround the whole lot to preserve standard input, something like:

(FILE=res.log
BAKIFS=$IFS
IFS=$(echo -en "\n\b")
exec 3<&0
exec 0<$FILE
while read -r LINE
do
    fnUseResolution $LINE app1 <&4
    fnUseResolution $LINE app2 <&4
    fnUseResolution $LINE app3 <&4
done
exec 0<&3
IFS=$BAKIFS) 4<&0

I'm assuming here that your function is fnUseResolution.

However, in looking at that code, it may be simpler to use a different file handle for the read rather than the select since you have more control over that than I thought.

Try out:

FILE=res.log
BAKIFS=$IFS
IFS=$(echo -en "\n\b")
#exec 3<&0
exec 4<$FILE
while read -u 4 -r LINE
do
    fnUseResolution $LINE app1
    fnUseResolution $LINE app2
    fnUseResolution $LINE app3
done
#exec 0<&3
IFS=$BAKIFS

The -u 4 option to read tels it to use file handle 4 rather than 0 and I've changed the exec to match that. I haven't tested it but it should work okay.

By doing it that way, you never change standard input so it should be okay within the function. Additionally, there's no reason to save/restore it in file handle 3 in that case, hence I've commented those exec calls out.


This should work; Bash return the user response to variable $REPLY. This shall work, if you type in "yes" or "no".

echo; echo "Did you see a display?"
select YN in "yes" "no" ; do
    case $REPLY in
        yes)
            echo "$2 at $X x $Y @$REF Hz">>/root/ran.log
            break   
        ;;
        no)
            echo "$2 at $X x $Y @$REF Hz">>/root/didntrun.log
            break   
        ;;
        *)
            echo "blah! blah! $REPLY"
            break
    esac
done
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号