Выберите дело не работает, как ожидалось - PullRequest
0 голосов
/ 21 апреля 2011

Я написал это заявление выбора дела, которое отказывается работать, и я не могу понять, что я делаю неправильно. Любая помощь приветствуется. Спасибо.

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

Это вывод bash -x этой части кода. #? повторяется много раз на экране и пропускает запрос пользователя и переходит к следующей части кода.

+ 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
#? 

РЕДАКТИРОВАТЬ: понял, что это происходит, потому что код выбора вызывается между циклом, который читает строки из файла. Изменение в stdin файла приводит к тому, что select пропускает приглашение пользователя и пытается прочитать файл. Теперь мне нужен способ разобраться в этом.

РЕДАКТИРОВАТЬ 2: Это код, который вызывает функции fnUseResolution, внутри которых находится оператор выбора.

res.log имеет разрешение в каждой строке в формате, например 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

Ответы [ 2 ]

2 голосов
/ 21 апреля 2011

Это прекрасно работает для меня, останавливается и позволяет мне ввести либо 1, либо 2.

См. Следующую расшифровку:

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$ _

Вы уверены, что стандартвход подключен к вашему терминалу в этот момент?


Поскольку вы подтвердили мои подозрения, что ваш стандартный вход не подключен к терминалу, является несколько сложным способомчтобы исправить это, поиграв с дескрипторами файлов.

Рассмотрим следующий код, который выполняет то, что вы указали в своем комментарии (вызывая функцию, принимающую стандартный ввод из цикла, который перенаправил стандартный ввод):

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

Если вы запустите этот код, вы увидите:

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

Это указывает на то, что ввод был перемешан между двумя считывателями, while read и select.

Хитрость в решении этой проблемы состоит в том, чтобы разъединить двух читателей, изменив код следующим образом:

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

Вывод для этого:

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
=====

Что это делает (в своем отвратительноммода) это начало недоработкичтобы у вас все получилось, с хитростью в том, что подоболочка сначала соединяет стандартный текущий вход (терминал, вероятно) с файловым дескриптором 4 для дальнейшего использования.

Внутри subshell, вы запускаете свой обычный цикл while read, который меняет стандартный ввод (обработчик файла 0) для чтения из оператора echo.

Но вот хитрость: когда вы вызываете свою функцию, вы говоритеон берет свой стандартный ввод из дескриптора файла 4, а не из текущего дескриптора файла 0. Так как он связан с сохраненным, оригинальным, стандартным вводом, он ничего не получит от стандартного ввода while read (вывод echo).

Это может занять некоторое время, чтобы обернуть вашу голову (и я вспоминаю, как мне пришлось отрывать мозги от пола, когда я впервые это увидел, так как моя голова взорвалась).Но, как только вы поймете, как это работает, вы увидите его элегантность: -)

Вывод для этого:

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
=====

И, основываясь на вашем коде, вероятно,Хорошей отправной точкой является окружение всего лота для сохранения стандартного ввода, например:

(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

Я предполагаю, что ваша функция равна fnUseResolution.

Однако, глядяв этом коде может быть проще использовать другой дескриптор файла для read, а не select, поскольку у вас больше контроля над этим, чем я думал.

Попробуйте:

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

Опция -u 4 для чтения говорит о том, что используется файловый дескриптор 4, а не 0, и я изменил exec, чтобы соответствовать этому.Я не тестировал его, но он должен работать нормально.

Делая это таким образом, вы никогда не меняете стандартный ввод, поэтому он должен быть в пределах функции.Кроме того, в этом случае нет причин сохранять / восстанавливать его в дескрипторе файла 3, поэтому я прокомментировал эти exec вызовы.

0 голосов
/ 21 апреля 2011

Это должно работать;Bash возвращает ответ пользователя на переменную $ REPLY.Это сработает, если вы введете «да» или «нет».

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
...