从终端启动GUI程序的优雅而高效的方式,而无需发送垃圾邮件(Bash或任何Posix shell)



每隔一段时间,我就必须从终端会话中启动一个GUI程序来做一些事情。它通常是Chrome显示一些HTML文件的一些相似的任务。然而,这些程序到处都是警告,实际上写任何东西都可能变得荒谬,所以我一直想将stderr/stdout重定向到/dev/null

虽然($PROGRAM &) &>/dev/null看起来还可以,但我决定为它创建一个简单的Bash函数,这样我就不必每次都重复了。

所以现在我的解决方案是这样的:

#
# silly little function
#
gui ()
{
if [ $# -gt 0 ] ; then
($@ &) &>/dev/null
else
echo "missing argument"
fi
}
#
# silly little example
#
alias google-chrome='gui google-chrome'

所以我想知道的是:

  • 有没有一种方法可以不用没完没了的别名列表,仍然很快
  • 是否有不同的策略来实现这一点
  • 其他外壳是否提供不同的解决方案

在提出这些问题时,我想指出的是,您的策略和解决方案可能与我的有很大偏差。将输出重定向到/dev/null并进行混叠是我所知道的唯一方法,但可能有完全不同的方法更有效。

因此这个问题:-)

正如其他人在评论中指出的那样,我认为真正的问题是如何在命令行上区分gui和非gui应用程序。因此,我能想到的最干净的方法是把这部分放在你的脚本中:

