Как прочитать пользовательский ввод из канала?

9

Предположим, у меня есть файл confirmation.shсо следующим содержимым:

#!/bin/bash
echo -n "Are you sure [Y/n]? "
read line
case "$line" in
    n|N) echo "smth"
        ;;
    y|Y) echo "smth"
        ;;
esac

и я хочу запустить этот скрипт следующим образом:

cat confirmation.sh | sh

Я вижу, Are you sure [Y/n]?и сценарий прерывается. В чем проблема?

Игорь Тимошенко
источник
2
У вас /bin/bashв строке bang, но вы используете .shрасширение и пытаетесь передать скрипт sh. Не проблема, так как ваш код совместим с обоими, но стоит отметить.
Грэм

Ответы:

8

Как уже говорили другие, это потому , что перенаправление stdinof shбыло прочитано из канала, оно не подключено к терминалу, как обычно. Единственное, что вы можете сделать, чтобы обойти это, - /dev/ttyэто заставить скрипт читать из терминала. Например:

#!/bin/sh

read -p "Are you sure [Y/n]?" line </dev/tty
case "$line" in
  y|Y) echo "confirmed"
    ;;
  *) echo "not confirmed"
    ;;
esac

Обычно вы делаете это только в том случае, если вы специально хотите запретить людям вводить сценарии, например:

echo Y | sh confirmation.sh

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

Graeme
источник
2
sh 3<<CONFIRM /dev/fd/3
    $(cat ./confirmation.sh)
CONFIRM

sh 3<./confirmation.sh /dev/fd/3

примечание: спасибо @Graeme за исправление в двух приведенных выше примерах ...

Это намного легче сделать, если вы будете stdinчестны.

2<./confirmation.sh . /dev/stderr

Или, поскольку 0 1 2 терминала - это один и тот же файл, просто добавьте:

read line <&2

И ваш

cat ./confirmation.sh | sh

Работает просто отлично.

mikeserv
источник
Я не могу заставить что-либо из этого работать отдельно от последнего.
Graeme
Странно, они все работали на меня ... Я нахожусь в середине чего-то, но через ... или около того минут я могу проверить снова. В то же время ... смелые ваш комментарий может быть? Я не люблю распространять дезинформацию.
mikeserv
@Graeme - не уверен, почему я пропустил это в первый раз - мог бы поклясться, что я тестировал их все одновременно - но первые два действительно нуждаются в изменении, чтобы работать правильно. Спасибо.
mikeserv
Выглядит хорошо, он все еще не читает с терминала, хотя я получаю из stderrхотя. Я, наверное, должен, хотя, я не понимаю, почему.
Грэм
@Graeme - странно - я пробовал это с тире ш зш и баш. Все сработало.
mikeserv
0

Это сработало для одного аргумента, передаваемого в мой скрипт:

if [ -p /dev/stdin ]; then set -- "$( cat )"; fi

Затем я могу получить доступ к переданным данным в позиционном аргументе ( $1), который совместим с другими сценариями.


От info test:

[ -p /dev/stdin ]: -p FILETrue, если FILE существует и является именованным каналом.

LinuxSecurityFreak
источник
-1

Короткий ответ: ты не можешь. Канал перенаправляет стандартный вывод на стандартный ввод, поэтому вы не можете запустить интерактивный скрипт, поскольку вы уже перенаправили вывод первой команды в качестве ввода второй команды в операторе канала.

Возможно, вы хотите сделать что-то вроде этого:

cat confirmation.sh > ask.sh && sh ask.sh
Дэвид
источник
Вы все еще можете запустить интерактивный скрипт, смотрите мой ответ. Кроме того, почему бы не просто sh confirmation.sh?
Graeme