我只想运行Telegram,并将其添加到启动应用程序中。关键是我需要将其最小化。有命令吗?
我只想运行Telegram,并将其添加到启动应用程序中。关键是我需要将其最小化。有命令吗?
Answers:
以最小化的方式启动应用程序需要两个命令:
因此,命令或脚本必须是“智能”的。第二个命令应等待应用程序窗口实际出现。
下面的脚本可以执行此操作,并且可以用作以最小化方式启动应用程序的常规解决方案。只需使用以下语法运行它:
<script> <command_to_run_the_application> <window_name>
#!/usr/bin/env python3
import subprocess
import sys
import time
subprocess.Popen(["/bin/bash", "-c", sys.argv[1]])
windowname = sys.argv[2]
def read_wlist(w_name):
try:
l = subprocess.check_output(["wmctrl", "-l"]).decode("utf-8").splitlines()
return [w.split()[0] for w in l if w_name in w][0]
except (IndexError, subprocess.CalledProcessError):
return None
t = 0
while t < 30:
window = read_wlist(windowname)
time.sleep(0.1)
if window != None:
subprocess.Popen(["xdotool", "windowminimize", window])
break
time.sleep(1)
t += 1
该脚本同时需要wmctrl
和xdotool
:
sudo apt-get install wmctrl xdotool
然后:
startup_minimizd.py
使用gedit
以下命令测试脚本:
python3 /path/to/startup_minimizd.py gedit gedit
Startup Applications
wmctrl
),该窗口以第二个参数命名。xdotool
为了防止由于某种原因而可能不出现该窗口的无限循环,该脚本将窗口的显示时间限制为30秒。无需提及您可以一次将脚本用于多个应用程序,因为使用脚本外部的参数运行脚本即可。
如果窗口标题不确定或可变,或者窗口名称中存在名称冲突的风险,则使用pid
可以更可靠地使用。
下面的脚本是基于使用的应用程序的PID,如在两者的输出wmctrl -lp
和ps -ef
。
设置几乎相同,但是此版本不需要窗口标题,因此运行它的命令是:
python3 /path/to/startup_minimizd.py <command_to_run_application>
就像第一个脚本,它需要双方wmctrl
和xdotool
#!/usr/bin/env python3
import subprocess
import sys
import time
command = sys.argv[1]
command_check = command.split("/")[-1]
subprocess.Popen(["/bin/bash", "-c", command])
t = 1
while t < 30:
try:
w_list = [l.split() for l in subprocess.check_output(["wmctrl", "-lp"]).decode("utf-8").splitlines()]
proc = subprocess.check_output(["pgrep", "-f", command_check]).decode("utf-8").strip().split()
match = sum([[l[0] for l in w_list if p in l] for p in proc], [])
subprocess.Popen(["xdotool", "windowminimize", match[0]])
break
except (IndexError, subprocess.CalledProcessError):
pass
t += 1
time.sleep(1)
尽管通常第二个版本应该更可靠,但是如果应用程序是由包装脚本启动的,则命令的pid将不同于最终调用的应用程序。
在这种情况下,我建议使用第一个脚本。
根据评论中的要求,在专门用于启动STEAM的版本下面将其最小化。
事实证明,Steam
其行为与“常规”应用程序完全不同:
Steam
它不会运行一个 pid,但至少不超过(在我的测试中)八个!Steam
在启动时至少有两个窗口(一个类似飞溅的窗口)运行,但有时会出现一个附加的消息窗口。pid 0
,这在脚本中是一个问题。这种特殊的行为Steam
要求提供特殊版本的脚本,下面将添加该版本。脚本启动Steam
,并在12秒钟内监视相应窗口的所有新窗口WM_CLASS
,检查它们是否已最小化。如果没有,脚本将确保它们将是。
像原来的剧本,这个需求wmctrl
和xdotool
安装。
#!/usr/bin/env python3
import subprocess
import time
command = "steam"
subprocess.Popen(["/bin/bash", "-c", command])
def get(cmd):
return subprocess.check_output(cmd).decode("utf-8").strip()
t = 0
while t < 12:
try:
w_list = [l.split()[0] for l in get(["wmctrl", "-l"]).splitlines()]
for w in w_list:
data = get(["xprop", "-id", w])
if all(["Steam" in data, not "_NET_WM_STATE_HIDDEN" in data]):
subprocess.Popen(["xdotool", "windowminimize", w])
except (IndexError, subprocess.CalledProcessError):
pass
t += 1
time.sleep(1)
runsteam_minimized.py
通过以下命令运行它:
python3 /path/to/runsteam_minimized.py
except:
只返回None。最好让它失败,以便您查看失败的原因;否则,它可能会因各种不同的原因而中断,并且会在没有广告的情况下通过。
subprocess.CalledProcesError
由于错误wmctrl
)和IndexError
(正常异常)将在一分钟内编辑:)。感谢您的提及
最好将user72216和Sergey给出的脚本作为该问题的常规解决方案,但是有时您希望最小化启动的应用程序已经具有可以执行您想要的操作的开关。
以下是一些带有相应启动程序命令字符串的示例:
-startintray
选项:<path-to-Telegram>/Telegram -startintray
-silent
选项:/usr/bin/steam %U -silent
--minimized
选项:/usr/bin/transmission-gtk --minimized
在Unity中,这些应用程序开始时会以顶部菜单栏中的图标(而不是启动器上的图标)最小化,尽管在您开始使用该应用程序后仍会出现正常的启动图标。其他应用程序的行为可能有所不同。
我采用了Jacob的脚本,并对其进行了一些修改,以使其更具通用性。
#!/usr/bin/python
import os
import subprocess
import sys
import time
import signal
WAIT_TIME = 10
def check_exist(name):
return subprocess.Popen("which "+name,
shell=True,
stdout=subprocess.PIPE
).stdout.read().rstrip("-n")
def killpid(pidlist):
for pid in pidlist:
args = ["xdotool",
"search",
"--any",
"--pid",
pid,
"--name",
"notarealprogramname",
"windowunmap",
"--sync",
"%@"]
subprocess.Popen(args)
def killname(name):
args = ["xdotool",
"search",
"--any",
"--name",
"--class",
"--classname",
name,
"windowunmap",
"--sync",
"%@"]
subprocess.Popen(args)
sys.argv.pop(0)
if check_exist(sys.argv[0]) == "":
sys.exit(1)
if check_exist("xdotool") == "":
sys.stderr.write("xdotool is not installed\n")
sys.exit(1)
if check_exist("wmctrl") == "":
sys.stderr.write("wmctrl is not installed\n")
sys.exit(1)
try:
prog = subprocess.Popen(sys.argv, preexec_fn=os.setsid)
except OSError, e:
sys.exit(1)
time.sleep(WAIT_TIME)
idlist = subprocess.Popen("pgrep -g " + str(prog.pid),
shell=True,
stdout=subprocess.PIPE
).stdout.read().splitlines()
ps1 = os.fork()
if ps1 > 0:
ps2 = os.fork()
if ps1 == 0: # Child 1
os.setpgid(os.getpid(), os.getpid())
killpid(idlist)
sys.exit(0)
elif ps2 == 0: # Child 2
killname(os.path.basename(sys.argv[0]))
sys.exit(0)
elif ps1 > 0 and ps2 > 0: # Parent
time.sleep(WAIT_TIME)
os.killpg(os.getpgid(int(ps1)), signal.SIGTERM)
os.kill(ps2, signal.SIGTERM)
os.waitpid(ps1, 0)
os.waitpid(ps2, 0)
sys.exit(0)
else:
exit(1)
主要区别在于:
WAIT_TIME应该设置得足够大,以允许程序派生其子进程。在我的计算机上,足以应付Steam之类的大型程序。如果需要,增加它。
加成
xdotool
的选项windowunmap
在某些应用程序和任务栏程序(例如,Linux mint的任务栏)上可能很时髦,因此这是这些异常的脚本的替代版本。
#!/usr/bin/python
import os
import subprocess
import sys
import time
import signal
WAIT_TIME = 10
def check_exist(name):
return subprocess.Popen("which "+name,
shell=True,
stdout=subprocess.PIPE
).stdout.read().rstrip("-n")
def killpid(pidlist):
for pid in pidlist:
args = ["xdotool",
"search",
"--sync",
"--pid",
pid]
for i in subprocess.Popen(args,
stdout=subprocess.PIPE).\
stdout.read().splitlines():
if i != "":
subprocess.Popen("wmctrl -i -c " +
hex(int(i)), shell=True)
def killname(name):
args = ["xdotool",
"search",
"--sync",
"--any",
"--name",
"--class",
"--classname",
name]
for i in subprocess.Popen(args,
preexec_fn=os.setsid,
stdout=subprocess.PIPE)\
.stdout.read().splitlines():
if i != "":
subprocess.Popen("wmctrl -i -c " + hex(int(i)),
shell=True)
sys.argv.pop(0)
if check_exist(sys.argv[0]) == "":
sys.exit(1)
if check_exist("xdotool") == "":
sys.stderr.write("xdotool is not installed\n")
sys.exit(1)
if check_exist("wmctrl") == "":
sys.stderr.write("wmctrl is not installed\n")
sys.exit(1)
try:
prog = subprocess.Popen(sys.argv, preexec_fn=os.setsid)
except OSError, e:
sys.exit(1)
time.sleep(WAIT_TIME)
idlist = subprocess.Popen("pgrep -g " + str(prog.pid),
shell=True,
stdout=subprocess.PIPE
).stdout.read().splitlines()
ps1 = os.fork()
if ps1 > 0:
ps2 = os.fork()
if ps1 == 0: # Child 1
os.setpgid(os.getpid(), os.getpid())
killpid(idlist)
sys.exit(0)
elif ps2 == 0: # Child 2
killname(os.path.basename(sys.argv[0]))
sys.exit(0)
elif ps1 > 0 and ps2 > 0: # Parent
time.sleep(WAIT_TIME)
os.killpg(os.getpgid(int(ps1)), signal.SIGTERM)
os.kill(ps2, signal.SIGTERM)
os.waitpid(ps1, 0)
os.waitpid(ps2, 0)
sys.exit(0)
else:
exit(1)
startminimized
。然后我跑了startminimized gnome-calendar
。日历打开并继续运行?
WAIT_TIME
。对于较弱的计算机,我使用40秒的延迟。您也可以尝试第二个脚本,因为它使用其他命令来最小化应用程序。
如果程序正在关闭到托盘上,则实际上可能是要在启动时关闭程序窗口,而不是将其最小化。这种程序的一个例子是Viber。在这种情况下,可以使用以下脚本start_closed.sh
:
#!/bin/bash
# Check that there is only one input argument
if [[ $# -gt 1 ]]; then
echo "Usage: $0 <program-to-start>"
exit 1
fi
$1 & # Start program passed in first argument
pid=$! # Get PID of last started program
xdotool search --sync --pid $pid | # Wait for window with PID to appear...
xargs wmctrl -i -c # ...and close it
用法: <path-to-script> <program-to-start>
xdotool
在Wayland上的安装中无法正常工作。
我需要封闭的程序而不是最小化的程序,并且我尝试了这里发布的所有脚本,那些有效的脚本仅对某些程序有效,而对其他程序无效。因此,我编写了一个效果更好的代码(几乎看不到窗口出现,只有托盘图标看起来很自然),并且可以对我尝试过的所有程序都有效。它基于雅各的作品。使用此脚本,您可能需要根据程序添加一个参数(请参见下文),但对于许多程序而言,它始终对我有用,它也应该可以使用Steam。
用法:
sudo apt-get install wmctrl xdotool
startup_closed.py
授予其执行权限,然后执行python3 ./startup_closed.py -c <command to open program>
-splash
或-hide
,反复尝试。例如:python3 ./startup_closed.py -hide -c teamviewer
或python3 ./startup_closed.py -splash -c slack
./startup_closed.py --help
脚本:
#!/usr/bin/env python3
import subprocess
import sys
import time
import argparse
import random
parser = argparse.ArgumentParser(description='This script executes a command you specify and closes or hides the window/s that opens from it, leaving only the tray icon. Useful to "open closed to tray" a program. If the program does not have a tray icon then it just gets closed. There is no magic solution to achieve this that works for all the programs, so you may need to tweek a couple of arguments to make it work for your program, a couple of trial and error may be required with the arguments -splash and -hide, you probably will not need the others.')
parser.add_argument("-c", type=str, help="The command to open your program. This parameter is required.", required=True)
parser.add_argument("-splash", help="Does not close the first screen detected. Closes the second window detected. Use in programs that opens an independent splash screen. Otherwise the splash screen gets closed and the program cannot start.", action='store_true', default=False)
parser.add_argument("-hide", help="Hides instead of closing, for you is the same but some programs needs this for the tray icon to appear.", action='store_true', default=False)
parser.add_argument("-skip", type=int, default=0, help='Skips the ammount of windows specified. For example if you set -skip 2 then the first 2 windows that appear from the program will not be affected, use it in programs that opens multiple screens and not all must be closed. The -splash argument just increments by 1 this argument.', required=False)
parser.add_argument("-repeat", type=int, default=1, help='The amount of times the window will be closed or hidden. Default = 1. Use it for programs that opens multiple windows to be closed or hidden.', required=False)
parser.add_argument("-delay", type=float, default=10, help="Delay in seconds to wait before running the application, useful at boot to not choke the computer. Default = 10", required=False)
parser.add_argument("-speed", type=float, default=0.02, help="Delay in seconds to wait between closing attempts, multiple frequent attempts are required because the application may be still loading Default = 0.02", required=False)
args = parser.parse_args()
if args.delay > 0:
finalWaitTime = random.randint(args.delay, args.delay * 2);
print(str(args.delay) + " seconds of delay configured, will wait for: " + str(finalWaitTime))
time.sleep(finalWaitTime)
print("waiting finished, running the application command...")
command_check = args.c.split("/")[-1]
subprocess.Popen(["/bin/bash", "-c", args.c])
hasIndependentSplashScreen = args.splash
onlyHide = args.hide
skip = args.skip
repeatAmmount = args.repeat
speed = args.speed
actionsPerformed = 0
lastWindowId = 0
if hasIndependentSplashScreen:
skip += 1
while True:
try:
w_list = [l.split() for l in subprocess.check_output(["wmctrl", "-lp"]).decode("utf-8").splitlines()]
proc = subprocess.check_output(["pgrep", "-f", command_check]).decode("utf-8").strip().split()
match = sum([[l[0] for l in w_list if p in l] for p in proc], [])
if len(match) > 0:
windowId = match[0]
if windowId != lastWindowId:
if skip > 0:
skip -= 1
print("skipped window: " + windowId)
lastWindowId = windowId
else:
print("new window detected: " + windowId)
if onlyHide:
subprocess.Popen(["xdotool", "windowunmap", windowId])
print("window was hidden: " + windowId)
else:
subprocess.Popen(["xdotool", "key", windowId, "alt+F4"])
print("window was closed: " + windowId)
actionsPerformed += 1
lastWindowId = windowId
if actionsPerformed == repeatAmmount:
break
except (IndexError, subprocess.CalledProcessError):
break
time.sleep(speed)
print("finished")
我提供了一个非常优雅的解决方案,该解决方案仅依赖于xdotool
,并且对于没有“最小化启动”参数的应用程序(例如Telegram)非常有用。
唯一的缺点是必须为每个应用程序手动设计解决方案,但是假设这不是问题(例如:如果要自动启动某个应用程序,而又不允许它在登录后污染屏幕),这将更加简单明了。 。
## Starts Telegram and immediately closes it
xdotool search --sync --onlyvisible --name '^Telegram$' windowclose &
telegram-desktop &
## Starts WhatsApp and immediately closes it
xdotool search --sync --onlyvisible --name '(\([0-9]*\) ){0,1}(WhatsApp$|WhatsApp Web$)' windowclose &
whatsapp-nativefier &
乍一看,您可能会认为最好使用流程的PID或类进行匹配,但这实际上适得其反,因为您经常会为同一PID获得多个结果。例如,实际上正在等待通知的0x0窗口,系统托盘图标或任何其他“隐藏”窗口。
解决方案是设计一个xdotool命令,该命令始终仅返回一个唯一窗口。在我使用的两个示例中--name
,您都可以结合使用多个选择器--all
(例如:匹配给定的类名+类名+名称正则表达式)。通常,好的--name
正则表达式可以解决问题。
制作search
完条件后,只需生成一个带有参数和条件的xdotool实例(与外壳分离)--sync
,然后是windowclose
。之后运行您的应用程序:
xdotool search --sync [... myapp-match-conditions] windowclose &
my-app
找出
xdotool search --help
所有可能组合的可能性,您可以安排这些组合来定位所需的确切窗口。有时,它变得棘手,您必须结合多个条件,但是一旦完成,它就很少会失败(当然,除非有更新会更改应用程序并破坏您的实现)。