#!/bin/bash
if [ $# -gt 0 ] ; then
($@ &) &>/dev/null
else
echo "missing argument"
fi

放入一个名为gui的文件中,然后chmod +x并将其放入~/bin/中(确保~/bin$PATH中)。现在,您可以使用启动gui应用程序

`gui google-chrome`

提示。

或者,您可以执行上述操作,然后使用bind:

bind 'RETURN: "e[1~gui e[4~n"'

这将允许你只做:

google-chrome

在提示上,它会自动将gui附加到google-chrome之前

或者,您可以使用将上述操作绑定到F12,而不是RETURN

bind '"e[24~": "e[1~gui e[4~n"'

将您想要使用gui启动的内容与非gui启动的内容区分开来。

更多关于绑定的讨论。

这些替代方案为您提供了一种摆脱无休止别名的方法;混合:

  • gui放入~/bin/中,以及
  • 如上所示,绑定使用guiF12

似乎是最理想的(尽管很粗糙)解决方案。


更新-@Enno Weichert的结果解决方案:

正在完善此解决方案。。。

这将处理别名(尽管有点古怪)和不同的转义编码(以更实用而非详尽的方式)。

将此放入$(HOME)/bin/quiet

#!/bin/bash -i
if [ $# -gt 0 ] ; then
# Expand if $1 is an alias
if [ $(alias -p | awk -F "[ =]" '{print $2}' | grep -x $1) > 0 ] ; then
set -- $(alias $1 | awk -F "['']" '{print $2}') "${@:2}"
fi
($@ &) &>/dev/null
else
echo "missing argument"
fi

$(HOME)/.inputrc

#
# Bind prepend `quiet ` to [ALT][RETURN]
#
# The condition is of limited use actually but serves to seperate
# TTY instances from Gnome Terminal instances for me.
# There might very well be other VT emulators that ID as `xterm`
# but use totally different escape codes!
#
$if $term=xterm
"eC-j": "eOHquiet eOFn"
$else
"eC-m": "e[1~quiet e[4~n"
$endif

虽然这是一个丑陋的破解,但我有时会使用nohup来满足类似的需求。它的副作用是重定向命令的输出,并使程序独立于终端会话。

对于在桌面环境中运行GUI程序的情况,资源泄漏的风险很小,因为该程序无论如何都会以窗口管理器会话结束。尽管如此,还是应该考虑到这一点。

还可以选择打开另一个终端会话。例如,在Gnome中,您可以使用gnome-terminal --comand 'yourapp'
但这将导致打开许多无用的终端窗口。

首先,从终端启动GUI应用程序是个好主意,因为这样可以减少鼠标的使用。它在选项和参数方面更快、更方便。例如,以浏览器为例。假设您在剪贴板中已准备好粘贴URL。只需键入ice(用于Iceweeel),然后点击Shift Insert(用于粘贴)和Enter。将其与单击图标(可能在菜单中)进行比较,等待窗口加载(如果有启动页面,情况会更糟),然后单击URL栏(或点击Ctrl-L),然后点击Ctrl-V。。。所以我理解你希望这能奏效。

但是,我不认为这需要一个"无限"的别名和函数列表。你真的在使用那么多GUI应用程序吗?即便如此,别名也是单行的——对于处理参数来说可能更实用的函数,可能是1-5行(稀疏)代码。而且,不要觉得你需要一劳永逸地设置它们——在需要的时候,一个接一个地设置它们。不久,你就会拥有所有的。

此外,如果您有一个选项卡式终端,如urxvt(有一个Perl扩展),您将受益于从"GUI:s"移动到"CLI:s":对于下载,有rtcurrent;对于IRC,irssi;代替XEmacs(或emacs)、emacs -nw;有一个到vlc的CLI接口,用于流式传输音乐;对于邮件,Emacs的rmail;等等。!搜寻:)

(我遇到的唯一一个可悲的例外是浏览器,我认为这是一个失败的原因。从技术角度来看,Lynx、W3M等可能很好,但这并不总是重要的,因为现代网页在设计时根本没有考虑到这些纯文本浏览器。老实说,这些网页中的很多在这些浏览器中看起来都不太清晰。)

提示:为了充分利用选项卡式终端,您希望"更改选项卡"快捷键"关闭"(例如,Alt-J表示上一个选项卡,Alt-K表示下一个选项卡),而不是使您能够到达的箭头键。

最后,有一个解决方案可以解决这个问题,那就是在~/.xinitrc(包括终端模拟器)中启动"GUI:s"作为后台进程(使用&)。不是很灵活,但非常适合你每次使用电脑时经常使用的东西。

好的,所以我一直认为一个函数就足够了。没有别名,没有抨击,没有废话。但在我看来,在不影响常规使用(如扩展和完成)的情况下,唯一的方法是将函数放在命令的末尾。这并不像人们最初想象的那么容易。

首先,我考虑了一个附加在末尾的函数,在该函数中,我会调用printf "%b" "u",以切断当前线路,插入另一个printf,将其粘贴回,并在开头引用一两句话,然后在末尾做一些不需要的事情。很抱歉,我不知道该怎么做。即使我这样做了,我也不希望这种方法有任何真正的可靠性/可移植性,因为shell解释escape序列的方式各不相同,更不用说它们运行的终端模拟器了。也许stty可以提供一种沿着这些路线前进的方法,但如果是这样,你在这里就找不到了。。。现在,无论如何。

相反,我最终采取了将当前命令的/proc/{PID}/cmdline复制到一个变量的方法,然后(可耻地)完全杀死它,最后按照我的意愿包装它。从好的方面来说,这很容易做到,很快就完成了(尽管我可以想象以任何一种方式争论它的"效率"),而且似乎大部分都能工作,无论原始输入是别名、变量还是函数等。我相信它也是POSIX可移植的(尽管我记不清是否需要通过POSIX的名称指定kill SIGNALS),这绝对不是胡说八道。

另一方面,它的优雅当然还有很多不足之处,尽管它可能不值得担心,但它确实完全浪费了一个PID.,而且它并没有完全阻止的shell垃圾邮件;也就是说,在我的shell中,我已经使用set启用了后台jobs报告,因此,当第一次运行时,shell善意地通知我,我刚刚打开并浪费了两行PID。此外,因为我复制了../cmdline,而不是直接与0文件描述符接口,所以我预计管道和;等会有问题。这一点我很快就能解决,而且很可能会解决。

我会解决这个问题,也就是说,如果我找不到一种方法来使用SIGTSTP+SIGCONT,我怀疑可以这样做,首先在子shell外暂停进程,然后在重定向子shell的输出后在一个内继续。由于我还没有发现的原因,这似乎不可靠,但我认为它很有希望。也许nohuptrap(实际上是rehup)是需要的,但我也不确定如何将它们组合在一起。。。

顺便说一句,事不宜迟,我的半中止的向后gui启动器:

% _G() { ( 
_gui_cmd="$(tr '' ' ' </proc/"$!"/cmdline )" ; 
kill -9 "$!" ; 
exec eval "${_gui_cmd} &" ) 
&>/dev/null 2&>1 
}
% google-chrome-beta --disk-cache-dir="/tmp/cache" --disk-cache-size=100000000 &_G                                                   
[1] 2674
[1]  + 2674 killed     google-chrome-beta --disk-cache-dir="/tmp/cache" --disk-cache-   
%

因此,如果试图进行类似操作,可能会遇到的另一个问题是shell假设的扩展顺序。如果没有像我这样的技巧,在前面的命令将函数作为参数之前,你肯定会在扩展函数时遇到一些严重的困难。我相信,通过简单地将_G函数调用绑定到原始命令的&后台指令,我已经在没有不必要的冗余的情况下防止了这种情况。只需将&_G添加到要运行的命令的尾部,祝你好运。

-Mike

第页。S.好吧,写下最后一句话让我想到tee可以做些什么。

最新更新