linux 如何在sys.stdin.read()和子进程调用vim之后不搞砸终端?

pes8fvy9  于 2023-06-29  发布在  Linux
关注(0)|答案(1)|浏览(165)

我想创建像git rebase -i HEAD~6这样的交互模式代码,但用于PIPE编辑,stdin重定向。另一个例子是moreutilsvipe
为了做到这一点,我学习了下面的代码。

# Source: https://stackoverflow.com/a/39989442/20307768
import sys, tempfile, os
from subprocess import call

EDITOR = os.environ.get('EDITOR', 'vim')  # that easy!

initial_message = b'something'  # if you want to set up the file somehow

with tempfile.NamedTemporaryFile(suffix=".tmp") as tf: 
  tf.write(initial_message)
  tf.flush()
  call([EDITOR, tf.name])

为了获取PIPE并编辑它,我添加了两行。

text = sys.stdin.read()
initial_message = text.encode()

有问题的完整代码如下。

import sys, tempfile, os
from subprocess import call

EDITOR = os.environ.get('EDITOR', 'vim')

text = sys.stdin.read()
initial_message = text.encode()

with tempfile.NamedTemporaryFile(suffix=".tmp") as tf: 
    tf.write(initial_message)
    tf.flush()
    call([EDITOR, tf.name])

在shell中使用echo "some words" | python the_code.py运行第二段代码并退出vim :q!后,终端就乱套了。(shell命令中的reset将修复它。)
没有reset,我可以输入macOS的shell,但提示符在一个奇怪的地方。我甚至不能输入Linux的shell。

  • 我已经输入了set -x *
[rockyos@localhost python-vipe]$ echo "asdfasd" | python vipe.py
+ python vipe.py
+ echo asdfasd
Vim: Warning: Input is not from a terminal
++ printf '\033]0;%s@%s:%s\007' rockyos localhost '~/TODO/python-vipe'
                                                                            ++ history -a
                                                                                         ++ history -c
                                                                                                      ++ history -r
                                                                                                                   [rockyos@localhost python-vipe]$

我只是想返回正常终端后运行第二个完整的代码。还有,为什么会这样?
我在代码的最后尝试了os.system('stty sane; clear;')os.system('reset')。(https://stackoverflow.com/a/17452756/20307768os.system('reset')给了我想要的。但是信息很烦人。我的意思是我可以再做一次os.system('clear'),但这不是正常的其他程序所做的。

Erase set to delete. 
Kill set to control-U (^U). 
Interrupt set to control-C (^C).
i2byvkas

i2byvkas1#

我想创建交互模式的代码,如git rebase -i HEAD~6,但用于PIPE编辑,stdin重定向。另一个例子是Moreutils的VIPE。
vipe是一个开源工具,source code不到100行,所以让我们花点时间来看看它是如何做到这一点的。它不能依赖于stdin或stdout作为终端,因为通常它是在一系列管道的中间使用的。
这是他们如何解决的,在Perl中。首先,它们关闭STDIN,即文件描述符0。然后,它们在描述符0处以读取模式打开/dev/tty。他们也为STDOUT做同样的事情,但我们不需要。

close STDIN;
open(STDIN, "</dev/tty") || die "reopen stdin: $!";
open(OUT, ">&STDOUT") || die "save stdout: $!";
close STDOUT;
open(STDOUT, ">/dev/tty") || die "reopen stdout: $!";

那么,我们如何在Python中做同样的事情呢?
1.在读取模式下打开/dev/tty
1.可能尚未在描述符0处打开,因此将其复制到描述符0。
1.关闭旧FD。
代码:

import sys, tempfile, os
from subprocess import check_call

EDITOR = os.environ.get('EDITOR', 'vim')

text = sys.stdin.read()
initial_message = text.encode()

with tempfile.NamedTemporaryFile(suffix=".tmp") as tf: 
    tf.write(initial_message)
    tf.flush()

    stdin_fd = os.open('/dev/tty', os.O_RDONLY)
    os.dup2(stdin_fd, 0)
    os.close(stdin_fd)

    check_call([EDITOR, tf.name])

    print(open(tf.name).read())

这是在OSX 13.3.1上测试的。

相关问题