Làm thế nào để đọc đầu vào của người dùng từ một đường ống?


9

Giả sử tôi có một tệp có tên confirmation.shvới nội dung sau:

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

và tôi muốn chạy tập lệnh này theo cách sau:

cat confirmation.sh | sh

Tôi thấy Are you sure [Y/n]?và kịch bản bị gián đoạn. Có vấn đề gì vậy?


2
Bạn có /bin/bashtrong dòng bang, nhưng bạn sử dụng một .shphần mở rộng và cố gắng chuyển tập lệnh sang sh. Không phải là một vấn đề vì mã bạn có tương thích với cả hai, nhưng đáng để chỉ ra.
Graeme

Câu trả lời:


8

Như những người khác đã nói, đây là bởi vì stdincủa shđã được chuyển đến đọc từ đường ống, nó không phải là kết nối với thiết bị đầu cuối vì nó sẽ bình thường được. Một điều bạn có thể làm để giải quyết vấn đề này là sử dụng /dev/ttyđể buộc tập lệnh đọc từ thiết bị đầu cuối. Ví dụ:

#!/bin/sh

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

Thông thường bạn sẽ chỉ làm điều này nếu bạn đặc biệt muốn ngăn mọi người viết kịch bản đầu vào, ví dụ:

echo Y | sh confirmation.sh

Điều này vẫn sẽ đọc từ thiết bị đầu cuối mặc dù người dùng có thể mong đợi điều này sẽ tự động nhập Ytại dấu nhắc. Nó là phổ biến cho các chương trình mong đợi một mật khẩu để làm điều này.


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

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

lưu ý: cảm ơn @Graeme đã sửa lỗi cho tôi về hai ví dụ trên ...

Nó dễ dàng hơn nhiều để làm nếu bạn giữ stdinrõ ràng.

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

Hoặc, vì 0 1 2 của thiết bị đầu cuối đều là cùng một tệp, chỉ cần thêm:

read line <&2

Và của bạn

cat ./confirmation.sh | sh

Hoạt động tốt.


Tôi không thể làm cho bất kỳ thứ nào trong số này hoạt động ngoài cái cuối cùng.
Graeme

Thật kỳ lạ, tất cả đều làm việc cho tôi ... Tôi đang ở giữa một cái gì đó, nhưng trong ... 10 phút hoặc lâu hơn tôi có thể kiểm tra lại. Trong thời gian chờ đợi ... CÓ THỂ GỬI Ý KIẾN CỦA BẠN ? Tôi không thích phổ biến thông tin sai lệch.
mikeerv

@Graeme - không chắc tại sao tôi bỏ lỡ lần đầu tiên - có thể đã thề rằng tôi đã thử nghiệm tất cả chúng cùng một lúc - nhưng hai lần đầu tiên cần một thay đổi để hoạt động chính xác. Cảm ơn bạn.
mikeerv

Có vẻ tốt, Nó vẫn không đọc từ thiết bị đầu cuối khi tôi lấy nguồn từ stderrmặc dù. Tôi có lẽ nên mặc dù, tôi không hiểu tại sao.
Graeme

@Graeme - lạ - Tôi đã thử nó với dash sh zsh và bash. Tất cả đều hoạt động.
mikeerv

0

Điều này đã làm việc cho một đối số duy nhất được dẫn đến kịch bản của tôi:

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

Sau đó tôi có thể truy cập dữ liệu được dẫn trong một đối số vị trí ( $1), tương thích với các hoạt động tập lệnh khác của tôi.


Từ info test:

[ -p /dev/stdin ]: -p FILElà True nếu FILE tồn tại và là một ống có tên.


-1

Câu trả lời ngắn gọn là bạn không thể. Đường ống chuyển hướng stdout sang stdin, do đó bạn không thể chạy tập lệnh tương tác, vì bạn đã chuyển hướng đầu ra từ lệnh đầu tiên làm đầu vào cho lệnh thứ hai trong câu lệnh pipe.

Bạn có thể đang muốn làm một cái gì đó như thế này:

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

Bạn vẫn có thể chạy một kịch bản tương tác, xem câu trả lời của tôi. Ngoài ra, tại sao không chỉ sh confirmation.sh?
Graeme
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.