stdout이 파이프가 아닌 터미널이라고 생각하도록 응용 프로그램을 속이는 방법


147

" stdin이 터미널인지 파이프인지 감지합니까? " 의 반대를 수행하려고합니다 .

STDOUT에서 파이프를 감지하기 때문에 출력 형식을 변경하는 응용 프로그램을 실행 중이며 리디렉션 할 때 동일한 출력을 얻도록 대화 형 터미널이라고 생각하고 싶습니다.

expect스크립트로 감싸 거나 proc_open()PHP에서 사용하면 그렇게 할 것이라고 생각했지만 그렇지 않습니다.

어떤 아이디어가 있습니까?


8
합니까 empty.sf.net의 도움을?
ephemient

1
@ephemient : 대답해야합니다. 그건 그렇고 훌륭한 유틸리티
신경

질문은 stdout에 대해 이야기하지만 제목은 stdin을 언급합니다. 제목이 잘못되었다고 생각합니다.
Piotr Dobrogost

답변:


177

아하!

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가 tty에 있지 않을 때 초기화가 실패했습니다. 당신의 트릭은 완벽하게 작동합니다. 스크립트가 기본적으로 설치되어 있기 때문에 unbuffer보다 선호했습니다!
neuro

scriptBusyBox 에서도 사용 가능합니다 !
고인돌

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"에 대한 셸 변수를 보고있는 경우에는 작동하지 않습니다 .
Jay Taylor

1
이것은 놀랍다. Wine 내에서 실행되는 실행 파일 내에 임베디드 Python 라이브러리가있는 매우 드문 유스 케이스에 이것이 필요했습니다. 터미널에서 실행하면 작동했지만 .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
맥 OS 상점에서 작동하지 않는 것, 내가 얻을 script: illegal option -- f
알렉산더 밀스

23

버퍼 해제 와 함께 제공 스크립트를 기대 한다 이 확인을 처리합니다. 그렇지 않은 경우, 응용 프로그램은 예를 들어 출력이 연결된 것과 다른 것을보고있을 수 있습니다. 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 */
}

그러나 실제로 expectPTY를 생성 한다는 인상을 받았습니다 .


macOS x에서 하위 프로세스로 nettop을 실행하는 방법을 알고 있습니까? 내 앱에서 nettop의 출력을 얻고 싶습니다. forkpty를 사용해 보았지만 여전히 nettop을 성공적으로 실행할 수 없었습니다.
Vince Yuan

16

이전 답변을 참조하면 Mac OS X에서 "스크립트"는 다음과 같이 사용할 수 있습니다.

script -q /dev/null commands...

그러나 stdout에서 "\ n"을 "\ r \ n"으로 바꿀 수 있으므로 다음과 같은 스크립트가 필요할 수도 있습니다.

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) Linux 및 MacO 모두에서 작동하는 @ A-Ron의 답변 업데이트 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와 함께 사용하려면 주석에서 옵션 중 하나를 시도했습니다.

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

"UNIX 환경에서의 고급 프로그래밍, 제 2 판"책의 샘플 코드에는 pty 프로그램도 포함되어 있습니다!

Mac OS X에서 pty를 컴파일하는 방법은 다음과 같습니다.

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.