Як читати вхід користувача з труби?


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знаходитесь у рядку вибуху, але ви використовуєте .shрозширення та намагаєтесь передати сценарій sh. Не проблема, оскільки наявний у вас код сумісний з обома, але варто вказати.
Graeme

Відповіді:


8

Як вже сказав, це тому , що stdinз 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під час запиту. Це звичайно для програм, які очікують на це пароль.


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

Працює просто чудово.


Я не можу змусити жодного з них працювати від останнього.
Graeme

Дивно, всі вони працювали на мене ... Я в середині чогось, але через ... 10 хвилин, я можу знову перевірити. Тим часом ... СТАРИТИ КОМЕНТАР, можливо? Мені не подобається поширення дезінформації.
mikeserv

@Graeme - не впевнений, чому я пропустив це вперше - міг присягнути, що перевіряв їх все одночасно - але перші два потрібні зміни, щоб правильно працювати. Дякую.
mikeserv

Виглядає добре, він все ще не читається з терміналу, коли я надсилаю джерело stderr. Я, мабуть, повинен, хоча не розумію, чому.
Graeme

@Graeme - дивно - я спробував це з тире ш шш і баш. Всі працювали.
mikeserv

0

Це працювало для одного аргументу, який передається моєму сценарію:

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

Тоді я можу отримати доступ до даних, пов'язаних з конвеєрним файлом, в позиційному аргументі ( $1), який сумісний з іншими моїми сценаріями.


Від info test:

[ -p /dev/stdin ]: -p FILEє правдою, якщо файл FILE існує і називається трубою.


-1

Коротка відповідь - ви не можете. Труба перенаправляє stdout на stdin, тому не можна запускати інтерактивний скрипт, оскільки ви вже перенаправили вихід з першої команди як вхід до другої команди в операторі pipe.

Можливо, ви хочете зробити щось подібне:

cat confirmation.sh > ask.sh && sh ask.sh

Ви все ще можете запустити інтерактивний сценарій, дивіться мою відповідь. Крім того, чому б не просто sh confirmation.sh?
Graeme
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.