如何在使用 sys.stdin.read() 和调用 subprocess 打开 vim 后避免终端混乱?

huangapple go评论114阅读模式
英文:

How not to mess terminal up after sys.stdin.read() and subprocess invoking vim?

问题

以下是您要翻译的内容:

I want to create interactive mode code like git rebase -i HEAD~6 but for PIPE editing, stdin redirection. another example is vipe of moreutils.

To do that, I learn this code below.

  1. # Source: https://stackoverflow.com/a/39989442/20307768
  2. import sys, tempfile, os
  3. from subprocess import call
  4. EDITOR = os.environ.get('EDITOR', 'vim') # that easy!
  5. initial_message = b'something' # if you want to set up the file somehow
  6. with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
  7. tf.write(initial_message)
  8. tf.flush()
  9. call([EDITOR, tf.name])

To get PIPE and edit it, I added two lines.

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

The problematic full code is below.

  1. import sys, tempfile, os
  2. from subprocess import call
  3. EDITOR = os.environ.get('EDITOR', 'vim')
  4. text = sys.stdin.read()
  5. initial_message = text.encode()
  6. with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
  7. tf.write(initial_message)
  8. tf.flush()
  9. call([EDITOR, tf.name])

After running the second code with echo "some words" | python the_code.py in the shell and exiting vim:q!, the terminal is messed up. (reset in the shell command will fix it.)

Without reset, I can type in a shell of macOS, but the prompt is in a weird place.
I can't even type in a shell of Linux.

I typed set -x, already.

  1. [rockyos@localhost python-vipe]$ echo "asdfasd" | python vipe.py
  2. + python vipe.py
  3. + echo asdfasd
  4. Vim: Warning: Input is not from a terminal
  5. ++ printf '3]0;%s@%s:%s
    [rockyos@localhost python-vipe]$ echo "asdfasd" | python vipe.py
  6. + python vipe.py
  7. + echo asdfasd
  8. Vim: Warning: Input is not from a terminal
  9. ++ printf '\033]0;%s@%s:%s\007' rockyos localhost '~/TODO/python-vipe'
  10. ++ history -a
  11. ++ history -c
  12. ++ history -r
  13. [rockyos@localhost python-vipe]$ 
  14. 7' rockyos localhost '~/TODO/python-vipe'
  15. ++ history -a
  16. ++ history -c
  17. ++ history -r
  18. [rockyos@localhost python-vipe]$

I just want to return normal terminal after running the second full code.
Also, why is this happening?

I tried os.system('stty sane; clear;') and os.system('reset') at the end of the code. (os.system('reset') gave me what I wanted. But the message is annoying. I mean I can do os.system('clear') again, but that is not what normal other program does.

  1. Erase set to delete.
  2. Kill set to control-U (^U).
  3. Interrupt set to control-C (^C).
英文:

I want to create interactive mode code like git rebase -i HEAD~6 but for PIPE editing, stdin redirection. another example is vipe of moreutils.

To do that, I learn this code below.

  1. # Source: https://stackoverflow.com/a/39989442/20307768
  2. import sys, tempfile, os
  3. from subprocess import call
  4. EDITOR = os.environ.get('EDITOR', 'vim') # that easy!
  5. initial_message = b'something' # if you want to set up the file somehow
  6. with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
  7. tf.write(initial_message)
  8. tf.flush()
  9. call([EDITOR, tf.name])

To get PIPE and edit it, I added two lines.

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

The problematic full code is below.

  1. import sys, tempfile, os
  2. from subprocess import call
  3. EDITOR = os.environ.get('EDITOR', 'vim')
  4. text = sys.stdin.read()
  5. initial_message = text.encode()
  6. with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
  7. tf.write(initial_message)
  8. tf.flush()
  9. call([EDITOR, tf.name])

After running the second code with echo "some words" | python the_code.py in the shell and exiting vim:q!, the terminal is messed up. (reset in the shell command will fix it.)

Without reset, I can type in a shell of macOS, but the prompt is in a weird place.
I can't even type in a shell of Linux.

I typed set -x, already.

  1. [rockyos@localhost python-vipe]$ echo "asdfasd" | python vipe.py
  2. + python vipe.py
  3. + echo asdfasd
  4. Vim: Warning: Input is not from a terminal
  5. ++ printf '3]0;%s@%s:%s
    [rockyos@localhost python-vipe]$ echo "asdfasd" | python vipe.py
  6. + python vipe.py
  7. + echo asdfasd
  8. Vim: Warning: Input is not from a terminal
  9. ++ printf '\033]0;%s@%s:%s\007' rockyos localhost '~/TODO/python-vipe'
  10.                                                                             ++ history -a
  11.                                                                                          ++ history -c
  12.                                                                                                       ++ history -r
  13.                                                                                                                    [rockyos@localhost python-vipe]$ 
  14. 7' rockyos localhost '~/TODO/python-vipe'
  15. ++ history -a
  16. ++ history -c
  17. ++ history -r
  18. [rockyos@localhost python-vipe]$

I just want to return normal terminal after running the second full code.
Also, why is this happening?

I tried os.system('stty sane; clear;') and os.system('reset') at the end of the code.(https://stackoverflow.com/a/17452756/20307768) os.system('reset') gave me what I wanted. But the message is annoying. I mean I can do os.system('clear') again, but that is not what normal other program does.

  1. Erase set to delete.
  2. Kill set to control-U (^U).
  3. Interrupt set to control-C (^C).

答案1

得分: 3

  1. import sys, tempfile, os
  2. from subprocess import check_call
  3. EDITOR = os.environ.get('EDITOR', 'vim')
  4. text = sys.stdin.read()
  5. initial_message = text.encode()
  6. with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
  7. tf.write(initial_message)
  8. tf.flush()
  9. stdin_fd = os.open('/dev/tty', os.O_RDONLY)
  10. os.dup2(stdin_fd, 0)
  11. os.close(stdin_fd)
  12. check_call([EDITOR, tf.name])
  13. print(open(tf.name).read())
英文:

>I want to create interactive mode code like git rebase -i HEAD~6 but for PIPE editing, stdin redirection. another example is vipe of moreutils.

vipe is an open-source tool, and the source code is less than 100 lines long, so let's take a moment to look at how it does this. It can't rely on stdin or stdout to be a terminal, because normally it's used in the middle of a series of pipes.

Here's how they solved that, in Perl. First, they close STDIN, or file descriptor 0. Then, they open /dev/tty in read mode at descriptor 0. They also do the same thing for STDOUT, but we don't need that.

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

So, how can we do the same thing in Python?

  1. Open /dev/tty in read mode.
  2. That might not have been opened at descriptor 0, so copy it to descriptor 0.
  3. Close the old FD.

Code:

  1. import sys, tempfile, os
  2. from subprocess import check_call
  3. EDITOR = os.environ.get('EDITOR', 'vim')
  4. text = sys.stdin.read()
  5. initial_message = text.encode()
  6. with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:
  7. tf.write(initial_message)
  8. tf.flush()
  9. stdin_fd = os.open('/dev/tty', os.O_RDONLY)
  10. os.dup2(stdin_fd, 0)
  11. os.close(stdin_fd)
  12. check_call([EDITOR, tf.name])
  13. print(open(tf.name).read())

This was tested on OSX 13.3.1.

huangapple
  • 本文由 发表于 2023年6月22日 03:18:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/76526479.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定