ตรวจสอบว่า stdin เป็นขั้วหรือท่อ?


118

เมื่อฉันรัน " python" จากเทอร์มินัลโดยไม่มีข้อโต้แย้งมันจะแสดงเชลล์โต้ตอบของ Python

เมื่อฉันรัน " cat | python" จากเทอร์มินัลมันจะไม่เปิดโหมดโต้ตอบ อย่างไรก็ตามโดยไม่ได้รับอินพุตใด ๆ ก็ตรวจพบว่าเชื่อมต่อกับท่อ

ฉันจะตรวจจับที่คล้ายกันใน C หรือ C ++ หรือ Qt ได้อย่างไร


7
สิ่งที่คุณต้องการคืออย่าตรวจสอบว่า stdin เป็นไปป์หรือไม่ แต่ถ้า stdin / stdout เป็นเทอร์มินัล
Juliano

คำตอบ:


137

ใช้isatty:

#include <stdio.h>
#include <io.h>
...    
if (isatty(fileno(stdin)))
    printf( "stdin is a terminal\n" );
else
    printf( "stdin is a file or a pipe\n");

(บน windows จะขึ้นต้นด้วยเครื่องหมายขีดล่าง: _isatty, _fileno)


13
+1: stdin อาจเป็นไปป์หรือเปลี่ยนเส้นทางจากไฟล์ ดีกว่าที่จะตรวจสอบว่าเป็นแบบโต้ตอบมากกว่าที่จะตรวจสอบว่ามันเป็นไม่ได้
John Kugelman

51
เมื่อวันที่ POSIX ไม่มีio.hและคุณต้องรวมisatty() unistd.h
maxschlepzig

คำถามติดตามผล: จะอ่านเนื้อหาไปป์อย่างไรในกรณีที่ stdin ไม่ใช่ tty stackoverflow.com/q/16305971/96656
Mathias Bynens

หมายเหตุ: คุณจำเป็นต้องตรวจสอบ stdout (STDOUT_FILENO) ถ้าคุณต้องการที่จะดูว่า -output- ของคุณเป็น TTY lessหรือไม่ในกรณีที่คุณต้องการที่จะส่งออกปราบถ้าประปาเข้า
Coroos

71

สรุป

สำหรับกรณีการใช้งานจำนวนมากฟังก์ชันPOSIXisatty()คือสิ่งที่จำเป็นในการตรวจสอบว่า stdin เชื่อมต่อกับเทอร์มินัลหรือไม่ ตัวอย่างเล็กน้อย:

#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  if (isatty(fileno(stdin)))
    puts("stdin is connected to a terminal");
  else
    puts("stdin is NOT connected to a terminal");
  return 0;
}

ส่วนต่อไปนี้จะเปรียบเทียบวิธีการต่างๆที่สามารถใช้ได้หากต้องทดสอบระดับการโต้ตอบที่แตกต่างกัน

วิธีการในรายละเอียด

มีหลายวิธีในการตรวจสอบว่าโปรแกรมกำลังทำงานแบบโต้ตอบหรือไม่ ตารางต่อไปนี้แสดงภาพรวม:

cmd \ method ctermid open isatty fstat
-------------------------------------------------- ----------
./test / dev / tty ตกลงใช่ S_ISCHR
./test ≺ test.cc / dev / tty ตกลง NO S_ISREG
cat test.cc | ./test / dev / tty ตกลงไม่มี S_ISFIFO
เสียงสะท้อน. / ทดสอบ | ในขณะนี้ / dev / tty FAIL NO S_ISREG

ผลลัพธ์มาจากระบบ Ubuntu Linux 11.04 โดยใช้โปรแกรมต่อไปนี้:

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int main() {
  char tty[L_ctermid+1] = {0};
  ctermid(tty);
  cout << "ID: " << tty << '\n';
  int fd = ::open(tty, O_RDONLY);
  if (fd < 0) perror("Could not open terminal");
  else {
    cout << "Opened terminal\n";
    struct termios term;
    int r = tcgetattr(fd, &term);
    if (r < 0) perror("Could not get attributes");
    else cout << "Got attributes\n";
  }
  if (isatty(fileno(stdin))) cout << "Is a terminal\n";
  else cout << "Is not a terminal\n";
  struct stat stats;
  int r = fstat(fileno(stdin), &stats);
  if (r < 0) perror("fstat failed");
  else {
    if (S_ISCHR(stats.st_mode)) cout << "S_ISCHR\n";
    else if (S_ISFIFO(stats.st_mode)) cout << "S_ISFIFO\n";
    else if (S_ISREG(stats.st_mode)) cout << "S_ISREG\n";
    else cout << "unknown stat mode\n";
  }
  return 0;
}

อุปกรณ์ปลายทาง

ถ้าเซสชั่นแบบโต้ตอบความต้องการความสามารถบางอย่างคุณสามารถเปิดอุปกรณ์ปลายทางและ (ชั่วคราว) tcsetattr()กำหนดแอตทริบิวต์ขั้วที่คุณต้องผ่าน

ตัวอย่าง Python

รหัสงูใหญ่ที่ตัดสินใจไม่ว่าจะเป็นล่ามทำงานโต้ตอบisatty()การใช้งาน ฟังก์ชั่นPyRun_AnyFileExFlags()

/* Parse input from a file and execute it */

int
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
                     PyCompilerFlags *flags)
{
    if (filename == NULL)
        filename = "???";
    if (Py_FdIsInteractive(fp, filename)) {
        int err = PyRun_InteractiveLoopFlags(fp, filename, flags);

โทร Py_FdIsInteractive()

/*
 * The file descriptor fd is considered ``interactive'' if either
 *   a) isatty(fd) is TRUE, or
 *   b) the -i flag was given, and the filename associated with
 *      the descriptor is NULL or "<stdin>" or "???".
 */
int
Py_FdIsInteractive(FILE *fp, const char *filename)
{
    if (isatty((int)fileno(fp)))
        return 1;

ที่โทรisatty().

ข้อสรุป

มีระดับการโต้ตอบที่แตกต่างกัน สำหรับการตรวจสอบว่าstdinเชื่อมต่อกับไพพ์ / ไฟล์หรือเทอร์มินัลจริงisatty()เป็นวิธีการที่เป็นธรรมชาติในการทำเช่นนั้น


6

อาจเป็นไปได้ว่าพวกเขากำลังตรวจสอบประเภทของไฟล์ที่ "stdin" อยู่กับ fstat สิ่งนี้:

struct stat stats;
fstat(0, &stats);
if (S_ISCHR(stats.st_mode)) {
    // Looks like a tty, so we're in interactive mode.
} else if (S_ISFIFO(stats.st_mode)) {
    // Looks like a pipe, so we're in non-interactive mode.
}

แน่นอนว่า Python เป็นโอเพ่นซอร์สดังนั้นคุณสามารถดูสิ่งที่พวกเขาทำและรู้ได้อย่างแน่นอน:

http://www.python.org/ftp/python/2.6.2/Python-2.6.2.tar.bz2


4

บน Windows คุณสามารถใช้ GetFileType

HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
DWORD type = GetFileType(hIn);
switch (type) {
case FILE_TYPE_CHAR: 
    // it's from a character device, almost certainly the console
case FILE_TYPE_DISK:
    // redirected from a file
case FILE_TYPE_PIPE:
    // piped from another program, a la "echo hello | myprog"
case FILE_TYPE_UNKNOWN:
    // this shouldn't be happening...
}


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.