在终端中运行.desktop文件

根据我的收集, .desktop文件是允许自定义应用程序设置的快捷方式。 例如,我在/usr/share/applications/文件夹中有很多它们。

如果我在nautilus打开该文件夹,我可以通过双击其关联文件来运行这些应用程序,例如双击firefox.desktop运行Firefox。 但是,我找不到通过终端做同样事情的方法。

如果我做gnome-open foo.desktop它只是打开foo.desktop作为文本文件。 如果我使它可执行,然后在bash中运行它只是失败(这是预期的,它显然不是bash脚本)。
编辑:执行exec /fullpath/foo.desktop给我一个Permission denied消息,即使我改变自己的所有权。 如果我创建可执行文件并执行相同的命令,我使用的终端选项卡只是关闭(我猜它崩溃了)。 最后,如果我执行sudo exec /fullpath/foo.desktop ,我收到错误报告sudo: exec: command not found

这是我的问题,如何从终端运行foo.desktop文件?

运行的命令包含在桌面文件中,前面是Exec=因此您可以通过以下方式提取和运行该命令:

 `grep '^Exec' filename.desktop | tail -1 | sed 's/^Exec=//' | sed 's/%.//' | sed 's/^"//g' | sed 's/" *$//g'` & 

打破这一点

 grep '^Exec' filename.desktop - finds the line which starts with Exec | tail -1 - only use the last line, in case there are multiple | sed 's/^Exec=//' - removes the Exec from the start of the line | sed 's/%.//' - removes any arguments - %u, %f etc | sed 's/^"//g' | sed 's/" *$//g' - removes " around command (if present) `...` - means run the result of the command run here & - at the end means run it in the background 

你可以将它放在一个文件中,比如~/bin/deskopen中的内容

 #!/bin/sh `grep '^Exec' $1 | tail -1 | sed 's/^Exec=//' | sed 's/%.//' | sed 's/^"//g' | sed 's/" *$//g'` & 

然后使其可执行

 chmod +x ~/bin/deskopen 

然后你可以这样做,例如

 deskopen /usr/share/applications/ubuntu-about.desktop 

参数( %u%F等)详见http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html#exec-variables – 其中没有一个与发布相关在命令行。

答案应该是

 xdg-open program_name.desktop 

但由于一个错误,这不再有效。

对于任何最近支持gtk-launch ubuntu,只需简单地去

gtk-launch 其中是没有.desktop部分的.desktop文件的名称

所以gtk-launch foo打开foo.desktop

如果不在/usr/share/application或执行gtk-launch的地方,那么命令是gtk-launch 。 ( gtk-launch文档 )

可以从终端或alt + F2(alt + F2存储命令在历史记录中使用,以便于访问)

截至今天(12.10), 该bug仍然存在。 事实上,这取决于gvfs-open (由xdg-open调用)的工作方式。

尽管如此,我还是快速解决了这个问题(从nautilus源代码中窃取灵感)。 这有点令人费解,但在Ubuntu 12.10上运行完美,在Unity启动器上添加了一个有意义的图标(不再是? )。

首先,我使用Gio编写了一个python脚本并将其保存为~/bin/run-desktop

 #!/usr/bin/python from gi.repository import Gio import sys def main(myname, desktop, *uris): launcher = Gio.DesktopAppInfo.new_from_filename(desktop) launcher.launch_uris(uris, None) if __name__ == "__main__": main(*sys.argv) 

该脚本需要具有可执行权限,因此我在终端中运行它:

 chmod +x ~/bin/run-desktop 

然后我在~/.local/share/applications/run-desktop.desktop上创建了相对的.desktop条目:

 [Desktop Entry] Version=1.0 Name=run-desktop Exec=run-desktop %U MimeType=application/x-desktop Terminal=false Type=Application 

