Python3/Linux - 在默认编辑器中打开文本文件并等待完成

如何解决Python3/Linux - 在默认编辑器中打开文本文件并等待完成

我需要等到用户在默认图形应用程序(Debian 和衍生产品)中编辑完文本文件。

如果我将 xdg-open 与 subprocess.call(通常等待)一起使用,它将在编辑器中打开文件后继续。我假设是因为 xdg-open 本身异步启动编辑器。

通过检索 text/plain mime-type 的启动器并将其与 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 $1}'" % 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()

等待子进程终止。设置并返回返回码属性。

此外,不能保证获得 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:转义 % 个字符

Exec key spec 状态

文字百分比字符必须转义为 %%。

我对 replace() 的使用可能会替换 % 被转义的字符。为简单起见,我选择忽略这种边缘情况。

备注 3:原子

我假设所需的行为是始终等到图形编辑器关闭。对于 atom 文本编辑器,除非提供 --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)

测试

我使用 GNOME 桌面环境在 Ubuntu 上测试了上面的示例代码。我使用以下图形文本编辑器进行了测试:geditmousepadatom

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-