简体   繁体   English

使用Python子进程中的Vim编辑临时文件在Mac OS上无法正常工作

[英]Editing temporary file with Vim in Python subprocess not working as expected on Mac OS

My initial goal was to get user input via a command-line text-editor from within a Python script. 我最初的目标是通过Python脚本中的命令行文本编辑器获取用户输入。 More specifically, my plan was to create a temporary file and populate it with some pre-written text, open the file with a text-editor and allow the user to modify the file content, read the data from the file after the user exits the editor, then finally delete the file after it's all over. 更具体地说,我的计划是创建一个临时文件并用一些预先写好的文本填充它,用文本编辑器打开文件并允许用户修改文件内容,在用户退出后从文件中读取数据编辑器,然后最后删除文件后全部删除。

I seem to have found a way to do this that is working for me, but along the way I tried a couple of approaches that did not work and I'd like to understand exactly why. 我似乎已经找到了一种方法来做到这一点,这对我有用,但一路上我尝试了几种不起作用的方法,我想知道究竟为什么。

Consider the following Python script (a slightly modified version of the script taken from this post ): 考虑以下Python脚本(从这篇文章中略微修改的脚本版本):

#!/usr/bin/env python2
# -*- encoding: ascii -*-
"""callvim.py

Demonstrates calling a text-editor (e.g. Vim) from within a Python script,
including passing input to the editor and reading output from the editor.
"""

import tempfile
import os
from subprocess import call

# Get the text editor from the shell, otherwise default to Vim
EDITOR = os.environ.get('EDITOR','vim')

# Set initial input with which to populate the buffer
initial_message = "Hello world!"

# Open a temporary file to communicate through
with tempfile.NamedTemporaryFile(suffix=".tmp") as tf:

    # Write the initial content to the file I/O buffer
    tf.write(initial_message)

    # Flush the I/O buffer to make sure the data is written to the file
    tf.flush()

    # Open the file with the text editor
    call([EDITOR, tf.name])

    # Rewind the file offset to the beginning of the file
    tf.seek(0)

    # Read the file data into a variable
    edited_message = tf.read()

    # Output the data
    print(edited_message)

I've tried running this script in two different environments so far: on a macOS computer (running macOS 10.12) and on a Debian computer (running Debian 8.8). 到目前为止,我已尝试在两个不同的环境中运行此脚本:在macOS计算机(运行macOS 10.12)和Debian计算机(运行Debian 8.8)上运行。 Both computers have the same (minor) version of Vim installed (Vim 7.4). 两台计算机都安装了相同(次要)的Vim版本(Vim 7.4)。

When I run this script with EDITOR=vim on my Debian 8 (Jessie) machine it works as expected. 当我在Debian 8(Jessie)机器上使用EDITOR=vim运行此脚本时,它按预期工作。 I'm prompted with Vim and a buffer containing the string "Hello world!". 我被Vim和一个包含字符串“Hello world!”的缓冲区提示。 After editing the buffer to contain the string "Goodbye world!", saving the file, and exiting Vim, I see the string "Goodbye world!" 在编辑缓冲区以包含字符串“Goodbye world!”,保存文件并退出Vim之后,我看到字符串“Goodbye world!” printed to the console. 打印到控制台。

When I run the same script on my macOS 10.12 (Sierra) machine it does not seem to work. 当我在我的macOS 10.12(Sierra)机器上运行相同的脚本时,它似乎不起作用。 The same procedure results in "Hello world!" 同样的程序导致“Hello world!” being displayed on-screen - as if the file is being read before it is edited. 在屏幕上显示 - 就像在编辑文件之前正在读取文件一样。

However if run the script on my Mac with EDITOR=nano then once again everything seems to work as expected. 但是如果在我的Mac上使用EDITOR=nano运行脚本,那么一切似乎都按预期工作。

I tried a few variations on this script using different methods from the tempfile module (eg using tempfile.TemporaryFile() and tempfile.mkstemp() ) with the same results. 我尝试使用tempfile模块中的不同方法(例如使用tempfile.TemporaryFile()tempfile.mkstemp() )对此脚本进行了一些变化,结果相同。

Now consider the following alternative script: 现在考虑以下替代脚本:

#!/usr/bin/env python2
# -*- encoding: ascii -*-
"""callvim.py

Demonstrates calling a text-editor (e.g. Vim) from within a Python script,
including passing input to the editor and reading output from the editor.
"""

import subprocess
import os

# Create a temporary file and write some default text
file_path = "tempfile"
file_handle = open(file_path, "w")
file_handle.write("Hello world!")
file_handle.close()

# Open the file with Vim
subprocess.call(["vim", file_path])

# Rewind to the beginning of the file
file_handle = open(file_path, 'r')

# Read the data from the file
data = file_handle.read()

# Close the temporary file
file_handle.close()

# Delete the temporary file
os.remove(file_path)

# Print the data
print(data)

This script, which avoids using the tempfile module, appears to be working consistently across both platforms. 这个避免使用tempfile模块的脚本似乎在两个平台上都能保持一致。

So it seems that this script may be failing for some reason having to do with how Vim and the tempfile Python module interact on macOS. 因此,由于某些原因,这个脚本可能因为Vim和tempfile Python模块在macOS上的交互方式而失败。 What's going on here? 这里发生了什么?

This is happening because your second script closes the file handle before invoking vim , then opens a new one afterwards, whereas the first script doesn't. 发生这种情况是因为你的第二个脚本在调用vim之前关闭文件句柄,然后在之后打开一个新脚本,而第一个脚本则没有。 It has nothing to do with the tempfile module per se. 它与tempfile模块本身无关。 This code works as expected: 此代码按预期工作:

import tempfile, os
from subprocess import call

initial_message = "Hello world!"

tf = tempfile.NamedTemporaryFile(suffix=".tmp", delete=False)
tf.write(initial_message)
tf.close()

call(['vim', tf.name])

tf = open(tf.name, 'r')
edited_message = tf.read()
tf.close()

os.unlink(tf.name)

print(edited_message)

Note the delete=False in the call to NamedTemporaryFile , which ensures that the file isn't deleted when we close it the first time (we have to delete it manually with os.unlink later). 注意对NamedTemporaryFile的调用中的delete=False ,它确保在我们第一次关闭它时不删除该文件(我们必须稍后用os.unlink手动删除它)。

I think what's going on here is that vim isn't editing your file in-place, it's writing to a swap file. 我认为这里发生的事情是vim没有就地编辑你的文件,而是写入交换文件。 When you save and quit, vim replaces the original file with the edited one, leaving your file handle pointing to the old, unedited one. 保存并退出时, vim将原始文件替换为已编辑的文件,使文件句柄指向旧的未编辑文件。 There are ways to prevent vim from using swap files (see, eg here ), but I wouldn't recommend them. 有一些方法可以阻止vim使用交换文件(参见例如此处 ),但我不推荐它们。 Just remember to update your file handles after vim has done its business. 只需记住在vim完成业务后更新文件句柄。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM