在终端中运行.desktop文件


140

据我所知,.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从终端运行文件?


5
注意:exec失败的原因是因为exec用您指定的进程替换了当前正在运行的进程,所以您所做的就是尝试通过将桌面作为已编译的二进制文件运行来替换shell。之所以不能,sudo exec是因为它是内置的Shell,而不是二进制命令。
丹妮丝

有趣的是,我想知道为什么它导致选项卡关闭。
马拉巴巴

相关文件
2011年

我看到了,他们最终解析了.desktop文件。无论如何,谢谢您的链接。
enzotib

主持人:哎呀,我可能已经不小心
举了

Answers:


54

运行的命令包含在桌面文件中,其后跟有以下命令,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 -他们都不是相关的在命令行启动。


到目前为止,这提供了最佳结果,但有时会产生不良行为。每当“ Exec =”行具有%u或%i之类的参数时,就会发生这种情况。Bash尝试将该字符串作为常规参数传递。例如,doing grep '^Exec' firefox.desktop | sed 's/^Exec=//'使用加载www。%u.com的选项卡打开Firefox 。
马拉巴巴2010年

目前,我已经添加了一秒钟sed来删除所有参数。但是我认为可能会有更“自然”的方式来运行它。
马拉巴巴2010年

我用额外的sed更新了答案-我忘记了桌面文件可能包含参数。
Hamish Downer

您应在“ grep”之后的管道上添加“ tail -1”,因为“ Exec =“可以多次出现,然后,只能执行最后一次出现..
daisy 2012年

13
-1:这可能适用于简单.desktop文件,但会忽略类似这样的条目Path=TryExec=并且可能会影响执行。Exec=如果文件包含桌面操作(“快速列表”),它也会执行错误
MestreLion,2013年

85

答案应该是

xdg-open program_name.desktop

但是由于存在错误,它不再起作用。


7
哇,这仍然是一个bug,在xdg中有很多进展。 exo-open被列为解决方法,它也会打开gedit。:(
Drew

1
@RichardHolloway:gnome-openxdg-open,这是周围的其他方式!因此问题出在gvfs-open(后继者或gnome-open
MestreLion,

13
“不再有效”?从来没有!xdg-open通过mimetype关联工作,并且.desktop文件与文本编辑器关联,因为它们是文本的子类
MestreLion 2013年

13
这是如此愚蠢(以至于没有从终端运行桌面文件的明智方法)
Sam Watkins 2014年

2
在Arch Linux上为我工作,但也许是Ubuntu特有的错误。
维克多

75

使用任何最近支持的ubuntu都gtk-launch可以

gtk-launch <file>.desktop文件的名称(包含或不包含该.desktop部分)在哪里

所以gtk-launch foo打开foo.desktop

gtk-launch文件

.desktop应该位于/ usr / share / applications,/ usr / local / share / applications或〜/ .local / share / applications中

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


这就是方法,也可以在debian中工作。

gtk-launch firefox.desktop〜/ .local / share / applications /为我启动firefox,查看目录〜/ .local / share / applications /。好像您是正确的,Firefox应该不应该作为参数传递.desktop文件的目录。实际上,传递给gtk-launch的目录不应该用于查找包含.desktop文件的目录(实际上不是)
Croad Langshan

好极了!一个有效的答案!
艾丽西亚(Alicia)

这在Arch Linux中也对我有用。其他答案都不能令人满意,但是这个答案很好。:-)即使我正在运行KDE,它也可以工作。
MountainX

2
在Ubuntu 18.10上,没有适用于我的版本。每当它抱怨该应用程序不存在时,无论我是否位于桌面文件所在的文件夹中,是否包括.desktop扩展名,以及是否分别命名目录。
约瑟夫·加文

38

截至今天(12.10),该错误仍然存在。实际上,这取决于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

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

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

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

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

现在:

  • xdg-open something.desktop可以正常工作
  • #!/usr/bin/xdg-open 在可执行的桌面条目顶部的hashbang也可以工作

gvfs-open解决错误的时间将是无用的工作,但与此同时...


1
这比Hamish Downer的答案更好,因为它将正确处理命令中的多Exec=行和%参数。
Flimm 2013年

4
感谢您的代码-我使用的是Lucid,我只是将其另存为/usr/bin/xdg-openpy,并给了它chmod +x-而launcher.launch([],context)不是...None,context)(因为“ TypeError:参数1:必须是序列,而不是NoneType ”)。现在xdg-openpy app.desktop可以从命令行运行(双击时一切正常app.desktop),如果我尝试在终端中调用xdg-open并按Tab ,它可以提醒我。干杯!
sdaau

