2010-09-10 29 views
22

Tôi muốn đọc một ký tự một lần từ dòng lệnh trong PHP, tuy nhiên có vẻ như có một số loại đệm đầu vào từ một nơi nào đó ngăn chặn điều này.PHP CLI: Cách đọc một ký tự đầu vào duy nhất từ ​​TTY (mà không phải đợi khóa nhập)?

xem xét mã này:

#!/usr/bin/php 
<?php 
echo "input# "; 
while ($c = fread(STDIN, 1)) { 
    echo "Read from STDIN: " . $c . "\ninput# "; 
} 
?> 

Typing trong "foo" như đầu vào (và nhấn Enter), sản lượng Tôi nhận được là:

input# foo 
Read from STDIN: f 
input# Read from STDIN: o 
input# Read from STDIN: o 
input# Read from STDIN: 

input# 

Sản lượng Tôi mong là:

input# f 
input# Read from STDIN: f 

input# o 
input# Read from STDIN: o 

input# o 
input# Read from STDIN: o 

input# 
input# Read from STDIN: 

input# 

(Tức là, với các ký tự đang được kiểm tra lại d và xử lý khi chúng được gõ).

Tuy nhiên, hiện tại, mỗi ký tự chỉ được đọc sau khi nhấn enter. Tôi có một nghi ngờ TTY là đệm đầu vào.

Cuối cùng tôi muốn có thể đọc các phím bấm như mũi tên LÊN, mũi tên XUỐNG, v.v.

Cảm ơn bạn trước!

Trả lời

30

Giải pháp cho tôi là đặt chế độ -icanon trên TTY (sử dụng stty). Eg .:

stty -icanon 

Vì vậy, các mã mà hiện đang làm việc là:

#!/usr/bin/php 
<?php 
system("stty -icanon"); 
echo "input# "; 
while ($c = fread(STDIN, 1)) { 
    echo "Read from STDIN: " . $c . "\ninput# "; 
} 
?> 

Output:

input# fRead from STDIN: f 
input# oRead from STDIN: o 
input# oRead from STDIN: o 
input# 
Read from STDIN: 

input# 

Props để câu trả lời đưa ra ở đây:
Is there a way to wait for and get a key press from a (remote) terminal session?

Để biết thêm thông tin, xem:
http://www.faqs.org/docs/Linux-HOWTO/Serial-Programming-HOWTO.html#AEN92

Đừng quên để khôi phục lại TTY khi bạn đang thực hiện với nó ...

Khôi phục cấu hình tty

Reset nhà ga trở lại con đường đó là có thể được thực hiện bằng cách lưu trạng thái tty trước khi bạn thực hiện thay đổi cho nó. Sau đó, bạn có thể khôi phục trạng thái đó khi hoàn tất.

Ví dụ:

<?php 

// Save existing tty configuration 
$term = `stty -g`; 

// Make lots of drastic changes to the tty 
system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef"); 

// Reset the tty back to the original configuration 
system("stty '" . $term . "'"); 

?> 

Đây là cách duy nhất để giữ gìn tty và đặt nó trở lại cách người dùng đã có nó trước khi bạn bắt đầu.

Lưu ý rằng nếu bạn không phải lo lắng về việc bảo tồn tình trạng ban đầu, bạn có thể đặt nó trở lại mặc định cấu hình "lành mạnh" chỉ đơn giản bằng cách thực hiện:

<?php 

// Make lots of drastic changes to the tty 
system("stty raw opost -ocrnl onlcr -onocr -onlret icrnl -inlcr -echo isig intr undef"); 

// Reset the tty back to sane defaults 
system("stty sane"); 

?> 
+0

"Đừng quên đặt lại TTY khi bạn đã làm xong!" - làm thế nào để chúng tôi thiết lập lại nó? – mpen

+3

Đánh dấu: Tại cơ bản nhất bạn có thể làm 'stty sane'. Tuy nhiên, để đảm bảo bạn đang đặt lại tty về trạng thái chính xác, trước đây bạn nên lưu trạng thái của nó trước. Tôi đã mở rộng câu trả lời để bao gồm các ví dụ về cách thực hiện điều đó. – dansimau

