Làm thế nào để lừa một ứng dụng nghĩ rằng thiết bị xuất chuẩn của nó là một thiết bị đầu cuối, không phải là một đường ống


147

Tôi đang cố gắng làm điều ngược lại với " Phát hiện nếu stdin là thiết bị đầu cuối hoặc đường ống? ".

Tôi đang chạy một ứng dụng thay đổi định dạng đầu ra bởi vì nó phát hiện một đường ống trên STDOUT và tôi muốn nó nghĩ rằng đó là một thiết bị đầu cuối tương tác để tôi có được đầu ra tương tự khi chuyển hướng.

Tôi đã nghĩ rằng gói nó trong một expectkịch bản hoặc sử dụng một proc_open()PHP sẽ làm điều đó, nhưng nó không.

Có ý tưởng nào ngoài đó không?


8
rỗng.sf.net giúp đỡ?
ephemient

1
@ephemient: nên đã có một câu trả lời. Nhân tiện sử dụng tuyệt vời ...
neuro

Câu hỏi nói về thiết bị xuất chuẩn nhưng tiêu đề đề cập đến stdin. Tôi nghĩ rằng tiêu đề là sai.
Piotr Dobrogost

Câu trả lời:


177

Aha!

Các scriptlệnh làm những gì chúng tôi muốn ...

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

Có lừa không!

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: chỉ vấp phải vấn đề với một lib làm khởi tạo tĩnh. Một sự thay đổi gần đây trong Fedora 12 đã khiến cho init đó thất bại khi exe lanched không ở trong tình trạng khó khăn. Thủ thuật của bạn hoạt động hoàn hảo. Tôi thích nó hơn unbuffer vì script được cài đặt theo mặc định!
thần kinh

scriptthậm chí có sẵn trong BusyBox !
heo

9
Nếu bạn muốn chuyển nó thành một cái gì đó tương tác, như less -R, nơi đầu vào thiết bị đầu cuối đi đến less -R, thì bạn cần thêm một số mánh khóe. Ví dụ, tôi muốn một phiên bản đầy màu sắc của git status | less. Bạn cần chuyển -Rđến ít hơn để nó tôn trọng màu sắc, và bạn cần sử dụng scriptđể có được git statusmàu đầu ra. Nhưng chúng tôi không muốn scriptgiữ quyền sở hữu bàn phím, chúng tôi muốn điều này xảy ra less. Vì vậy, tôi sử dụng nó bây giờ và nó hoạt động tốt : 0<&- script -qfc "git status" /dev/null | less -R . Một vài nhân vật đầu tiên đóng stdin cho một người đi làm này.
Aaron McDaid

2
Lưu ý: Điều này không hoạt động trong trường hợp thành phần kiểm tra tính tương tác đang xem xét $-biến shell cho "i".
Jay Taylor

1
Thật đáng kinh ngạc. Tôi cần điều này cho một trường hợp sử dụng cực kỳ hiếm với thư viện Python được nhúng trong một tệp thực thi được chạy trong Wine. Khi tôi chạy trong một thiết bị đầu cuối, nó hoạt động nhưng khi tôi chạy tệp .desktop tôi đã tạo thì nó sẽ luôn bị sập vì Py_Initializekhông thấy stdin / stderr thích hợp.
Tatsh

60

Dựa trên giải pháp của Chris , tôi đã đưa ra chức năng trợ giúp nhỏ sau đây:

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

Việc tìm kiếm kỳ quặc printflà cần thiết để mở rộng chính xác các đối số của tập lệnh trong $@khi bảo vệ các phần có thể được trích dẫn của lệnh (xem ví dụ bên dưới).

Sử dụng:

faketty <command> <args>

Thí dụ:

$ 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
Bạn có thể muốn sử dụng --returntùy chọn, nếu phiên bản của bạn scriptcó nó, để giữ mã thoát của quy trình con.
JWD