3
+1。这是不需要手动分析.desktop文件的唯一答案,因此这是最理智(也是最安全)的方法。还使用了Modern gi.repository而不是不推荐使用pygtk,太棒了!:)
MestreLion,

2
实际上,这是关于卡洛·佩莱格里尼的答案的问题。我是新人,如果有更好的放置方法,请纠正我。该脚本确实运行良好,但是我在Unity启动器上获得的图标不是.desktop文件中定义的图标,而是'Exec'ed命令的默认图标。有什么想法吗?
Ingo Leonhardt

1
@Noitidart写了最后一个答案,导致Mo做了一些Google,我发现。还没有检查出来,但可能会
有所

31

正确的方式

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)。

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

launch(){

    # Usage: launch PATH [URI...]

    # NOTE: The bulk of this function is executed in a subshell, i.e. `(..)`
    #       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. 删除执行=
  3. 删除所有Exec的变量(例如%f%u%U)。可以按照规范的意图用位置参数替换这些参数,但是这样做会大大增加问题的复杂性。请参阅最新的桌面条目规范
  4. 执行命令
  5. 立即使用适当的退出代码退出(以免执行多条Exec行)

请注意,此AWK脚本解决了一些其他情况可能无法解决的极端情况。具体来说,此命令将删除多个Exec变量(注意不要以其他方式删除%符号),将仅执行一个Exec行命令,即使Exec行命令包含一个或多个等号(例如script.py --profile=name),其行为也将与预期的一样。

只是其他一些警告...根据规范,TryExec是:

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

考虑到这一点,执行其价值没有任何意义。

其他一些问题是PathTerminalPath由运行程序的工作目录组成。Terminal是一个布尔值,指示程序是否在终端窗口中运行。所有这些都可以解决,但是由于已经有该规范的实现,因此没有必要重新发明轮子。如果您确实想实现Path,请记住它会system()产生一个子流程,因此您不能通过执行诸如这样的操作来更改工作目录system("cd \047" working_directory "\047"); system(command)。但是,您大概可以做类似的事情system("cd \047" working_directory "\047 && " command)。注意\ 047是单引号(因此命令在带空格的路径上不会中断)。

Python的替代品

我从Carlo的这里窃取了一个页面,后者建议创建一个Python脚本来利用gi模块。这是从外壳执行相同代码而无需创建文件和担心I / O的最小方法。

launch(){

# Usage: launch PATH [URI...]

python - "$@" <<EOF
import sys
from gi.repository import Gio
Gio.DesktopAppInfo.new_from_filename(sys.argv[1]).launch_uris(sys.argv[2:])
EOF

}

然后执行启动器功能,如下所示:

launch ./path/to/shortcut.desktop

请注意,URI的使用是可选的。另外,不会执行任何错误检查,因此,如果要使脚本具有持久性,您将要确保启动器存在并且可读(在使用前)。


但是awk命令很好。因此+1
AB

在这种情况下,什么是URI?我在任意文件夹中都有一个桌面文件。我如何直接对它运行gtk-launch而不必将其包装在其他脚本中呢?这太疯狂了。
约瑟夫·加文

awk如果命令具有两次转义的空格或反斜杠,则此解决方案将无法正常工作。它打破了这个:Exec=env WINEPREFIX="/path/to/.wine" wine c:\\\\windows\\\\command\\\\start.exe /Unix /path/to/.wine/dosdevices/c:/users/Public/Рабочий\\ стол/appname.lnkdex解决方案正常工作。
MarSoft

27

当OP并没有询问KDE时,对于正在运行KDE的任何人,都可以使用以下命令:

kioclient exec <path-to-desktop-file>

在Fedora上,它包含在kde-runtimerpm中。


5
已投票,因为它有效。只要安装了此程序,就完全不需要运行KDE。
basic6

有没有办法对此有标准?我已经编译了clementine播放器,该播放器存在一个错误,该错误仅通过.desktop文件(带有plasma-shell)启动时才会发生。而且我不知道如何输出日志。
Kwaadpepper 2015年

@Kwaadpepper,您可以使用.desktop文件启动外壳程序脚本,该脚本在内部运行命令并将输出重定向到文件。
拉曼

目前,在任何用于Arch Linux的KDE软件包中都找不到kioclient。但是,gtk-launch在我的KDE下工作。
MountainX

15

您可以使用dex

dex foo.desktop

2
恕我直言,这是正确的答案:一个工具,一次调用,仅将文件作为参数。我也在寻找测试手写.desktop文件的东西。它也可以创建.desktop文件,是的!:-)
Axel Beckert