最后,我将条目作为[Default Applications]部分下的~/.local/share/applications/mimeapps.list的默认处理程序关联为:

 [Default Applications] .... application/x-desktop=run-desktop.desktop 

现在:

  • xdg-open something.desktop按预期工作
  • 在可执行桌面条目之上的#!/usr/bin/xdg-open hashbang也可以

gvfs-open解决这个bug时,它将是无用的工作,但在此期间……

正确的方式

如果可用的话,你应该使用gtk-launch 。 它通常是包libgtk-3-bin的一部分 (这可能因发行版而异)。

gtk-launch使用如下:

 gtk-launch APPLICATION [URI...] gtk-launch app-name.desktop gtk-launch app-name 

请注意, gtk-launch需要安装.desktop文件(即位于/usr/share/applications~/.local/share/applications )。

因此,为了解决这个问题,我们可以使用一个hackish little Bash函数,在启动之前临时安装所需的.desktop文件。 安装.desktop文件的“正确”方法是通过desktop-file-install但我将忽略它。

 launch(){ # Usage: launch PATH [URI...] # NOTE: The bulk of this function is executed in a subshell, ie `(..)` # This isn't strictly necessary, but it keeps everything # out of the global namespace and lessens the likelihood # of side effects. ( # where you want to install the launcher to appdir=$HOME/.local/share/applications # the template used to install the launcher template=launcher-XXXXXX.desktop # ensure $1 has a .desktop extension, exists, is a normal file, is readable, has nonzero size # optionally use desktop-file-validate for stricter checking # desktop-file-validate "$1" 2>/dev/null || { [[ $1 = *.desktop && -f $1 && -r $1 && -s $1 ]] || { echo "ERROR: you have not supplied valid .desktop file" >&2 return 1 } # ensure the temporary launcher is deleted upon exit trap 'rm "$launcherfile" &>/dev/null' EXIT # create a temp file to overwrite later launcherfile=$(mktemp -p "$appdir" "$template") launchername=${launcherfile##*/} # overwrite temp file with the launcher file if cp "$1" "$launcherfile" &>/dev/null; then gtk-launch "$launchername" "${@:2}" else echo "ERROR: failed to copy launcher to applications directory" >&2 return 1 fi ) } 

您可以像这样使用它(如果需要,还可以传递其他参数或URI):

 launch PATH [URI...] launch ./path/to/shortcut.desktop 

手动替代方案

如果要手动解析并执行.desktop文件,可以使用以下awk命令执行此操作:

 awk '/^Exec=/ {sub("^Exec=", ""); gsub(" ?%[cDdFfikmNnUuv]", ""); exit system($0)}' app-name.desktop 

如果要将awk命令视为一体化脚本; 我们甚至可以显示错误消息,并在未找到Exec命令的情况下以返回码1退出:

 awk 'BEGIN {command=""} /^Exec=/ {sub("^Exec=", ""); gsub(" ?%[cDdFfikmNnUuv]", ""); command=$0; exit} END {if (command!="") {exit system(command)} else {if (FILENAME == "-") {printf "ERROR: Failed to identify Exec line\n" > "/dev/stderr"} else {printf "ERROR: Failed to identify Exec line in \047%s\047\n", FILENAME > "/dev/stderr"} close("/dev/stderr"); exit 1}}' 

上述命令将:

  1. 找到以Exec =开头的行
  2. 删除Exec =
  3. 删除任何Exec变量(例如%f%u%U )。 可以根据规范的意图用位置参数替换它们,但这样做会增加问题的复杂性。 查看最新的桌面条目规范 。
  4. 执行命令
  5. 立即退出并使用适当的退出代码(以便不执行多个Exec行)

请注意,此AWK脚本解决了一些边缘情况,这些边缘情况可能会或可能不会被其他一些答案正确解决。 具体来说,此命令删除多个Exec变量(注意不要以其他方式删除%符号),只执行单个Exec行命令,并且即使Exec行命令包含一个或多个等号(例如script.py --profile=name ),它也将按预期运行script.py --profile=name )。

