시작시 최소화 된 프로그램을 어떻게 실행할 수 있습니까?


19

전보를 실행하고 시작 앱에 추가했습니다. 요점은 최소화해야한다는 것입니다. 어떤 명령?


Telegram을 시작하는 명령은 무엇이며 응용 프로그램이 시작된 직후의 창 이름은 무엇입니까?
Jacob Vlijm

내가 사용한 명령은 앱의 경로 일 뿐이며 창 이름은 Telegram Desktop
Hossein Soltanloo

안녕하세요 Hossien, 창 제목 대신 pid를 사용하는 것을 선호하는 경우를 대비하여 내 대답을 편집했습니다.
Jacob Vlijm

트윗 담아 가기 매우 효율적이고 유용합니다! 그러나 첫 번째 방법은 가변 창 이름 사례에서 완벽하게 작동합니다. 잘 했어!
Hossein Soltanloo

1
@SumeetDeshmukh 당신은 엄청나게 친절하고 관대 한 사람입니다. 정말!
Jacob Vlijm

답변:


29

응용 프로그램 시작 최소화

응용 프로그램을 최소화 된 방식으로 시작하려면 두 가지 명령이 필요합니다.

  • 응용 프로그램 시작
  • 창을 최소화

따라서 명령 또는 스크립트는 "스마트"해야합니다. 두 번째 명령은 응용 프로그램 창이 실제로 나타날 때까지 기다려야합니다.

응용 프로그램을 시작하기위한 일반적인 솔루션 최소화

아래 스크립트는이를 수행하며 응용 프로그램을 최소화 된 방식으로 시작하는 일반적인 솔루션으로 사용할 수 있습니다. 다음 구문으로 실행하십시오.

<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

그때:

  1. 스크립트를 빈 파일로 복사하여 다른 이름으로 저장하십시오. startup_minimizd.py
  2. gedit다음 명령을 사용 하여 스크립트를 테스트하십시오 .

    python3 /path/to/startup_minimizd.py gedit gedit
    
  3. 모두 제대로 작동하면 (응용 프로그램에 대한) 명령을 Startup Applications

설명

  • 스크립트는 첫 번째 인수로 제공 한 명령을 실행하여 응용 프로그램을 시작합니다.
  • 그런 다음 스크립트는 창 목록 ( wmctrl)을 사용하여 두 번째 인수의 이름을 딴 창을 확인합니다.
  • 창이 표시되면 xdotool 어떤 이유로 창이 표시되지 않는 경우 무한 루프를 방지하기 위해 스크립트는 창이 표시되는 시간을 30 초로 제한합니다.

노트

스크립트 외부에서 인수를 사용하여 스크립트를 실행하므로 한 번에 여러 응용 프로그램에 스크립트를 사용할 수 있습니다.


편집하다

pid로 창을 인식

창 제목이 확실하지 않거나 가변적이거나 창 이름에 이름이 충돌 할 위험이있는 경우를 사용하는 pid것이보다 안정적인 방법입니다.

아래 스크립트는 두 가지 출력 모두에서 wmctrl -lp와 같이 응용 프로그램의 pid 사용을 기반으로합니다.ps -ef .

설정은 거의 동일하지만이 버전에서는 창 제목이 필요하지 않으므로 실행 명령은 다음과 같습니다.

python3 /path/to/startup_minimizd.py <command_to_run_application>

그냥 첫 번째 스크립트처럼이 필요로 모두 wmctrlxdotool

스크립트

#!/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 용 특정 버전의 스크립트를 EDIT2

주석에서 요청한대로, 버전 아래에서, 특히 STEAM 시작을 위해 만들어진 최소화.

Steam 용으로 왜 특정 버전입니까?

그것은 밝혀 Steam"정상적인"응용 프로그램에서 매우 다른 동작합니다을 :

  • 그것은 하나의 pid를 Steam실행하지는 않지만 (내 테스트에서) 8 이상 은 아닙니다 !
  • Steam시작 두 개 이상의 창 (하나의 스플래시 모양 창)으로 시작되지만 때때로 추가 메시지 창이 나타납니다.
  • Steam의 Windows pid 0에는 스크립트의 문제입니다.
  • 주 창을 만든 후 1 초 정도 지나면 창이 두 번째로 올라가므로 단일 최소화가 수행되지 않습니다.

이 예외적 인 동작은 Steam아래에 추가 된 특수 버전의 스크립트 를 요청합니다. 스크립트가 시작되고 Steam12 초 동안 해당의 모든 새 창을 계속 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을 반환 하려고하지는 않습니다 . 실패한 것이 무엇인지 알 수 있도록 실패하는 것이 좋습니다. 그렇지 않으면 모든 종류의 다른 원인으로 인해 중단 될 수 있으며 광고되지 않은 상태로 전달됩니다.
fedorqui

1
@fedorqui 좋은 하나, 두 가지 예외가 발생할 수 있습니다 : subprocess.CalledProcesError (버그의 결과로 wmctrl) 및 IndexError(일반 예외)는 1 분 안에 편집됩니다 :). 언급 해 주셔서 감사합니다
Jacob Vlijm

@HosseinSoltanloo 스크립트를 실행하는 명령은 정확히 무엇입니까?
Jacob Vlijm

@JacobVlijm이 스크립트는 제대로 작동하지만 해결할 수도있는 다른 문제가 있습니다. 읽지 않은 메시지가 있고 앱을 열 때마다 읽지 않은 메시지가 두 개 있으므로 스크립트 이름이 변경되지 않으므로 창 이름이 "Telegram (2)"와 같이 변경됩니다.
Hossein Soltanloo