5
Tôi khuyên bạn nên thay đổi chức năng này như vậy: function faketty { script -qfc "$(printf "%q " "$@")" /dev/null; } Nếu không, một tệp có tên typescriptsẽ được tạo mỗi khi lệnh được chạy, trong nhiều trường hợp.
w0rp ngày

1
Tôi dường như không hoạt động trên MacOS tho, tôi nhận được script: illegal option -- f
Alexander Mills

23

Các unbuffer kịch bản mà đi kèm với mong đợi nên xử lý ok này. Nếu không, ứng dụng có thể đang xem xét một cái gì đó khác với những gì đầu ra của nó được kết nối với, ví dụ. biến môi trường TERM được đặt là gì.


17

Tôi không biết nếu nó có thể thực hiện được từ PHP, nhưng nếu bạn thực sự cần quy trình con để xem TTY, bạn có thể tạo PTY .

Trong 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 */
}

Tôi thực sự đã có ấn tượng rằng expectchính nó tạo ra một PTY, mặc dù.


Bạn có biết cách chạy nettop như tiến trình con trên mac os x không? Tôi muốn nhận đầu ra của nettop trong ứng dụng của mình. Tôi đã thử sử dụng forkpty nhưng vẫn không thể chạy nettop thành công.
Vince Yuan

16

Tham khảo câu trả lời trước đó, trên Mac OS X, "tập lệnh" có thể được sử dụng như bên dưới ...

script -q /dev/null commands...

Nhưng, vì nó có thể thay thế "\ n" bằng "\ r \ n" trên thiết bị xuất chuẩn, bạn cũng có thể cần tập lệnh như thế này:

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

Nếu có một số đường ống giữa các lệnh này, bạn cần phải xóa thiết bị xuất chuẩn. ví dụ:

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

1
Cảm ơn cú pháp OS X, nhưng, đánh giá bằng câu lệnh Perl của bạn, có vẻ như bạn muốn nói rằng nó thay đổi các trường hợp của "\ r \ n" thành "\ n", không phải theo cách khác, đúng không?
mkuity0

8

Quá mới để nhận xét về câu trả lời cụ thể, nhưng tôi nghĩ rằng tôi sẽ theo dõi fakettychức năng được đăng bởi ingomueller-net ở trên vì nó gần đây đã giúp tôi hiểu.

Tôi thấy rằng điều này đang tạo một typescripttệp mà tôi không muốn / cần vì vậy tôi đã thêm / dev / null làm tệp đích của tập lệnh:

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


3

Cập nhật câu trả lời của @ A-Ron cho a) hoạt động trên cả Linux & MacOs b) truyền mã trạng thái một cách gián tiếp (vì MacO scriptkhông hỗ trợ nó)

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
}

Ví dụ:

$ 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

Tôi đã cố gắng để có được màu sắc khi chạy shellcheck <file> | less, vì vậy tôi đã thử các câu trả lời ở trên, nhưng chúng tạo ra hiệu ứng kỳ quái này trong đó văn bản được bù theo chiều ngang từ nơi cần có:

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.

(Đối với những người không quen thuộc với shellcheck, dòng cảnh báo được cho là phù hợp với vị trí của sự cố.)

Để các câu trả lời ở trên hoạt động với shellcheck, tôi đã thử một trong các tùy chọn từ các nhận xét:

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

Những công việc này. Tôi cũng đã thêm --returnvà sử dụng các tùy chọn dài, để làm cho lệnh này bớt khó hiểu hơn một chút:

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

Hoạt động trong Bash và Zsh.


1

Ngoài ra còn có một chương trình pty bao gồm trong mã mẫu của cuốn sách "Lập trình nâng cao trong môi trường UNIX, Ấn bản thứ hai"!

Dưới đây là cách biên dịch pty trên 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 *

Lỗi thực sự kỳ lạ, quá: Lỗi nhanh: tên miền không xác định: scriptsnippets.joyent.com. Vui lòng kiểm tra xem tên miền này đã được thêm vào một dịch vụ.
i336_
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.