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
How to trick an application into thinking its stdout is a terminal, not a pipe
提问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 expect
script 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 script
command 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 printf
is 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
回答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 expect
itself 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 faketty
function posted by ingomueller-net above since it recently helped me out.
对具体答案发表评论太新了,但我想我会跟进faketty
上面 ingomueller-net 发布的功能,因为它最近帮助了我。
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:
我发现这正在创建一个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 --return
and 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 script
does 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 *