โปรแกรมรู้ได้อย่างไรว่า stdout เชื่อมต่อกับเทอร์มินัลหรือไปป์?


12

ฉันมีปัญหาในการดีบั๊กโปรแกรม segfaulting เพราะ ouput ที่ถูกต้องก่อน segfault คือสิ่งที่ฉันต้องการ แต่สิ่งนี้จะหายไปหากฉันกำลังไพพ์เอาต์พุตไปยังไฟล์ ตามคำตอบนี้: /unix//a/17339/22615นี่เป็นเพราะบัฟเฟอร์เอาต์พุตของโปรแกรมจะล้างออกทันทีเมื่อเชื่อมต่อกับเทอร์มินัล แต่จะอยู่ที่จุดเฉพาะเมื่อเชื่อมต่อกับไพพ์ คำถามสองสามข้อที่นี่:

  • โปรแกรมกำหนดว่า stdout เชื่อมต่อกับอะไรได้อย่างไร

  • คำสั่ง "สคริปต์" สร้างพฤติกรรมเช่นเดียวกับเมื่อโปรแกรมเขียนไปยังเทอร์มินัลอย่างไร

  • สามารถทำได้โดยไม่ใช้คำสั่งสคริปต์หรือไม่


คำถามที่เกี่ยวข้องคือunix.stackexchange.com/q/513926/5132
JdeBP

คำตอบ:


23

บอกว่าตัวบอกไฟล์ชี้ไปที่อุปกรณ์ปลายทาง

โปรแกรมสามารถบอกได้ว่า file descriptor เชื่อมโยงกับอุปกรณ์ tty หรือไม่โดยใช้isatty()ฟังก์ชั่น C มาตรฐาน (ซึ่งโดยทั่วไปแล้วภายใต้การioctl()เรียกระบบtty เฉพาะที่ไม่มีอันตรายซึ่งจะกลับมาพร้อมกับข้อผิดพลาดเมื่อ fd ไม่ได้ชี้ไปที่อุปกรณ์ tty) .

[/ testยูทิลิตี้สามารถทำมันได้กับ-tผู้ประกอบการ

if [ -t 1 ]; then
  echo stdout is open to a terminal
fi

การติดตามการเรียกใช้ฟังก์ชัน libc บนระบบ GNU / Linux:

$ ltrace [ -t 1 ] | cat
[...]
isatty(1)                                      = 0
[...]

การติดตามการโทรของระบบ:

$ strace [ -t 1 ] | cat
[...]
ioctl(1, TCGETS, 0x7fffd9fb3010)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]

บอกถ้ามันชี้ไปที่ท่อ

ในการพิจารณาว่า fd นั้นเชื่อมโยงกับไพพ์ / ฟีฟ่าเราสามารถใช้การfstat()เรียกของระบบซึ่งจะส่งกลับโครงสร้างของst_modeฟิลด์ที่มีประเภทและสิทธิ์ของไฟล์ที่เปิดใน fd นั้น S_ISFIFO()มาตรฐาน C แมโครที่สามารถใช้กับที่st_modeสนามเพื่อตรวจสอบว่า FD เป็นท่อ / FIFO

ไม่มียูทิลิตี้มาตรฐานที่สามารถทำได้fstat()แต่มีการใช้งานที่ไม่สอดคล้องกันหลายประการของstatคำสั่งที่สามารถทำได้ zsh's statในตัวด้วยstat -sf "$fd" +modeซึ่งผลตอบแทนโหมดเป็นตัวแทนสตริงที่มีตัวละครที่แสดงให้เห็นถึงชนิด (ครั้งแรกpสำหรับท่อ) GNU statสามารถทำเช่นเดียวกันกับstat -c %A - <&"$fd"ได้ แต่stat -c %F - <&"$fd"ต้องรายงานประเภทเดียว ด้วย BSD stat: หรือstat -f %St <&"$fd"stat -f %HT <&"$fd"

บอกถ้ามันหาได้

แอ็พพลิเคชันโดยทั่วไปไม่สนใจว่า stdout เป็นไพพ์แม้ว่า พวกเขาอาจสนใจว่ามันหาได้ (แม้ว่าโดยทั่วไปแล้วจะไม่ตัดสินใจว่าจะบัฟเฟอร์หรือไม่)

