วิธีหลอกให้แอปพลิเคชันคิดว่า stdout เป็นเทอร์มินัลไม่ใช่ไปป์


147

ฉันกำลังพยายามทำสิ่งที่ตรงกันข้ามกับ " ตรวจหาว่า stdin เป็นเทอร์มินัลหรือท่อหรือไม่ "

ฉันกำลังเรียกใช้แอปพลิเคชันที่เปลี่ยนรูปแบบผลลัพธ์เนื่องจากตรวจพบไพพ์บน STDOUT และฉันต้องการให้คิดว่าเป็นเทอร์มินัลแบบโต้ตอบเพื่อให้ได้ผลลัพธ์เดียวกันเมื่อเปลี่ยนเส้นทาง

ฉันคิดว่าการห่อไว้ในexpectสคริปต์หรือใช้proc_open()ใน PHP จะทำ แต่ไม่ได้

มีความคิดเห็นอะไรบ้าง?


8
ไม่empty.sf.netความช่วยเหลือ?
ephemient

1
@ephemient: ควรได้รับคำตอบ ใช้ประโยชน์ได้ดีตามทาง ...
ประสาท

คำถามพูดถึง stdout แต่ชื่อกล่าวถึง stdin ฉันคิดว่าชื่อผิด
Piotr Dobrogost

คำตอบ:


177

Aha!

scriptคำสั่งไม่สิ่งที่เราต้องการ ...

script --return --quiet -c "[executable string]" /dev/null

หลอกลวง!

Usage:
 script [options] [file]

Make a typescript of a terminal session.

Options:
 -a, --append                  append the output
 -c, --command <command>       run command rather than interactive shell
 -e, --return                  return exit code of the child process
 -f, --flush                   run flush after each write
     --force                   use output file even when it is a link
 -q, --quiet                   be quiet
 -t[<file>], --timing[=<file>] output timing data to stderr or to FILE
 -h, --help                    display this help
 -V, --version                 display version

1
+1: เพียงสะดุดกับปัญหากับ lib ที่เริ่มต้นคงที่ การเปลี่ยนแปลงล่าสุดใน Fedora 12 ทำให้การเริ่มต้นนั้นล้มเหลวเมื่อ exe lanched ไม่ได้อยู่ในรูปแบบ เคล็ดลับของคุณทำงานได้อย่างสมบูรณ์ ฉันชอบมันมากกว่า unbuffer เนื่องจากสคริปต์ถูกติดตั้งตามค่าเริ่มต้น!
ประสาท

scriptมีให้บริการในBusyBox !
dolmen

9
ถ้าคุณต้องการที่จะแปะมันลงในสิ่งที่มีการโต้ตอบเช่นless -Rที่ที่เทอร์มินัลอินพุทไปless -Rคุณก็ต้องใช้กลอุบายเพิ่มเติม git status | lessตัวอย่างเช่นผมอยากเป็นรุ่นที่มีสีสันของ คุณต้องผ่าน-Rให้น้อยลงเพื่อให้มันเคารพสีและคุณจำเป็นต้องใช้scriptเพื่อให้ได้git statusสีที่ออกมา แต่เราไม่ต้องการscriptให้เป็นเจ้าของคีย์บอร์ดเราต้องการให้มันเป็นlessเช่นนั้น 0<&- script -qfc "git status" /dev/null | less -R ดังนั้นผมจึงใช้วิธีนี้ในขณะนี้และจะทำงานได้ดี: ตัวละครสองสามตัวแรกเหล่านั้นปิด stdin สำหรับคำสั่งนี้
Aaron McDaid

2
หมายเหตุ: วิธีนี้ใช้ไม่ได้ในกรณีที่องค์ประกอบการตรวจสอบการตอบโต้กำลังดู$-ตัวแปรเชลล์สำหรับ "i"
เจย์เทย์เลอร์

1
สิ่งนี้ช่างมหัศจรรย์. ฉันต้องการสิ่งนี้สำหรับกรณีการใช้งานที่หายากมากพร้อมกับห้องสมุด Python ที่ฝังอยู่ภายในไฟล์ปฏิบัติการที่เรียกใช้ภายใน Wine เมื่อฉันวิ่งใน terminal มันใช้งานได้ แต่เมื่อฉันรันไฟล์ .desktop ฉันสร้างมันขึ้นมาก็มักจะผิดพลาดเพราะPy_Initializeไม่เห็น stdin / stderr ที่เหมาะสม
Tatsh

60

จากวิธีการแก้ปัญหาของ Chrisฉันมาพร้อมกับฟังก์ชั่นตัวช่วยเล็ก ๆ น้อย ๆ ดังต่อไปนี้:

faketty() {
    script -qfc "$(printf "%q " "$@")" /dev/null
}

