如果脚本崩溃了,可以再次运行吗?

如果脚本崩溃了,可以再次运行吗?

我这里的概念/语言可能完全错误,所以如果我的想法是错误的,请随时指出我正确的方向。

我使用一款不再受支持的家庭流媒体应用,名为 StreamBaby。它旨在通过 LAN 向较旧的 TiVo DVR 进行流媒体传输,我非常喜欢它,所以仍然每天使用它。它是 Java,执行时会打开一个终端(我在 macOS 上运行它)并运行一个简单的脚本,然后启动 Java,我想它应该会执行它正在执行的操作。

不幸的是,该应用程序几乎每天都会在某个时候崩溃(我在终端中看到异常),我不得不从终端关闭它并重新启动它。这些崩溃没有规律,我猜它只是有一些错误。我希望发生的事情是,如果它崩溃,那么脚本就会终止并重新启动,因为无论如何我手动使用它所做的就是这些。主要原因是我的妻子经常通过这个看她的节目,当它崩溃时,她不得不要求我“修复它”,而我并不总是有空这样做。希望它只是重新启动。

有没有办法通过终端中包含的批处理脚本执行此操作?该脚本是:

@echo off
set LAUNCHDIR="%CD%"
pushd "%~dp0\native"
java -Djava.net.preferIPv4Stack=true -Xmx256m -Xmx256m -jar "%~dp0/jbin/streambaby.jar" %1 %2 %3 %4 %5 %6 %7 %8 
echo Exited.
pause
popd

或者这是否需要 Java 应用程序本身具有这种异常处理?

编辑:这是崩溃时的错误示例:

WARNING: run() exception 
java.io.IOException: No route to host (sendto failed)
    at java.net.PlainDatagramSocketImpl.send(Native Method)
    at java.net.DatagramSocket.send(DatagramSocket.java:693)
    at javax.jmdns.impl.JmDNSImpl.send(JmDNSImpl.java:1130)
    at javax.jmdns.impl.tasks.Responder.run(Responder.java:279)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)

我不知道它是否总是相同的,但它始终是run() exception

答案1

每种脚本语言都支持的最简单的解决方案是循环。只需一直重复调用即可java

@echo off
set LAUNCHDIR="%CD%"
pushd "%~dp0\native"
:start
java -Djava.net.preferIPv4Stack=true -Xmx256m -Xmx256m -jar "%~dp0/jbin/streambaby.jar" %1 %2 %3 %4 %5 %6 %7 %8 
echo Exited.
goto start
pause
popd

更复杂的解决方案可能涉及将应用程序作为服务运行。如果服务崩溃,服务管理器可以简单地重新启动服务。在 Linux 上,这相对容易,但在 Windows 上,程序必须符合特殊接口。幸运的是,有国家安全监测中心,一个服务包装工具。它可用于将此 Java 应用程序作为 Windows 服务运行。

当然,如果您对手动启动媒体服务器并拥有控制台窗口感到满意,那么也可以。

答案2

有多种可能的方法:

方法 1:

@DanielB 确实给出了最简单的答案。在脚本周围添加一个循环。

方法 2:

更优雅的方法是制作一个 launchd .plist 文件并将其放入(而不是链接进去)/Library/LaunchDaemons

基本.plist形式如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC -//Apple Computer//DTD PLIST 1.0//EN
http://www.apple.com/DTDs/PropertyList-1.0.dtd >
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.jvc.streambaby</string>
    <key>ProgramArguments</key>
    <array>
         <string>/path/to/your/script</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardErrorPath</key>
    <string>/dev/null</string>
    <key>StandardOutPath</key>
    <string>/dev/null</string>
</dict>
</plist>

当然,sudo launchctl load -w /Library/LaunchDaemons/com.jvc.streambaby.plist如果您已将 .plist 文件命名为,则可以使用 来加载 plist !com.jvc.streambaby.plist

注意:/dev/null如果您需要将错误和输出流保存为日志,请更改为某些真实文件路径。

方法 3:

最令人厌恶和直观的方法是使用 Automator 来执行脚本。其中肯定有一种方法可以重新启动脚本。但是,既然可以编写脚本,谁还会使用 GUI 呢!

可能还有其他方法,但是我不是 Apple 用户,所以不知道。

方法 4:

这是一个小脚本:

一定要改变变量,特别是streambaby根目录,并用python3运行它(在linux上使用Python 3.9.x制作)。

#!/usr/bin/env python3


# set the path to the streambaby directory
# do not add a trailing slash (/)
streambaby_home = "/home/<username>/streambaby"


import subprocess
import signal
import sys
import os

from pathlib import Path


# variable to keep track if the app should restart
# True by default
should_run = True

# everything should be made visible immediately
os.environ["PYTHONUNBUFFERED"] = "1"

# copy the current environment
proc_env = os.environ.copy()

# is this really necessary, but was in the batch file so....
proc_env["LAUNCHDIR"] = str(Path.home())

# split the whole command into a list

exec_cmd = [
    'java',
    '-Djava.net.preferIPv4Stack=true',
    '-Xmx256m', # don't know why this argument is added twice
    '-Xmx256m',
    '-jar',
    '{0}/jbin/streambaby.jar'.format(streambaby_home)
    ]

# put the subprocess execution in a function for cleanliness
# return the process handle
def runapp(cmd, env):
    return subprocess.Popen(
        cmd,                        # the command to execute
        env=env,                    # setup the environment variables
        shell=False,                # not using a shell, change to True if doesn't work
        stdout = subprocess.PIPE,   # pipe the console output so we can read it
        stderr = subprocess.STDOUT, # somethings pop up on stderr, we send them to stdout anyway
        start_new_session=True      # using a session makes the whole set of processes easier to kill
                                    # works only on *nix type systems
        )

# now we start the program in a loop and poll() the hell out of it
while should_run:
    proc = runapp(exec_cmd, proc_env)
    while True:
        stdout_string = str(proc.stdout.readline())
        if proc.poll() is not None: # poll returns None while the process is executing
            break
        if stdout_string != '':
            print(stdout_string) # if someone wants to see the output anyway
            if "run() exception" in stdout_string.strip(): # only checking for those 2 words
                # we have hit a snag; kill it
                # ...(evil laugh)...
                # SIGKILL is my favorite type of signal
                # sounds badass
                os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
            elif " Could not create the Java Virtual Machine" in stdout_string:
                # if the Java VM fails to start, there is no point in trying anymore
                should_run = False

    # get the return code
    return_code = proc.poll()

    print("The process exited with exit_code ( {0} )".format(return_code))

    # a normal process which terminates properly should return 0
    # I think we may need to change this
    # don't know what streambaby should be returning
    if return_code == 0 and should_run: 
        # if the process exits normally, don't restart the process
        should_run = False

相关内容