我正在尝试执行与“ 检测stdin是终端还是管道? ” 相反的操作。
我正在运行一个正在更改其输出格式的应用程序,因为它检测到STDOUT上的管道,并且我希望它认为它是一个交互式终端,以便在重定向时获得相同的输出。
我当时认为将其包装在expect
脚本中或proc_open()
在PHP中使用可以实现,但事实并非如此。
有什么想法吗?
我正在尝试执行与“ 检测stdin是终端还是管道? ” 相反的操作。
我正在运行一个正在更改其输出格式的应用程序,因为它检测到STDOUT上的管道,并且我希望它认为它是一个交互式终端,以便在重定向时获得相同的输出。
我当时认为将其包装在expect
脚本中或proc_open()
在PHP中使用可以实现,但事实并非如此。
有什么想法吗?
Answers:
啊哈!
该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
script
甚至可以在BusyBox中使用!
less -R
,终端输入将进入less -R
,则您需要一些额外的技巧。例如,我想要彩色版本的git status | less
。你需要传递-R
到更小,以便它尊重的颜色,你需要使用script
获得git status
输出色彩。但是我们不想script
保留键盘的所有权,我们希望它去less
。因此,我现在使用它,并且效果很好:0<&- script -qfc "git status" /dev/null | less -R
。前几个字符为此命令关闭了stdin。
$-
外壳变量中查找“ i”的情况下,此方法不起作用。
Py_Initialize
没有看到正确的stdin / stderr。
根据克里斯的解决方案,我想到了以下小助手功能:
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
--return
选项(如果您的版本script
具有该选项)来保留子进程的退出代码。
function faketty { script -qfc "$(printf "%q " "$@")" /dev/null; }
名为的文件typescript
。
script: illegal option -- f
我不知道它在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。
参考先前的答案,在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'
更新@ A-Ron的答案a)在Linux和MacO上均可工作b)间接传播状态代码(因为MacO 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
我试图在运行时获取颜色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.
(对于那些不熟悉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中使用。
“ 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 *