เพื่อทดสอบว่า fd สามารถค้นหาได้ (ท่อ, ซ็อกเก็ต, อุปกรณ์ tty ไม่สามารถค้นหาได้, ไฟล์ปกติและอุปกรณ์บล็อกส่วนใหญ่โดยทั่วไป), หนึ่งสามารถลองการlseek()เรียกระบบสัมพัทธ์กับออฟเซต 0 (ไร้เดียงสา) ddเป็นยูทิลิตี้มาตรฐานที่เป็นอินเตอร์เฟสlseek()แต่ไม่สามารถใช้สำหรับการทดสอบนั้นได้เนื่องจากการใช้งานจะไม่โทรlseek()เลยหากคุณขอออฟเซ็ตเป็น 0

zshและksh93เปลือกหอยมีในตัวที่กำลังมองหาผู้ประกอบการว่า:

$ strace -e lseek ksh -c ': 1>#((CUR))' | cat
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ksh: 1: not seekable
$ strace -e lseek zsh -c 'zmodload zsh/system; sysseek -w current -u 1 0 || syserror'
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
Illegal seek

ปิดการใช้งานการบัฟเฟอร์

scriptคำสั่งใช้คู่หลอกขั้วที่จะจับเอาท์พุทของโปรแกรมเพื่อ stdout โปรแกรม (และ stdin และ stderr) จะเป็นอุปกรณ์หลอกขั้ว

เมื่อ stdout ไปยังอุปกรณ์เทอร์มินัลโดยทั่วไปยังคงมีการบัฟเฟอร์บางส่วน แต่มันจะขึ้นอยู่กับสาย printf/ putsและ co จะไม่เขียนอะไรจนกว่าตัวอักขระบรรทัดใหม่จะถูกส่งออก สำหรับไฟล์ประเภทอื่น ๆ การบัฟเฟอร์จะแบ่งตามบล็อก (จากไม่กี่กิโลไบต์)

มีหลายตัวเลือกในการปิดการใช้งานบัฟเฟอร์ซึ่งถูกกล่าวถึงในจำนวนของคำถาม & ที่นี่ (ค้นหาunbufferหรือstdbuf , ไม่สามารถเปลี่ยนเส้นทางการส่งออกตัดให้วิธีการไม่กี่วิธี) โดยใช้เทอร์มินัลเทียมหลอกsocat/ script/ expect/ unbuffer(เป็นexpectสคริปต์) / zsh's zptyหรือโดยการฉีดรหัสในการปฏิบัติการเพื่อปิดการใช้งานบัฟเฟอร์เป็นกระทำโดย GNU หรือของ stdbufFreeBSD


1
คำตอบที่ยอดเยี่ยมขอบคุณมากสำหรับสิ่งนี้!
mowwwalker

อีกวิธีหนึ่งที่เฉพาะเจาะจงของ Linux คือการสำรวจ/procไดเรกทอรีและแต่ละ/proc/<integer>/ไดเรกทอรีค้นหา/proc/<integer>/fd/และค้นหาไฟล์ descriptor ที่มีหมายเลขไอโหนดเดียวกันในpipefs serverfault.com/q/48330/363611 อย่างไรก็ตามนั่นเป็นประโยชน์เฉพาะในสคริปต์เมื่อไม่สามารถใช้ syscalls ที่อธิบายไว้ ในคำตอบของ Stephane และเป็นวิธีแก้ปัญหามากกว่าวิธีแก้ไขปัญหาที่เหมาะสม IMHO
Sergiy Kolodyazhnyy

บน BSD lseekจะประสบความสำเร็จในเทอร์มินัลและอุปกรณ์ตัวละครอื่น ๆ และเพียงแค่ / ตั้งค่าตัวนับที่เพิ่มขึ้นในการอ่านแต่ละครั้งที่ประสบความสำเร็จ () ฉันไม่รู้ว่าสิ่งนี้ทำให้พวกเขา "ค้นหา"
mosvy

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