我运行的应用程序会改变其输出格式,因为它在STDOUT上检测到一个管道,而我希望它认为它是一个交互式终端,以便在重定向时获得相同的输出。
我想过用
expect
脚本包装它或在PHP中使用proc_open()
,但都不行。有什么想法吗?
expect
脚本包装它或在PHP中使用proc_open()
,但都不行。哦!
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
less -R
,并让终端程序对颜色进行解释,那么需要进行一些额外的处理。比如,我想要一个彩色版本的 git status | less
。你需要给less
传递 -R
以使其支持颜色,并使用 script
来让 git status
输出彩色。但是我们不希望 script
占用键盘,我们希望输入传输到 less
。所以我现在使用了以下命令,运行良好:0<&- script -qfc "git status" /dev/null | less -R
。这几个字符会关闭此命令的标准输入。 - Aaron McDaidPy_Initialize
没有看到正确的stdin/stderr。 - Tatshscript
命令,因为你不需要用引号将命令包起来。该命令运行并将输出发送到tty,然后将其复制到提供的文件中,但是我似乎无法让Linux版本以相同的方式运行...我可能做错了什么。所以在macOS上,这个命令的Linux等价命令是什么:script -q -t 0 tmp.out perl -e 'print "Test\n"'
Test
cat tmp.out
Test
- hepcat72根据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
script
版本支持,您可能希望使用“--return”选项来保留子进程的退出代码。 - jwdfunction faketty { script -qfc "$(printf "%q " "$@")" /dev/null; }
否则,每次运行命令时都会创建一个名为 typescript
的文件,在许多情况下这是不必要的。 - w0rpscript: illegal option -- f
的错误信息。 - Alexander Mills参考之前的回答,在Mac OS X上,"script"可以像下面这样使用...
script -q /dev/null commands...
但是,由于它可能会在标准输出中使用"\r\n"替换"\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'
我不知道从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。
更新 @A-Ron 的答案,以便
a)适用于Linux和MacOs
b)间接传播状态码(因为MacOs 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
SHELL
,因此这个函数在我的(Linux)环境中失败了。script
命令使用 SHELL
来运行 /bin/sh -c ...
,然后运行 $cmd
。通过显式设置 SHELL=/bin/sh
,似乎可以省略其中的一个阶段。我用更简单的 SHELL=/bin/sh script -qfc "$cmd" /dev/null
替换了 Linux 的 script
行,它看起来具有相同的功能。 - Ben Maresuniq
被包裹在faketty
中并从管道中获取标准输入时,它似乎无法正常工作:grep --line-buffered ... | faketty uniq
。(我需要faketty
,因为我需要将uniq
的输出无缓冲地传递给另一个命令。) - undefined对于具体答案,我评论不了,但我想跟进一下ingomueller-net上发布的faketty
函数,因为它最近帮了我一个忙。
我发现这正创建一个typescript
文件,而我不想/不需要,所以我将/dev/null作为脚本目标文件添加:
function faketty { script -qfc "$(printf "%q " "$@")" /dev/null ; }
书籍《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 *
我试图在Linux上运行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.
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。
如果你正在寻找一种使用tee
高亮显示Ansible
输出的颜色,那么你也可以尝试这个方法。
export ANSIBLE_FORCE_COLOR=True