179

我正在尝试与“检测标准输入是终端还是管道? ”相反。

我正在运行一个正在更改其输出格式的应用程序,因为它检测到 STDOUT 上的管道,并且我希望它认为它是一个交互式终端,以便在重定向时获得相同的输出。

我在想将它包装在expect脚本中或proc_open()在 PHP 中使用 a 会做到这一点,但事实并非如此。

有什么想法吗?

4

9 回答 9

188

Aha!

The script command does what we want...

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

Does the trick!

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
于 2009-09-09T22:06:10.870 回答
72

基于克里斯的解决方案,我想出了以下小助手功能:

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
于 2013-12-05T13:47:02.663 回答
28

Expect附带的unbuffer脚本应该可以处理这个问题。如果不是,则应用程序可能正在查看其输出所连接的内容以外的其他内容,例如。TERM 环境变量设置为什么。

于 2009-09-11T11:03:51.793 回答
21

参考以前的答案,在 Mac OS X 上,可以使用“脚本”,如下所示......

script -q /dev/null commands...

但是,因为它可能会在标准输出上将“\n”替换为“\r\n”,所以您可能还需要这样的脚本:

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

如果这些命令之间有一些管道,则需要刷新标准输出。例如:

script -q /dev/null commands... | ruby -ne 'print "....\n";STDOUT.flush' |  perl -pe 's/\r\n/\n/g'
于 2012-11-27T15:51:52.303 回答
19

我不知道它是否可以从 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。

于 2009-09-09T19:21:44.620 回答
13

更新@A-Ron 对 a) 在 Linux 和 MacOs 上工作 b) 间接传播状态代码的答案(因为 MacOsscript不支持它)

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
于 2020-02-18T11:04:02.877 回答
9

Too new to comment on the specific answer, but I thought I'd followup on the faketty function posted by ingomueller-net above since it recently helped me out.

I found that this was creating a typescript file that I didn't want/need so I added /dev/null as the script target file:

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

于 2017-01-29T08:39:15.543 回答
3

在《UNIX 环境高级编程,第二版》一书的示例代码中还包含一个 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 *
于 2010-08-05T10:40:33.210 回答
3

我试图shellcheck <file> | less在 Linux 上运行时获得颜色,所以我尝试了上面的答案,但它们会产生这种奇怪的效果,即文本与应该在的位置水平偏移:

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 中工作。

于 2019-08-06T17:14:42.040 回答