+0

đẹp! cảm ơn bạn vì điều này :) – mpen

20

Dưới đây là một cách mà làm việc cho tôi với chức năng đọc và dòng, mà không cần phải gây rối với các công cụ tty.

readline_callback_handler_install('', function() { }); 
while (true) { 
    $r = array(STDIN); 
    $w = NULL; 
    $e = NULL; 
    $n = stream_select($r, $w, $e, null); 
    if ($n && in_array(STDIN, $r)) { 
    $c = stream_get_contents(STDIN, 1); 
    echo "Char read: $c\n"; 
    break; 
    } 
} 

Đã thử nghiệm với PHP 5.5.8 trên OSX.

+4

Đây phải là câu trả lời được chấp nhận. – h0tw1r3

+1

readline cần được biên dịch thành PHP. Kiểm tra cấu hình của bạn cho '' '--with-readline =/opt/local''' bằng cách sử dụng lệnh này:' '' php -i | grep readline''' – arikin

+1

Sử dụng (cập nhật gần đây) Arch Linux và PHP 7.0.5. Các 'break' gây ra điều này để đọc một char sau đó gian hàng; loại bỏ nó khiến mọi thứ trở nên tuyệt vời. Tôi cũng đã thay đổi '0' ở cuối' select' thành 'NULL', và bây giờ PHP không sử dụng CPU 100% (!). * Chức năng này có vẻ hoạt động hoàn hảo trên máy của tôi (bắt '\ 033' và mọi thứ) dựa trên thử nghiệm ban đầu. * –

2

Hàm bên dưới là phiên bản đơn giản của câu trả lời @ seb có thể được sử dụng để chụp một ký tự đơn. Nó không yêu cầu stream_select và sử dụng chặn của cố định readline_callback_handler_install thay vì tạo vòng lặp while. Nó cũng loại bỏ trình xử lý để cho phép đầu vào tiếp tục như bình thường (chẳng hạn như readline).

function readchar($prompt) 
{ 
    readline_callback_handler_install($prompt, function() {}); 
    $char = stream_get_contents(STDIN, 1); 
    readline_callback_handler_remove(); 
    return $char; 
} 

// example: 
if (!in_array(
    readchar('Continue? [Y/n] '), ["\n", 'y', 'Y'] 
    // enter/return key ("\n") for default 'Y' 
)) die("Good Bye\n"); 
$name = readline("Name: "); 
echo "Hello {$name}.\n"; 
-1
<?php 
`stty -icanon`; 
// this will do it 
stream_set_blocking(STDIN, 0); 
echo "Press 'Q' to quit\n"; 
while(1){ 
    if (ord(fgetc(STDIN)) == 113) { 
     echo "QUIT detected..."; 
     break; 
    } 
    echo "we are waiting for something..."; 
} 
+0

Dường như nó không hoạt động. Theo hướng dẫn 'stream_set_blocking' nói' Chức năng này hoạt động đối với bất kỳ luồng nào hỗ trợ chế độ không chặn (hiện tại, các tệp thông thường và luồng lưu trữ) .' Tôi e rằng STDIN không thuộc về đó. – DrLightman

+0

bạn cần phải thiết lập stty -icanon trong thiết bị đầu cuối và nó hoạt động tốt .. – joeldg

+1

Cảm ơn bạn cho đoạn mã này, mà có thể cung cấp một số giới hạn, giúp đỡ ngay lập tức. Một [giải thích đúng] (https://meta.stackexchange.com/q/114762) sẽ cải thiện đáng kể giá trị lâu dài của nó bằng cách cho thấy lý do tại sao đây là giải pháp tốt cho vấn đề và sẽ làm cho nó hữu ích hơn cho người đọc trong tương lai với người khác , các câu hỏi tương tự. Vui lòng [edit] (https://meta.stackoverflow.com/posts/360251/edit) câu trả lời của bạn để thêm một số giải thích, bao gồm các giả định bạn đã thực hiện. [ref] (https://meta.stackoverflow.com/a/360251/8371915) – user8371915

Các vấn đề liên quan