正确的方式
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}}'
前述命令将:
- 查找以Exec =开头的行
- 删除执行=
- 删除所有Exec的变量(例如
%f
,%u
,%U
)。可以按照规范的意图用位置参数替换这些参数,但是这样做会大大增加问题的复杂性。请参阅最新的桌面条目规范。
- 执行命令
- 立即使用适当的退出代码退出(以免执行多条Exec行)
请注意,此AWK脚本解决了一些其他情况可能无法解决的极端情况。具体来说,此命令将删除多个Exec变量(注意不要以其他方式删除%符号),将仅执行一个Exec行命令,即使Exec行命令包含一个或多个等号(例如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 && " 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的使用是可选的。另外,不会执行任何错误检查,因此,如果要使脚本具有持久性,您将要确保启动器存在并且可读(在使用前)。
exec
失败的原因是因为exec用您指定的进程替换了当前正在运行的进程,所以您所做的就是尝试通过将桌面作为已编译的二进制文件运行来替换shell。之所以不能,sudo exec
是因为它是内置的Shell,而不是二进制命令。