在终端中运行.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}}'
上述命令将:
- 找到以Exec =开头的行
- 删除Exec =
- 删除任何Exec变量(例如
%f
,%u
,%U
)。 可以根据规范的意图用位置参数替换它们,但这样做会增加问题的复杂性。 查看最新的桌面条目规范 。 - 执行命令
- 立即退出并使用适当的退出代码(以便不执行多个Exec行)
请注意,此AWK脚本解决了一些边缘情况,这些边缘情况可能会或可能不会被其他一些答案正确解决。 具体来说,此命令删除多个Exec变量(注意不要以其他方式删除%符号),只执行单个Exec行命令,并且即使Exec行命令包含一个或多个等号(例如script.py --profile=name
),它也将按预期运行script.py --profile=name
)。
其他几点需要注意……根据规范, TryExec是:
磁盘上可执行文件的路径,用于确定程序是否实际安装。 如果路径不是绝对路径,则在$ PATH环境变量中查找该文件。 如果文件不存在或者文件不可执行,则可以忽略该条目(例如,不在菜单中使用)。
考虑到这一点,执行它的价值是没有意义的。
其他一些问题是Path和Terminal 。 Path包含运行程序的工作目录.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)