3

I'm trying to open a file with vanilla vi (/usr/bin/vi on Solaris 10) from a Python (2.6.4) script, and nothing I do seems to be working. I want to have the script put some data in a temporary file, then open that file in vi for the user to edit. Ideally the script would block on the call to vi and continue executing when the user is finished, but I could settle for solutions transforming the script process into the vi process (via exec or something).

I've tried the following, but for each of them vi prints the first page of the file to the screen and then exits with a "Input read error":

os.execlp('vi', 'vi', filename)

os.system('vi' + ' ' + filename)

subprocess.call('vi' + ' ' + filename, shell=True)

For context, here is the code in its entirety:

#!/usr/bin/python

import sys
import os
import subprocess

fname = "." + str(os.getpid()) + ".pvi.tmp"
f = open(fname, 'w')

f.write("## Remember to save this to a new file if you want to keep it!\n")

for line in sys.stdin:
    f.write(line + "\n")

f.close()

# These all give the error "Input read error"
#os.execlp('vi', 'vi', fname)
#os.system('vi' + ' ' + fname)
#subprocess.call('vi' + ' ' + fname, shell=True)

os.unlink(fname)

I'm basically trying to emulate piping processes into vi, which my version doesn't support (vi - doesn't work). I would pipe them to this script which would then write the output to a temporary file and open it in vi.

Any help would be much appreciated!

4
  • What happens if you just type vi filename, instead of running the Python script? Is $TERM set correctly for your terminal? Commented Aug 26, 2014 at 0:42
  • How are you running your Python script? Commented Aug 26, 2014 at 0:42
  • vi filename works as expected. My $TERM is set to xterm, which I believe is correct. I'm running the script (called pvi) as echo "asdf" | pvi. Commented Aug 26, 2014 at 0:45
  • Oh, that's the problem. Input is the pipe from echo instead of a terminal. Commented Aug 26, 2014 at 0:48

3 Answers 3

3

The problem is that the standard input to vi is inherited from your script, which means it's attached to the pipe and not the terminal. Try this:

os.system('vi' + ' "' + fname + '" </dev/tty >/dev/tty 2>&1')

This is dangerous if fname comes from user input, since it could include embedded shell metacharacters and cause arbitrary commands to get executed. But in your code that appears not to be the case; you're constructing the value deterministically.

The alternative would be to use one of the exec calls to bypass the shell, but then you would have to also do the I/O redirection within the Python, and that gets a bit unwieldy. Whether or not it's worth the effort would depend on the risk exposure of your application. But again, this doesn't seem to be a concern in your particular case.

Sign up to request clarification or add additional context in comments.

2 Comments

Ah, awesome, this worked perfectly! So this is manually piping the terminal's stdin, stdout, and stderr into vi, right? I never would have figured that out. Thanks!
Basically. It actually works the other way around - it's attaching the vi process's three standard I/O streams (input, output, and error) to the terminal. The terminal doesn't know from stdin, stdout, and stderr; it has only a (possibly virtual) display and keyboard.
0

Your system call works for me on OSX:

os.system('vi' + ' ' + fname)

As it should. One consideration in this case is what happens to stdin. If you run this without redirection it works. But if you redirect stdin, for example with python scripty.py < input.txt then VI complains. It gives me Vim: Warning: Input is not from a terminal.

4 Comments

Unfortunately it has the same effect as those listed above for me. It's doubly frustrating because I can see that it's reading the file, it just quits immediately afterward. Judging by other stackoverflow answers, all of those above should work on various platforms, they just aren't working on mine.
@DiputsMonro see my edit. I think it's a question of how you run the python script.
I'm trying to pipe the output of a process into the script, such as echo "asdf" | myscript. Redirection gives me the same error. Are you using vanilla vi, or vim? I suspect this is an issue with the version of vi I have.
It's what comes with OSX, which I think is vim. But the issue is that vi inherits the script's stdin, which in your case is the redirected one. I can't find a portable way to turn the stdin back into a terminal, so I think you'll have to use one of the platform-specific ways listed in the other answers.
0

Your vi program's standard input is not connected to a terminal. One solution is to launch a new terminal:

os.system('xterm -e vi ' + fname)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.