Como ler a entrada do usuário de um tubo?

9

Vamos supor que eu tenha um arquivo nomeado confirmation.shcom o seguinte conteúdo:

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

e quero executar esse script da seguinte maneira:

cat confirmation.sh | sh

Eu vejo Are you sure [Y/n]?e o script é interrompido. Qual é o problema?

Igor Timoshenko
fonte
2
Você está /bin/bashna linha de chegada, mas usa uma .shextensão e tenta canalizar o script para sh. Não é um problema, pois o código que você possui é compatível com ambos, mas vale a pena destacar.
Graeme

Respostas:

8

Como já foi dito, isso é porque o stdinde shfoi redirecionada para ler a partir do tubo, ele não está conectado ao terminal, uma vez que, normalmente, seria. Uma coisa que você pode fazer para contornar isso é usar /dev/ttypara forçar a leitura do script no terminal. Por exemplo:

#!/bin/sh

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

Normalmente, você só faria isso se quiser especificamente impedir que as pessoas escrevam a entrada de scripts, por exemplo:

echo Y | sh confirmation.sh

Isso ainda será lido no terminal, mesmo que o usuário possa esperar que isso seja inserido automaticamente Yno prompt. É comum para programas que esperam que uma senha faça isso.

Graeme
fonte
2
sh 3<<CONFIRM /dev/fd/3
    $(cat ./confirmation.sh)
CONFIRM

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

nota: obrigado a @Graeme por me corrigir nos dois exemplos acima ...

É muito mais fácil fazer isso se você ficar stdinlonge.

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

Ou, como 0 1 2 do terminal são todos do mesmo arquivo, basta adicionar:

read line <&2

E seu

cat ./confirmation.sh | sh

Funciona muito bem.

mikeserv
fonte
Não consigo fazer com que nenhum deles funcione além do último.
Graeme
Estranho, todos eles funcionaram para mim ... Estou no meio de alguma coisa, mas em ... 10 minutos ou mais, posso testar novamente. Enquanto isso ... CULPA SEU COMENTÁRIO, talvez? Não gosto de divulgar informações erradas.
mikeserv
@ Graeme - não sei por que eu perdi a primeira vez - poderia jurar que testei todos eles ao mesmo tempo - mas os dois primeiros precisam de uma alteração para funcionar corretamente. Obrigado.
mikeserv
Parece bom, ele ainda não lê a partir do terminal quando eu fonte de stderrembora. Eu provavelmente deveria, porém, não entendo o porquê.
Graeme
@ Graeme - estranho - eu tentei com dash sh zsh e bash. Tudo funcionou.
mikeserv
0

Isso funcionou para um único argumento sendo canalizado para o meu script:

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

Posso acessar os dados canalizados em um argumento posicional ( $1), que é compatível com meus outros trabalhos de script.


De info test:

[ -p /dev/stdin ]: -p FILEé True se FILE existe e é um pipe nomeado.

LinuxSecurityFreak
fonte
-1

A resposta curta é que você não pode. O pipe redireciona stdout para stdin, portanto, você não pode executar um script interativo, pois você já redirecionou a saída do primeiro comando como entrada para o segundo comando na instrução de pipe.

Você pode estar procurando fazer algo assim:

cat confirmation.sh > ask.sh && sh ask.sh
David
fonte
Você ainda pode executar um script interativo, veja minha resposta. Além disso, por que não apenas sh confirmation.sh?
Graeme