2
@ JDHolland 나는 그것이 고칠 수 있다고 확신한다. 다음 며칠 안에 어딘가에 살펴볼 것입니다 :)
Jacob Vlijm

3

user72216 및 Sergey가 문제에 대한 일반적인 솔루션으로 제공하는 스크립트를 사용하는 것이 좋지만 때로는 시작하려는 응용 프로그램에 최소화하려는 스위치가 이미 있습니다.

다음은 해당하는 시작 프로그램 명령 문자열이 포함 된 몇 가지 예입니다.

  • 전보 (버전 0.7.10부터)에는 다음과 같은 -startintray옵션 이 있습니다.<path-to-Telegram>/Telegram -startintray
  • 스팀 -silent옵션 이 있습니다 :/usr/bin/steam %U -silent
  • 전송 --minimized옵션 이 있습니다 :/usr/bin/transmission-gtk --minimized

Unity에서는 이러한 응용 프로그램이 시작 프로그램의 아이콘이 아니라 상단 메뉴 표시 줄의 아이콘으로 최소화되기 시작하지만, 응용 프로그램 사용을 시작하면 일반 시작 아이콘이 계속 나타납니다. 다른 응용 프로그램은 다르게 작동 할 수 있습니다.


1

나는 야곱의 대본을 가지고 좀 더 보편적 인 대본을 만들기 위해 대본을 수정했습니다.

#!/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)

주요 차이점은 다음과 같습니다.

  • 프로그램은 프로세스의 그룹 ID (GID)를 설정합니다. 따라서 모든 자식 프로세스와 해당 창을 쉽게 찾을 수 있습니다
  • while 루프 대신 xdotool --sync 옵션이 사용됩니다
  • 스크립트는 프로그램에 인수를 전달할 수 있습니다

WAIT_TIME은 프로그램이 하위 프로세스를 분기 할 수 있도록 충분히 크게 설정해야합니다. 내 컴퓨터에서는 증기와 같은 큰 프로그램에 충분합니다. 필요한 경우 늘리십시오.

부가

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. 캘린더가 열려 있고 계속 실행됩니까?
Khurshid Alam

1
variable을 늘리려 고 시도 할 수 있습니다 WAIT_TIME. 약한 컴퓨터에는 40 초 지연을 사용합니다. 또한 다른 명령을 사용하여 앱을 최소화하므로 두 번째 스크립트를 사용해 볼 수 있습니다.
Sergey

1

프로그램이 트레이에 닫혀 있으면 시작시 프로그램 창을 최소화하지 않고 닫고 싶을 수 있습니다. 그러한 프로그램의 한 예는 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>


1
xdotoolWayland 설치에서는 제대로 작동하지 않을 수 있습니다 .
Videonauth

0

나는 단지 서핑을 하고이 질문을 보았으므로 운영 체제가 무엇인지 궁금합니다. 나에 관해서는 UBUNTU BUDGIE 18.04 LTS를 사용하고 있기 때문에이 운영 체제에서는 매우 간단합니다.

단순히 메뉴로 이동

메뉴에서 Budgie Desktop 설정으로 이동

데스크탑 설정에서 자동 시작으로 이동

"+"에서 2 가지 옵션을 제공합니다.

1. 응용 프로그램 추가

2. 명령 추가

응용 프로그램 추가 를 선택 하면 모든 응용 프로그램이 나열되고 원하는 응용 프로그램을 선택하고 컴퓨터를 시작할 때 시작되며 최소화됩니다.


0

나는 프로그램을 최소화하고 트레이에 닫아 두어야했으며 여기에 게시 된 모든 스크립트 (작동 한 스크립트는 일부 프로그램에서만 작동하고 다른 스크립트에서는 작동하지 않음)를 시도했습니다. 그래서 나는 훨씬 더 잘 작동하는 것을 코딩했습니다 (창이 거의 보이지 않고 트레이 아이콘 만 표시되며 기본 모양으로 보입니다). 내가 시도한 모든 프로그램에서 작동합니다. 그것은 야곱의 것을 근거로합니다. 이 스크립트를 사용하면 프로그램에 따라 인수를 추가해야 할 수도 있지만 (아래 참조) 항상 많은 프로그램을 사용하여 증기를 처리해야합니다.

용법:

  1. sudo apt-get install wmctrl xdotool
  2. 스크립트에 startup_closed.py실행 권한을 부여한 다음 실행python3 ./startup_closed.py -c <command to open program>
  3. 프로그램 트레이 아이콘이 표시되지 않거나 창이 표시되지 않으면 다음 인수 중 하나를 추가해야합니다. -splash또는 -hide시행 착오. 예를 들어 : python3 ./startup_closed.py -hide -c teamviewer또는python3 ./startup_closed.py -splash -c slack
  4. 더 많은 인수가 있지만 필요하지 않을 수도 있습니다. 또한 도움말에 인수가 필요한시기와 이유에 대한 자세한 정보가 있습니다../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")

0

나는 독점적으로에 의존하는 다소 우아한 솔루션을 제공했으며 Telegram과 같이 "최소화 시작" 인수가 xdotool없는 응용 프로그램에 매우 유용합니다 .

유일한 단점은 각 앱에 대해 솔루션을 수동으로 만들어야하지만 문제가 아니라고 가정하면 (예 : 로그인 한 후 화면을 오염시키지 않고 특정 응용 프로그램을 자동 시작하려는 경우) 훨씬 간단하고 간단합니다. .

실제 예

## 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원하는 정확한 창을 타겟팅 할 수있는 조합의 모든 가능성을 확인하십시오 . 때로는 까다로워지고 여러 가지 조건을 결합해야하지만 일단 업데이트가 완료되면 응용 프로그램이 변경되어 구현이 중단되지 않는 한 거의 실패하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.