¿Cómo leer la entrada del usuario desde una tubería?

9

Supongamos que tengo un archivo confirmation.shcon el siguiente contenido:

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

y quiero ejecutar este script de la siguiente manera:

cat confirmation.sh | sh

Ya veo Are you sure [Y/n]?y el guión se interrumpe. ¿Cuál es el problema?

Igor Timoshenko
fuente
2
Usted está /bin/bashen la línea de explosión, pero utiliza una .shextensión e intenta canalizar el script sh. No es un problema ya que el código que tienes es compatible con ambos, pero vale la pena señalarlo.
Graeme

Respuestas:

8

Como otros han dicho, esto se debe a que el stdinde shha sido redirigido para leer desde la tubería, no está conectado al terminal como lo estaría normalmente. Una cosa que puede hacer para evitar esto es usar /dev/ttypara forzar la lectura del script desde la terminal. P.ej:

#!/bin/sh

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

Normalmente solo haría esto si específicamente desea evitar que las personas escriban la entrada, por ejemplo:

echo Y | sh confirmation.sh

Esto aún se leería desde el terminal a pesar de que el usuario podría esperar que esto ingrese automáticamente Yen el indicador. Es común para los programas que esperan una contraseña para hacer esto.

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

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

nota: gracias a @Graeme por corregirme en los dos ejemplos anteriores ...

Es mucho más fácil de hacer si te mantienes stdinalejado.

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

O, dado que los 0 1 2 de un terminal son todos el mismo archivo, simplemente agregue:

read line <&2

Y tu

cat ./confirmation.sh | sh

Funciona bien

mikeserv
fuente
No puedo hacer que ninguno de estos funcione aparte del último.
Graeme
Extraño, todos trabajaron para mí ... Estoy en medio de algo, pero en ... 10 minutos más o menos puedo volver a probar. Mientras tanto ... ¿ NEGA SU COMENTARIO tal vez? No me gusta difundir información errónea.
mikeserv
@Graeme, no estoy seguro de por qué lo perdí la primera vez, podría haber jurado que los probé todos al mismo tiempo, pero los dos primeros necesitan un cambio para funcionar correctamente. Gracias.
mikeserv
Se ve bien, todavía no se lee desde el terminal cuando procedo de stderr. Aunque probablemente debería, no entiendo por qué.
Graeme
@Graeme - raro - Lo probé con dash sh zsh y bash. Todo funcionó.
mikeserv
0

Esto ha funcionado para un solo argumento que se canaliza a mi script:

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

Entonces puedo acceder a los datos canalizados en un argumento posicional ( $1), que es compatible con mis otros trabajos de script.


De info test:

[ -p /dev/stdin ]: -p FILEes True si existe ARCHIVO y es una tubería con nombre.

LinuxSecurityFreak
fuente
-1

La respuesta corta es que no puedes. La tubería redirige stdout a stdin, por lo que no puede ejecutar una secuencia de comandos interactiva, ya que ya ha redirigido la salida del primer comando como la entrada al segundo comando en la instrucción de tubería.

Quizás estés buscando hacer algo como esto:

cat confirmation.sh > ask.sh && sh ask.sh
David
fuente
Todavía puede ejecutar un script interactivo, vea mi respuesta. Además, ¿por qué no solo sh confirmation.sh?
Graeme