将自身安装到另一个位置并从那里运行的脚本
Script that installs itself to another location and runs from there
我正在尝试创建一个可以做两件事的 Python 脚本:
- 运行 通常如果没有参数存在。
- 如果
install
作为参数传递,将自身安装到特定目录 (/tmp
),并且 运行 在后台安装的版本,与当前进程分离。
我尝试了 subprocess.run
、subprocess.Popen
与 shell
、close_fds
和其他选项的多种组合(甚至尝试过 nohup
),但是由于我不太了解进程生成的工作原理,因此我似乎没有使用正确的进程。
当我使用 install
参数时,我正在寻找的是看到 "Installing..." 就是这样,新进程应该是 运行ning 在后台分离,我的 shell 准备好了。但是我看到的是子进程仍然附加并且我的终端忙于输出 "运行ning..." 就在安装消息之后。
应该怎么做?
import subprocess
import sys
import time
import os
def installAndRun():
print('Installing...')
scriptPath = os.path.realpath(__file__)
scriptName = (__file__.split('/')[-1] if '/' in __file__ else __file__)
# Copy script to new location (installation)
subprocess.run(['cp', scriptPath, '/tmp'])
# Now run the installed script
subprocess.run(['python3', f'/tmp/{scriptName}'])
def run():
for _ in range(5):
print('Running...')
time.sleep(1)
if __name__=="__main__":
if 'install' in sys.argv:
installAndRun()
else:
run()
编辑:我刚刚意识到进程在这样调用时并没有结束。
似乎正确的组合是对 stdout
和 stderr
使用 Popen + subprocess.PIPE
。代码现在看起来像这样:
import subprocess
import sys
import time
import os
def installAndRun(scriptPath, scriptName):
print('Installing...')
# Copy script to new location (installation)
subprocess.run(['cp', scriptPath, '/tmp'])
# Now run the installed script
subprocess.Popen(['python3', f'/tmp/{scriptName}'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def run(scriptPath):
for _ in range(5):
print(f'Running... {scriptPath}')
time.sleep(1)
if __name__=="__main__":
scriptPath = os.path.realpath(__file__)
scriptName = (__file__.split('/')[-1] if '/' in __file__ else __file__)
if 'install' in sys.argv:
installAndRun(scriptPath, scriptName)
else:
run(scriptPath)
不要使用“cp”复制脚本,而是使用shutil.copy()。
而不是“python3”,使用 sys.executable 以使用与原始脚本相同的解释器启动脚本。
subprocess.Popen() 只要 child 进程不向 stdout 和 stderr 写入任何内容,并且不请求任何输出,则不使用任何其他方法。通常,除非未调用 communicate() 或 PIPE read/written 到,否则不会启动该过程。您必须使用 os.fork() 与 parent 分离(研究守护进程是如何制作的),然后使用:
p = subprocess.Popen([sys.executable, new_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
p.stdin.close() # If you do not need it
p.communicate()
或者不对 stdin、stderr 和 stdout 使用 subprocess.PIPE,并确保分叉时终端绑定到 child。在 os.fork() 之后,你可以用 parent 做你想做的,用 child 做你想做的。您可以将 child 绑定到您想要的任何终端或启动一个新的 shell 例如:
pid = os.fork()
if pid==0: # Code in this if block is the child
<code to change the terminal and appropriately point sys.stdout, sys.stderr and sys.stdin>
subprocess.Popen([os.getenv("SHELL"), "-c", sys.executable, new_path]).communicate()
请注意,如果需要,您可以使用 stdin、stderr 和 stdout 参数将 PIPE 指向 file-like objects。
要在 Windows 上分离,您可以使用 os.startfile() 或在线程中使用 subprocess.Popen(...).communicate() 。如果您然后 sys.exit() parent,child 应该保持打开状态。 (这就是它在 Windows XP 和 Python 2.x 上的工作方式,我没有尝试使用 Py3 或较新的 Win 版本)
我正在尝试创建一个可以做两件事的 Python 脚本:
- 运行 通常如果没有参数存在。
- 如果
install
作为参数传递,将自身安装到特定目录 (/tmp
),并且 运行 在后台安装的版本,与当前进程分离。
我尝试了 subprocess.run
、subprocess.Popen
与 shell
、close_fds
和其他选项的多种组合(甚至尝试过 nohup
),但是由于我不太了解进程生成的工作原理,因此我似乎没有使用正确的进程。
当我使用 install
参数时,我正在寻找的是看到 "Installing..." 就是这样,新进程应该是 运行ning 在后台分离,我的 shell 准备好了。但是我看到的是子进程仍然附加并且我的终端忙于输出 "运行ning..." 就在安装消息之后。
应该怎么做?
import subprocess
import sys
import time
import os
def installAndRun():
print('Installing...')
scriptPath = os.path.realpath(__file__)
scriptName = (__file__.split('/')[-1] if '/' in __file__ else __file__)
# Copy script to new location (installation)
subprocess.run(['cp', scriptPath, '/tmp'])
# Now run the installed script
subprocess.run(['python3', f'/tmp/{scriptName}'])
def run():
for _ in range(5):
print('Running...')
time.sleep(1)
if __name__=="__main__":
if 'install' in sys.argv:
installAndRun()
else:
run()
编辑:我刚刚意识到进程在这样调用时并没有结束。
似乎正确的组合是对 stdout
和 stderr
使用 Popen + subprocess.PIPE
。代码现在看起来像这样:
import subprocess
import sys
import time
import os
def installAndRun(scriptPath, scriptName):
print('Installing...')
# Copy script to new location (installation)
subprocess.run(['cp', scriptPath, '/tmp'])
# Now run the installed script
subprocess.Popen(['python3', f'/tmp/{scriptName}'],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def run(scriptPath):
for _ in range(5):
print(f'Running... {scriptPath}')
time.sleep(1)
if __name__=="__main__":
scriptPath = os.path.realpath(__file__)
scriptName = (__file__.split('/')[-1] if '/' in __file__ else __file__)
if 'install' in sys.argv:
installAndRun(scriptPath, scriptName)
else:
run(scriptPath)
不要使用“cp”复制脚本,而是使用shutil.copy()。
而不是“python3”,使用 sys.executable 以使用与原始脚本相同的解释器启动脚本。
subprocess.Popen() 只要 child 进程不向 stdout 和 stderr 写入任何内容,并且不请求任何输出,则不使用任何其他方法。通常,除非未调用 communicate() 或 PIPE read/written 到,否则不会启动该过程。您必须使用 os.fork() 与 parent 分离(研究守护进程是如何制作的),然后使用:
p = subprocess.Popen([sys.executable, new_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
p.stdin.close() # If you do not need it
p.communicate()
或者不对 stdin、stderr 和 stdout 使用 subprocess.PIPE,并确保分叉时终端绑定到 child。在 os.fork() 之后,你可以用 parent 做你想做的,用 child 做你想做的。您可以将 child 绑定到您想要的任何终端或启动一个新的 shell 例如:
pid = os.fork()
if pid==0: # Code in this if block is the child
<code to change the terminal and appropriately point sys.stdout, sys.stderr and sys.stdin>
subprocess.Popen([os.getenv("SHELL"), "-c", sys.executable, new_path]).communicate()
请注意,如果需要,您可以使用 stdin、stderr 和 stdout 参数将 PIPE 指向 file-like objects。
要在 Windows 上分离,您可以使用 os.startfile() 或在线程中使用 subprocess.Popen(...).communicate() 。如果您然后 sys.exit() parent,child 应该保持打开状态。 (这就是它在 Windows XP 和 Python 2.x 上的工作方式,我没有尝试使用 Py3 或较新的 Win 版本)