完善!如果将使用dex gist.github.com/stuaxo/4169fc1342c496b7c8f7999188f2f242的 dex文件添加 到/ usr / share / applications /,则默认情况下您也可以在文件管理器中启动桌面文件而无需在gedit中打开它们。
斯图尔特·阿克森

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

如果安装了exo-utils,似乎在13.10版本中可以正常工作(例如Xubuntu就是这种情况)。


是的,刚刚在Ubuntu Studio 14.04中对其进行了测试。顺便说一句。xdg-open和gvfs-open也在那里工作。
jarno 2014年

同样适用于15
乔纳森(Jonathan)

在xfce中使用Debian。很棒!
king_julien

在Ubuntu 14.04中为我工作
Tyler Collier '18

8

哈米什答案的附录。

给定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}"代替shifting,但是它一直给我“不好的替代品” ...


我知道这是对Hamish答案的更恰当的评论,但是我是新用户,不允许发表评论。那好吧!
pabst 2012年

如果您享有声誉,那么最合适的操作实际上就是编辑该答案。
Flimm 2012年

提醒一下,任何人都可以建议编辑内容,甚至包括尚未登录的用户!没什么大不了的。
Flimm 2013年

1
这本身就是一个答案,很好。
布鲁诺·佩雷拉

你可以使用"${@:1}"的替代shift,但需要bash,而不是sh在你的#!家当。恕我直言,你原来的换挡方式更简单,更好
MestreLion

6

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

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

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

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

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


无论如何我都不是我想听到的内容
enzotib 2011年

6

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

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

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

归根结底,某些地方必须解析.desktop文件,这只是“标准/默认”程度的问题。


谢谢,有趣。作为“标准”,我指的是DE提供的并符合freedesktop.org的内容。
enzotib

支持awk而不是greps和seds 链。
jmtd

Upvote,尽管如果您有多Exec=行,则不起作用(或放置得更好-可以工作得太多):/
Boris Churzin

2

我从上述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)

1

当尝试测试这些文件时,我发现检查DM或会话管理器是否可以执行我期望的最简单方法是在UI文件夹浏览器中打开周围的目录,然后双击以打开它们。

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

sed不会反映DM的行为,包括诸如转义符之类的古怪的东西,并引用您真正不希望替代行为的地方。它不是命令行,但确实可以验证内容。我还发现设置Terminal=true对于调试很有用。


终端立即消失,使用Terminal = true时我没有时间看消息:s
gouessej 2015年

1

这样的答案对我来说很清楚:不要尝试执行桌面文件,而是执行桌面文件中指向的文件。

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

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

1

(从此处的其他答案中汇编)

根据您的系统以及系统上可能存在或可能不存在的各种错误,请尝试以下操作,直到其中一个起作用:

  1. xdg-open program_name.desktop
  2. exo-open program_name.desktop
  3. gtk-launch program_name.desktop
  4. kioclient exec program_name.desktop
  5. dex program_name.desktop

请注意,在Ubuntu系统上,您的“开始菜单”桌面启动器在中可用/usr/share/applications/

例如,为了显示以上命令中哪些命令在我的Ubuntu 14.04系统上起作用或不起作用,以下是对我的以下调用的结果:

  1. xdg-open /usr/share/applications/eclipse_for_cpp.desktop #由于错误而失败(尝试让我保存此.desktop文件)
  2. exo-open /usr/share/applications/eclipse_for_cpp.desktop #作品
  3. gtk-launch /usr/share/applications/eclipse_for_cpp.desktop #因“ gtk-launch:没有此类应用程序”而失败
  4. kioclient exec /usr/share/applications/eclipse_for_cpp.desktop #作品
  5. dex /usr/share/applications/eclipse_for_cpp.desktop#失败,sudo apt install dex并且无法找到包dex

0

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

如果仍然无法正常工作。通过更改使桌面文件在终端中可运行Terminal=true,并将其放在bash脚本中。运行脚本以捕获错误输出。纠正错误后,请改回来。


0

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)

尼斯,awkSerg =)
AB

有时会有一个TryExec,也许您应该检查您的第二个命令=)
AB

@AB Hehe,awk是我在文本处理方面的首选武器。此外,它的语法接近C。哦,已经添加了TryExec部分^ _ ^
Sergiy Kolodyazhnyy 2015年

您有我的+1 =)
AB

但是%f,该命令后面的%u%U或类似内容是什么?
AB
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.