Python3/Linux - 在默认编辑器中打开文本文件并等待完成
Python3/Linux - Open text file in default editor and wait until done
我需要等到用户在默认图形应用程序(Debian 和衍生版)中完成对文本文件的编辑。
如果我将 xdg-open 与 subprocess.call 一起使用(通常会等待),它将在编辑器中打开文件后继续。我假设是因为 xdg-open 本身异步启动了编辑器。
通过检索 text/plain mime 类型的启动器并将其与 Gio.DesktopAppInfo.new 一起使用以获得编辑器的命令,我终于得到了或多或少的工作代码。如果编辑器尚未打开,则在编辑器仍处于打开状态时进程结束。
我添加了检查 process.pid 和轮询进程的解决方案。两者都以无限循环结束。
等待进程完成似乎是一种过于复杂的方式。那么,有没有更强大的方法来做到这一点?
#! /usr/bin/env python3
import subprocess
from gi.repository import Gio
import os
from time import sleep
import sys
def open_launcher(my_file):
print('launcher open')
app = subprocess.check_output(['xdg-mime', 'query', 'default', 'text/plain']).decode('utf-8').strip()
print(app)
launcher = Gio.DesktopAppInfo.new(app).get_commandline().split()[0]
print(launcher)
subprocess.call([launcher, my_file])
print('launcher close')
def open_xdg(my_file):
print('xdg open')
subprocess.call(['xdg-open', my_file])
print('xdg close')
def check_pid(pid):
""" Check For the existence of a unix pid. """
try:
os.kill(int(pid), 0)
except OSError:
return False
else:
return True
def open_pid(my_file):
pid = subprocess.Popen(['xdg-open', my_file]).pid
while check_pid(pid):
print(pid)
sleep(1)
def open_poll(my_file):
proc = subprocess.Popen(['xdg-open', my_file])
while not proc.poll():
print(proc.poll())
sleep(1)
def open_ps(my_file):
subprocess.call(['xdg-open', my_file])
pid = subprocess.check_output("ps -o pid,cmd -e | grep %s | head -n 1 | awk '{print }'" % my_file, shell=True).decode('utf-8')
while check_pid(pid):
print(pid)
sleep(1)
def open_popen(my_file):
print('popen open')
process = subprocess.Popen(['xdg-open', my_file])
process.wait()
print(process.returncode)
print('popen close')
# This will end the open_xdg function while the editor is open.
# However, if the editor is already open, open_launcher will finish while the editor is still open.
#open_launcher('test.txt')
# This solution opens the file but the process terminates before the editor is closed.
#open_xdg('test.txt')
# This will loop indefinately printing the pid even after closing the editor.
# If you check for the pid in another terminal you see the pid with: [xdg-open] <defunct>.
#open_pid('test.txt')
# This will print None once after which 0 is printed indefinately: the subprocess ends immediately.
#open_poll('test.txt')
# This seems to work, even when the editor is already open.
# However, I had to use head -n 1 to prevent returning multiple pids.
#open_ps('test.txt')
# Like open_xdg, this opens the file but the process terminates before the editor is closed.
open_popen('test.txt')
您可以简单地等待子进程终止,而不是尝试轮询 PID,使用 subprocess.Popen.wait()
:
Wait for child process to terminate. Set and return returncode attribute.
此外,获取 get_commandline()
的第一部分并不能保证是启动器。 get_commandline()
返回的字符串将匹配 the Exec
key spec,这意味着返回字符串中的 %u
、%U
、%f
和 %F
字段代码应该替换为正确的值。
这是一些示例代码,基于您的 xdg-mime
方法:
#!/usr/bin/env python3
import subprocess
import shlex
from gi.repository import Gio
my_file = 'test.txt'
# Get default application
app = subprocess.check_output(['xdg-mime', 'query', 'default', 'text/plain']).decode('utf-8').strip()
# Get command to run
command = Gio.DesktopAppInfo.new(app).get_commandline()
# Handle file paths with spaces by quoting the file path
my_file_quoted = "'" + my_file + "'"
# Replace field codes with the file path
# Also handle special case of the atom editor
command = command.replace('%u', my_file_quoted)\
.replace('%U', my_file_quoted)\
.replace('%f', my_file_quoted)\
.replace('%F', my_file_quoted if app != 'atom.desktop' else '--wait ' + my_file_quoted)
# Run the default application, and wait for it to terminate
process = subprocess.Popen(
shlex.split(command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
process.wait()
# Now the exit code of the text editor process is available as process.returncode
我对我的示例代码有几点意见。
备注1:处理文件路径中的空格
要打开的文件路径用引号引起来很重要,否则 shlex.split(command)
会用空格分割文件名。
备注 2:转义 %
个字符
Literal percentage characters must be escaped as %%.
我对 replace()
的使用可能会替换被转义的 %
个字符。为简单起见,我选择忽略这种边缘情况。
备注3:原子
我假设期望的行为是一直等到图形编辑器关闭。对于 atom 文本编辑器,它将在启动 window 时立即终止,除非提供了 --wait
选项。因此,如果默认编辑器是 atom,我有条件地添加 --wait
选项。
备注4:subprocess.DEVNULL
subprocess.DEVNULL
是 python 3.3 中的新增功能。对于较旧的 python 版本,可以使用以下内容代替:
with open(os.devnull, 'w') as DEVNULL:
process = subprocess.Popen(
shlex.split(command), stdout=DEVNULL, stderr=DEVNULL)
测试
我在 Ubuntu 上使用 GNOME 桌面环境测试了上面的示例代码。我使用以下图形文本编辑器进行了测试:gedit、mousepad 和 atom.
我需要等到用户在默认图形应用程序(Debian 和衍生版)中完成对文本文件的编辑。
如果我将 xdg-open 与 subprocess.call 一起使用(通常会等待),它将在编辑器中打开文件后继续。我假设是因为 xdg-open 本身异步启动了编辑器。
通过检索 text/plain mime 类型的启动器并将其与 Gio.DesktopAppInfo.new 一起使用以获得编辑器的命令,我终于得到了或多或少的工作代码。如果编辑器尚未打开,则在编辑器仍处于打开状态时进程结束。
我添加了检查 process.pid 和轮询进程的解决方案。两者都以无限循环结束。
等待进程完成似乎是一种过于复杂的方式。那么,有没有更强大的方法来做到这一点?
#! /usr/bin/env python3
import subprocess
from gi.repository import Gio
import os
from time import sleep
import sys
def open_launcher(my_file):
print('launcher open')
app = subprocess.check_output(['xdg-mime', 'query', 'default', 'text/plain']).decode('utf-8').strip()
print(app)
launcher = Gio.DesktopAppInfo.new(app).get_commandline().split()[0]
print(launcher)
subprocess.call([launcher, my_file])
print('launcher close')
def open_xdg(my_file):
print('xdg open')
subprocess.call(['xdg-open', my_file])
print('xdg close')
def check_pid(pid):
""" Check For the existence of a unix pid. """
try:
os.kill(int(pid), 0)
except OSError:
return False
else:
return True
def open_pid(my_file):
pid = subprocess.Popen(['xdg-open', my_file]).pid
while check_pid(pid):
print(pid)
sleep(1)
def open_poll(my_file):
proc = subprocess.Popen(['xdg-open', my_file])
while not proc.poll():
print(proc.poll())
sleep(1)
def open_ps(my_file):
subprocess.call(['xdg-open', my_file])
pid = subprocess.check_output("ps -o pid,cmd -e | grep %s | head -n 1 | awk '{print }'" % my_file, shell=True).decode('utf-8')
while check_pid(pid):
print(pid)
sleep(1)
def open_popen(my_file):
print('popen open')
process = subprocess.Popen(['xdg-open', my_file])
process.wait()
print(process.returncode)
print('popen close')
# This will end the open_xdg function while the editor is open.
# However, if the editor is already open, open_launcher will finish while the editor is still open.
#open_launcher('test.txt')
# This solution opens the file but the process terminates before the editor is closed.
#open_xdg('test.txt')
# This will loop indefinately printing the pid even after closing the editor.
# If you check for the pid in another terminal you see the pid with: [xdg-open] <defunct>.
#open_pid('test.txt')
# This will print None once after which 0 is printed indefinately: the subprocess ends immediately.
#open_poll('test.txt')
# This seems to work, even when the editor is already open.
# However, I had to use head -n 1 to prevent returning multiple pids.
#open_ps('test.txt')
# Like open_xdg, this opens the file but the process terminates before the editor is closed.
open_popen('test.txt')
您可以简单地等待子进程终止,而不是尝试轮询 PID,使用 subprocess.Popen.wait()
:
Wait for child process to terminate. Set and return returncode attribute.
此外,获取 get_commandline()
的第一部分并不能保证是启动器。 get_commandline()
返回的字符串将匹配 the Exec
key spec,这意味着返回字符串中的 %u
、%U
、%f
和 %F
字段代码应该替换为正确的值。
这是一些示例代码,基于您的 xdg-mime
方法:
#!/usr/bin/env python3
import subprocess
import shlex
from gi.repository import Gio
my_file = 'test.txt'
# Get default application
app = subprocess.check_output(['xdg-mime', 'query', 'default', 'text/plain']).decode('utf-8').strip()
# Get command to run
command = Gio.DesktopAppInfo.new(app).get_commandline()
# Handle file paths with spaces by quoting the file path
my_file_quoted = "'" + my_file + "'"
# Replace field codes with the file path
# Also handle special case of the atom editor
command = command.replace('%u', my_file_quoted)\
.replace('%U', my_file_quoted)\
.replace('%f', my_file_quoted)\
.replace('%F', my_file_quoted if app != 'atom.desktop' else '--wait ' + my_file_quoted)
# Run the default application, and wait for it to terminate
process = subprocess.Popen(
shlex.split(command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
process.wait()
# Now the exit code of the text editor process is available as process.returncode
我对我的示例代码有几点意见。
备注1:处理文件路径中的空格
要打开的文件路径用引号引起来很重要,否则 shlex.split(command)
会用空格分割文件名。
备注 2:转义 %
个字符
Literal percentage characters must be escaped as %%.
我对 replace()
的使用可能会替换被转义的 %
个字符。为简单起见,我选择忽略这种边缘情况。
备注3:原子
我假设期望的行为是一直等到图形编辑器关闭。对于 atom 文本编辑器,它将在启动 window 时立即终止,除非提供了 --wait
选项。因此,如果默认编辑器是 atom,我有条件地添加 --wait
选项。
备注4:subprocess.DEVNULL
subprocess.DEVNULL
是 python 3.3 中的新增功能。对于较旧的 python 版本,可以使用以下内容代替:
with open(os.devnull, 'w') as DEVNULL:
process = subprocess.Popen(
shlex.split(command), stdout=DEVNULL, stderr=DEVNULL)
测试
我在 Ubuntu 上使用 GNOME 桌面环境测试了上面的示例代码。我使用以下图形文本编辑器进行了测试:gedit、mousepad 和 atom.