การค้นหาที่เล่นโวหารprintfนั้นเป็นสิ่งที่จำเป็นในการขยายข้อโต้แย้งของสคริปต์อย่างถูกต้องใน$@ขณะที่ปกป้องส่วนที่อาจยกมาของคำสั่ง (ดูตัวอย่างด้านล่าง)

การใช้งาน:

faketty <command> <args>

ตัวอย่าง:

$ python -c "import sys; print sys.stdout.isatty()"
True
$ python -c "import sys; print sys.stdout.isatty()" | cat
False
$ faketty python -c "import sys; print sys.stdout.isatty()" | cat
True

9
คุณอาจต้องการใช้--returnตัวเลือกถ้ามีรุ่นของscriptคุณเพื่อรักษารหัสออก 'กระบวนการเด็ก
jwd

5
ฉันขอแนะนำให้เปลี่ยนฟังก์ชั่นนี้ดังนี้: function faketty { script -qfc "$(printf "%q " "$@")" /dev/null; } มิฉะนั้นไฟล์ที่ชื่อtypescriptจะถูกสร้างขึ้นทุกครั้งที่มีการเรียกใช้คำสั่งในหลายกรณี
w0rp

1
ดูเหมือนจะไม่ทำงานบน MacOS script: illegal option -- f
Alexander Mills

23

unbufferสคริปต์ที่มาพร้อมกับคาดหวังว่า ควรจะจัดการตกลงนี้ หากไม่ใช่แอปพลิเคชันอาจดูอย่างอื่นที่ไม่ใช่สิ่งที่เชื่อมต่อกับเอาต์พุตเช่น ตัวแปรสภาพแวดล้อม TERM ถูกตั้งค่าเป็น


17

ฉันไม่รู้ว่ามันเป็นไปได้จาก PHP หรือไม่ แต่ถ้าคุณต้องการกระบวนการลูกเพื่อดู TTY คุณสามารถสร้างPTYได้

ใน C:

#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <pty.h>

int main(int argc, char **argv) {
    int master;
    struct winsize win = {
        .ws_col = 80, .ws_row = 24,
        .ws_xpixel = 480, .ws_ypixel = 192,
    };
    pid_t child;

    if (argc < 2) {
        printf("Usage: %s cmd [args...]\n", argv[0]);
        exit(EX_USAGE);
    }

    child = forkpty(&master, NULL, NULL, &win);
    if (child == -1) {
        perror("forkpty failed");
        exit(EX_OSERR);
    }
    if (child == 0) {
        execvp(argv[1], argv + 1);
        perror("exec failed");
        exit(EX_OSERR);
    }

    /* now the child is attached to a real pseudo-TTY instead of a pipe,
     * while the parent can use "master" much like a normal pipe */
}

จริง ๆ แล้วฉันอยู่ภายใต้การแสดงผลที่expectสร้างตัวเอง PTY แม้ว่า


คุณรู้วิธีเรียกใช้ nettop เป็นกระบวนการลูกบน mac os x หรือไม่? ฉันต้องการเอาท์พุทของ nettop ในแอพของฉัน ฉันลองใช้ forkpty แต่ก็ไม่สามารถเรียกใช้เน็ตท็อปได้สำเร็จ
วินซ์หยวน

16

การอ้างถึงคำตอบก่อนหน้านี้ใน Mac OS X สามารถใช้ "script" ดังต่อไปนี้ ...

script -q /dev/null commands...

แต่เนื่องจากอาจแทนที่ "\ n" ด้วย "\ r \ n" บน stdout คุณอาจต้องใช้สคริปต์เช่นนี้:

script -q /dev/null commands... | perl -pe 's/\r\n/\n/g'

หากมีไพพ์ระหว่างคำสั่งเหล่านี้คุณต้องล้างข้อมูล stdout ตัวอย่างเช่น:

script -q /dev/null commands... | ruby -ne 'print "....\n";STDOUT.flush' |  perl -pe 's/\r\n/\n/g'

1
ขอบคุณสำหรับไวยากรณ์ OS X แต่ตัดสินโดยคำสั่ง Perl ของคุณดูเหมือนว่าคุณตั้งใจจะบอกว่ามันเปลี่ยนอินสแตนซ์ของ "\ r \ n" เป็น "\ n" ไม่ใช่วิธีอื่นใช่มั้ย
mklement0

8

ใหม่เกินไปที่จะแสดงความคิดเห็นกับคำตอบเฉพาะ แต่ฉันคิดว่าฉันจะติดตามfakettyฟังก์ชั่นที่โพสต์โดย ingomueller-net ข้างต้นเนื่องจากเพิ่งช่วยฉันออก

ฉันพบว่านี่เป็นการสร้างtypescriptไฟล์ที่ฉันไม่ต้องการ / ต้องการฉันจึงเพิ่ม / dev / null เป็นไฟล์เป้าหมายของสคริปต์:

function faketty { script -qfc "$(printf "%q " "$@")" /dev/null ; }


3

การอัปเดตคำตอบของ @ A-Ron ให้กับ a) ทำงานทั้งบน Linux และ MacOs b) เผยแพร่รหัสสถานะทางอ้อม (เนื่องจาก MacO scriptไม่รองรับ)

faketty () {
  # Create a temporary file for storing the status code
  tmp=$(mktemp)

  # Ensure it worked or fail with status 99
  [ "$tmp" ] || return 99

  # Produce a script that runs the command provided to faketty as
  # arguments and stores the status code in the temporary file
  cmd="$(printf '%q ' "$@")"'; echo $? > '$tmp

  # Run the script through /bin/sh with fake tty
  if [ "$(uname)" = "Darwin" ]; then
    # MacOS
    script -Fq /dev/null /bin/sh -c "$cmd"
  else
    script -qfc "/bin/sh -c $(printf "%q " "$cmd")" /dev/null
  fi

  # Ensure that the status code was written to the temporary file or
  # fail with status 99
  [ -s $tmp ] || return 99

  # Collect the status code from the temporary file
  err=$(cat $tmp)

  # Remove the temporary file
  rm -f $tmp

  # Return the status code
  return $err
}

ตัวอย่าง:

$ faketty false ; echo $?
1

$ faketty echo '$HOME' ; echo $?
$HOME
0

embedded_example () {
  faketty perl -e 'sleep(5); print "Hello  world\n"; exit(3);' > LOGFILE 2>&1 </dev/null &
  pid=$!

  # do something else
  echo 0..
  sleep 2
  echo 2..

  echo wait
  wait $pid
  status=$?
  cat LOGFILE
  echo Exit status: $status
}

$ embedded_example
0..
2..
wait
Hello  world
Exit status: 3

2

ฉันพยายามรับสีเมื่อทำงานshellcheck <file> | lessดังนั้นฉันจึงลองคำตอบข้างต้น แต่พวกมันให้เอฟเฟ็กต์ที่แปลกประหลาดซึ่งข้อความจะถูกชดเชยในแนวนอนจากตำแหน่งที่ควรจะเป็น:

In ./all/update.sh line 6:
                          for repo in $(cat repos); do
                                                                  ^-- SC2013: To read lines rather than words, pipe/redirect to a 'while read' loop.

(สำหรับผู้ที่ไม่คุ้นเคยกับ shellcheck บรรทัดที่มีคำเตือนควรจะตรงกับที่เป็นปัญหา)

เพื่อคำตอบข้างต้นเพื่อทำงานกับ shellcheck ฉันลองหนึ่งในตัวเลือกจากความคิดเห็น:

faketty() {                       
    0</dev/null script -qfc "$(printf "%q " "$@")" /dev/null
}

วิธีนี้ใช้ได้ผล ฉันยังเพิ่ม--returnและใช้ตัวเลือกแบบยาวเพื่อทำให้คำสั่งนี้ไม่น่าเชื่อถือน้อยลงเล็กน้อย:

faketty() {                       
    0</dev/null script --quiet --flush --return --command "$(printf "%q " "$@")" /dev/null
}

ทำงานใน Bash และ Zsh


1

นอกจากนี้ยังมีโปรแกรม pty ที่รวมอยู่ในโค้ดตัวอย่างของหนังสือ "Advanced Programming in UNIX Environment, Second Edition"!

ต่อไปนี้เป็นวิธีรวบรวม pty บน Mac OS X:

man 4 pty  #  pty -- pseudo terminal driver

open http://en.wikipedia.org/wiki/Pseudo_terminal

# Advanced Programming in the UNIX Environment, Second Edition
open http://www.apuebook.com

cd ~/Desktop

curl -L -O http://www.apuebook.com/src.tar.gz

tar -xzf src.tar.gz

cd apue.2e

wkdir="${HOME}/Desktop/apue.2e"

sed -E -i "" "s|^WKDIR=.*|WKDIR=${wkdir}|" ~/Desktop/apue.2e/Make.defines.macos

echo '#undef _POSIX_C_SOURCE' >> ~/Desktop/apue.2e/include/apue.h

str='#include   <sys/select.h>'
printf '%s\n' H 1i "$str" . wq | ed -s calld/loop.c

str='
#undef _POSIX_C_SOURCE
#include <sys/types.h>
'
printf '%s\n' H 1i "$str" . wq | ed -s file/devrdev.c

str='
#include <sys/signal.h>
#include <sys/ioctl.h>
'
printf '%s\n' H 1i "$str" . wq | ed -s termios/winch.c

make

~/Desktop/apue.2e/pty/pty ls -ld *

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