欺骗应用程序认为它的标准输出是一个终端,而不是管道

137 bash terminal stdin pipe

我正试图做相反的事情

检测stdin是终端还是管道?

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

我当时认为将它包装在expect脚本中或使用proc_open()PHP中的它会这样做,但事实并非如此.

有什么想法吗?

小智 167

啊哈!

script命令的作用,我们想要什么?

script --return --quiet -c "[executable string]" /dev/null
Run Code Online (Sandbox Code Playgroud)

诀窍!

  • 如果你想将它管道化为交互式的东西,比如`less -R`,其中终端输入转到`less -R`,那么你需要一些额外的技巧.例如,我想要一个色彩版的`git status | less`.你需要将`-R`传递给less以便它尊重颜色,你需要使用`script`来获得`git status`来输出颜色.但是我们不希望`script`保持键盘的所有权,我们希望这个'更少'.所以我现在使用它并且它运行良好:`0 <& - script -qfc"git status"/ dev/null | 少-R`.前几个字符关闭了这个命令的标准输入. (8认同)
  • 注意:如果检查交互性的组件正在查看"i"的`$ -` shell变量,则这不起作用. (2认同)
  • 是否有一种与操作系统无关的方式来做到这一点?我喜欢 macOS 上的 script 命令,因为您不必将命令括在引号中。该脚本运行并将输出发送到在提供的文件中复制的 tty,但我似乎无法让 linux 版本以相同的方式运行......我可能做错了什么。那么在 macOS 上等效的 linux 脚本命令是什么:`script -q -t 0 tmp.out perl -e 'print "Test\n"'` `Test` `cat tmp.out` `Test` (2认同)

ing*_*net 56

根据Chris的解决方案,我提出了以下小助手功能:

faketty() {
    script -qfc "$(printf "%q " "$@")" /dev/null
}
Run Code Online (Sandbox Code Playgroud)

古怪的看printf是要正确扩大在脚本的参数$@,同时保护命令的可能引述部分(见下面的例子).

用法:

faketty <command> <args>
Run Code Online (Sandbox Code Playgroud)

例:

$ 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
Run Code Online (Sandbox Code Playgroud)

  • 你可能想使用`--return`选项,如果你的`script`版本有它,保留子进程的退出代码. (9认同)
  • 我建议改变这个函数:`function faketty {script -qfc"$(printf"%q""$ @")"/ dev/null; 否则,在许多情况下,每次运行命令时都会创建一个名为`typescript`的文件. (5认同)
  • 似乎在 MacOS 上不起作用,我收到了`script:非法选项 -- f` (4认同)

Col*_*eod 20

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


eph*_*ent 16

我不知道它是否可以通过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 */
}
Run Code Online (Sandbox Code Playgroud)

实际上我的印象是expect它本身确实创造了一个PTY.


Tsu*_*oka 15

参考上一个答案,在Mac OS X上,"脚本"可以像下面这样使用......

script -q /dev/null commands...
Run Code Online (Sandbox Code Playgroud)

但是,因为它可能会将返回代码从"\n"更改为"\ r \n",所以我需要像这样运行.

script -q /dev/null commands... | perl -pe 's/\r\n/\n/g'
Run Code Online (Sandbox Code Playgroud)

如果这些命令之间存在某些管道,则需要刷新stdout.例如:

script -q /dev/null commands... | ruby -ne 'print "....\n";STDOUT.flush' |  perl -pe 's/\r\n/\n/g'
Run Code Online (Sandbox Code Playgroud)


Jon*_*lin 10

更新@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
}
Run Code Online (Sandbox Code Playgroud)

例子:

$ 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
Run Code Online (Sandbox Code Playgroud)


小智 7

对于具体答案的评论太新了,但我想我会跟进fakettyingomueller-net发布的功能,因为它最近帮助了我.

我发现这是创建一个typescript我不想要/不需要的文件所以我添加了/ dev/null作为脚本目标文件:

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