bash 如何诱使应用程序认为其标准输出是终端,而不是管道

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1401002/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:29:58  来源:igfitidea点击:

How to trick an application into thinking its stdout is a terminal, not a pipe

bashterminalpipestdin

提问by

I'm trying to do the opposite of "Detect if stdin is a terminal or pipe?".

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

I'm running an application that's changing its output format because it detects a pipe on STDOUT, and I want it to think that it's an interactive terminal so that I get the same output when redirecting.

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

I was thinking that wrapping it in an expectscript or using a proc_open()in PHP would do it, but it doesn't.

我认为将它包装在expect脚本中或proc_open()在 PHP 中使用 a可以做到,但事实并非如此。

Any ideas out there?

有什么想法吗?

回答by

Aha!

啊哈!

The scriptcommand does what we want...

script命令执行我们想要的...

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

回答by ingomueller.net

Based on Chris' solution, I came up with the following little helper function:

基于Chris 的解决方案,我想出了以下小辅助函数:

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

The quirky looking printfis necessary to correctly expand the script's arguments in $@while protecting possibly quoted parts of the command (see example below).

古怪的外观printf对于正确扩展脚本的参数是必要的,$@同时保护命令的可能引用部分(参见下面的示例)。

Usage:

用法:

faketty <command> <args>

Example:

例子:

$ 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

回答by Colin Macleod

The unbufferscript that comes with Expectshouldhandle this ok. If not, the application may be looking at something other than what its output is connected to, eg. what the TERM environment variable is set to.

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

回答by ephemient

I don't know if it's doable from PHP, but if you really need the child process to see a TTY, you can create a PTY.

我不知道从 PHP 是否可行,但是如果您确实需要子进程来查看 TTY,则可以创建一个PTY

In C:

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

I was actually under the impression that expectitself does creates a PTY, though.

不过,我实际上的印象是expect它本身确实创建了一个 PTY。

回答by Tsuneo Yoshioka

Referring previous answer, on Mac OS X, "script" can be used like below...

参考之前的答案,在 Mac OS X 上,可以像下面这样使用“脚本”...

script -q /dev/null commands...

But, because it may replace "\n" with "\r\n" on the stdout, you may also need script like this:

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

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

If there are some pipe between these commands, you need to flush stdout. for example:

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

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

回答by A-Ron

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

对具体答案发表评论太新了,但我想我会跟进faketty上面 ingomueller-net 发布的功能,因为它最近帮助了我。

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

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

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

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

回答by Nick ODell

I was trying to get colors when running shellcheck <file> | less, so I tried the above answers, but they produce this bizarre effect where text is horizontally offset from where it should be:

我试图在运行时获取颜色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.

(For those unfamiliar with shellcheck, the line with the warning is supposed to line up with the where the problem is.)

(对于那些不熟悉 shellcheck 的人,带有警告的行应该与问题所在的行对齐。)

In order to the answers above to work with shellcheck, I tried one of the options from the comments:

为了使上述答案与 shellcheck 一起使用,我尝试了评论中的选项之一:

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

This works. I also added --returnand used long options, to make this command a little less inscrutable:

这有效。我还添加--return并使用了长选项,使这个命令不那么难以理解:

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

Works in Bash and Zsh.

在 Bash 和 Zsh 中工作。

回答by Jonas Berlin

Updating @A-Ron's answer to a) work on both Linux & MacOs b) propagate status code indirectly (since MacOs scriptdoes not support it)

更新@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
}

Examples:

例子:

$ 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

回答by frank

There's also a pty program included in the sample code of the book "Advanced Programming in the UNIX Environment, Second Edition"!

在“UNIX 环境中的高级编程,第二版”一书的示例代码中也包含了一个 pty 程序!

Here's how to compile pty on Mac OS X:

以下是在 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 *