其他几点需要注意……根据规范, TryExec是:

磁盘上可执行文件的路径,用于确定程序是否实际安装。 如果路径不是绝对路径,则在$ PATH环境变量中查找该文件。 如果文件不存在或者文件不可执行,则可以忽略该条目(例如,不在菜单中使用)。

考虑到这一点,执行它的价值是没有意义的。

其他一些问题是PathTerminalPath包含运行程序的工作目录.Terminal是一个布尔值,表示程序是否在终端窗口中运行。 这些都可以解决,但重新发明轮子是没有意义的,因为已经有规范的实施。 如果你想要实现Path ,请记住system()产生一个子进程,所以你不能通过做类似system("cd \047" working_directory "\047"); system(command)类的东西来改变工作目录system("cd \047" working_directory "\047"); system(command) system("cd \047" working_directory "\047"); system(command) 。 但是你可能会做类似system("cd \047" working_directory "\047 && " command)事情system("cd \047" working_directory "\047 && " command) 。 注意\ 047是单引号(因此命令不会在带空格的路径上中断)。

Python替代品

我在这里偷了一个来自Carlo的页面,他建议创建一个Python脚本来使用gi模块。 这是从shell执行相同代码的最小方法,无需创建文件并担心I / O.

 launch(){ # Usage: launch PATH [URI...] python - "$@" < 

然后按如下方式执行启动器function:

 launch ./path/to/shortcut.desktop 

请注意,URI的使用是可选的。 此外,没有执行错误检查,因此如果您希望脚本持久,您将需要确保启动程序存在且可读(在使用之前)。

虽然OP没有询问KDE,但对于运行KDE的任何人来说,可以使用以下命令:

kioclient exec

 exo-open [[path-to-a-desktop-file]...] 

如果安装了exo-utils,就好像在13.10版本中工作(就像Xubuntu一样)。

你可以使用dex 。

 dex foo.desktop 

对哈米什回答的补遗。

给定deskopen脚本,您可以使用它作为.desktop文件中的shebang行的引用,因为注释字符仍然是# 。 也就是说,把它作为.desktop文件的第一行:

 #!/usr/bin/env deskopen 

然后将.desktop文件标记为可执行文件(例如,使用chmod +x whatever.desktop ),然后就可以了

 path/to/whatever.desktop 

voilà – 该应用程序将打开! (完成我指定的图标文件,但我不知道如何。)

现在,如果您还希望deskopen通过任何命令行参数,您可以改为使用这个稍微修改过的版本:

 #!/bin/sh desktop_file=$1 shift `grep '^Exec' "${desktop_file}" | sed 's/^Exec=//' | sed 's/%.//'` "$@" & 

顺便说一句,我尝试使用"#{@:2}"而不是shift ,但它一直给我’糟糕的替代’……

目前没有一个应用程序可以执行您在Ubuntu归档中描述的内容。 正在进行一些努力来创建一个通用解决方案,以便为不符合这些XDG规范的桌面环境(如openbox)提供集成。

Arch Linux正在开发基于python-xdg库的xdg-autostart实现。 根据我的发现,这似乎还没有完全完成,但有一些成功的报告。

在gitorious(http://gitorious.org/xdg-autostart/)上还有一个xdg-autostart的C ++实现,它可能会受益于更广泛的使用。

如果任何一种解决方案适合您,请考虑提交必要的工作以包含在Debian或Ubuntu中。

要使用openstart中的任一工具,您可以在/etc/xdg/openbox/autostart.sh中调用它(如果我正确阅读openbox文档)。 如果这不起作用,您可以在任何openbox会话初始化脚本中调用它。

我没有立即解决方案满足“使用标准命令”的要求,但如果您确实想要最低限度地解析.desktop文件或想要创建Bash别名,那么以下内容应该有效:

  • awk -F= '/Exec=/{system($2); exit}' foo.desktop

另一种可能有趣的方法是创建内核级binfmt-misc方法,而不是.desktop文件上的匹配(请参阅grep -r . /proc/sys/fs/binfmt_misc/ ,了解您当前启用的模式)。

在一天结束时, 某些地方必须解析.desktop文件,这只是一个“标准/默认”的问题。

在尝试测试这些文件时,我找到了最简单的方法来检查DM或会话管理器是否会按照我的预期在UI文件夹浏览器中打开周围的目录,然后双击打开它们。

如果您在命令行中: gvfs-open .gnome-open . 将在配置的文件夹浏览器中打开它。

sed的东西不会反映DM的行为,包括像逃脱和引用这样的东西,你真的不想要替代行为。 它不是命令行,但它确实validation了事情。 我还发现设置Terminal=true对调试很有用。

这个SO答案是我明确表示的:不要尝试执行桌面文件,执行桌面文件中指向的文件。

例如,执行/home/jsmith/Desktop/x11vnc.sh

 Exec=/home/jsmith/Desktop/x11vnc.sh 

我从上面的Carlo的 回答中获取了脚本,并试图改进它以供我自己的桌面使用。

此版本的脚本将允许您运行任何应用程序,就像您在HUD上输入它一样,只要它可能是第一个结果。 它还允许您传递不支持URI的.desktop文件的文件参数。

 #!/usr/bin/env python from gi.repository import Gio from argparse import ArgumentParser import sys, os def find_app(search_string): for group in Gio.DesktopAppInfo.search(search_string): for entry in group: try: return Gio.DesktopAppInfo.new(entry) except: pass return None def main(args): launcher = None if os.path.isfile(args.appName): try: # If it's a file, do that first. launcher = Gio.DesktopAppInfo.new_from_filename(args.appName) except TypeError: print "'" + args.appName + "' is not a .desktop file" sys.exit(-1) # If it's a .desktop file in the DB, try using that if launcher is None and args.appName.endswith('.desktop'): try: launcher = Gio.DesktopAppInfo.new(args.appName) except TypeError: pass if launcher is None: # Search for the app by the text given launcher = find_app(args.appName) if launcher is None: print "No app named " + args.appName + " could be found" sys.exit(-1) if (launcher.supports_uris()): launcher.launch_uris(args.uris, None) elif (launcher.supports_files()): launcher.launch(list({ Gio.File.parse_name(x) for x in args.uris }), None) else : launcher.launch() if __name__ == "__main__": argParser = ArgumentParser(description="Launch a .desktop file or application") argParser.add_argument("appName", help="the name of any application, a desktop file's basename, or a concrete path to a desktop file", action='store' ) argParser.add_argument("uris", nargs='*', help="Files or URIs to pass to the application" ) args = argParser.parse_args() main(args) 

确保桌面文件指向的脚本也是可执行的。

如果仍然不起作用。 通过更改Terminal=true使终端中的桌面​​文件可运行,并将其放在bash脚本中。 运行脚本以捕获错误输出。 更正错误时更改回来。

Hamish的答案很棒,但我想建议一个更简单的替代方案,涉及更少的管道:

$(awk -F= '/^Exec/||/^TryExec/ {print $2;exit}' /usr/share/applications/firefox.desktop)

在这种情况下, awk搜索以Exec开头的行,然后我们只需在该行之后打印字段,使用for循环和=我们打印字段2,即在该字段之后的任何内容。 命令末尾的大括号$(...)是参数替换,因此shell将执行awk命令返回的任何内容; 在这种情况下,它返回Exec=之后的实际命令。

在一些罕见的情况下,可能有多个=符号,这仍然是可能的。 为此,我建议

 $(awk -F= '/^Exec/||/^TryExec/ {for(i=2;i<=NF;i++) print $i;exit}' /usr/share/applications/firefox.desktop)