从 Python 执行 shell 命令并合并环境更改(没有子进程)?
From Python execute shell command and incorporate environment changes (without subprocess)?
我正在探索使用 iPython 作为 shell 替代需要良好日志记录和操作重现性的工作流程。
我有一些非 python 二进制程序和 bash shell 命令 运行 在我的常用工作流中操纵影响后续工作的环境变量。即当 运行 从 bash 时,环境发生变化。
如何将这些案例合并到 Python / iPython 交互式 shell 中并修改会话中的环境?
让我们关注最关键的案例。
从 bash,我会做:
> sysmanager initialize foo
其中 sysmanager
是一个函数:
> type sysmanager
sysmanager is a function
sysmanager ()
{
eval `/usr/bin/sysmanagercmd bash $*`
}
我不控制二进制文件 sysmanagercmd
,它通常会对环境变量进行重要的操作。使用 eval
内置意味着这些操作会影响 shell 进程向前发展——这对设计至关重要。
如何从 Python / iPython 调用此命令具有相同的效果? python 是否有等同于 bash 的 eval
内置非 python 命令的东西?
由于没有遇到任何内置功能来执行此操作,我编写了以下函数来实现广泛的意图。环境变量的修改和工作目录的改变反映在函数returns之后的pythonshell中。 shell 别名或函数的任何修改 不会 保留,但也可以通过增强此功能来完成。
#!/usr/bin/env python3
"""
Some functionality useful when working with IPython as a shell replacement.
"""
import subprocess
import tempfile
import os
def ShellEval(command_str):
"""
Evaluate the supplied command string in the system shell.
Operates like the shell eval command:
- Environment variable changes are pulled into the Python environment
- Changes in working directory remain in effect
"""
temp_stdout = tempfile.SpooledTemporaryFile()
temp_stderr = tempfile.SpooledTemporaryFile()
# in broader use this string insertion into the shell command should be given more security consideration
subprocess.call("""trap 'printf "\0`pwd`\0" 1>&2; env -0 1>&2' exit; %s"""%(command_str,), stdout=temp_stdout, stderr=temp_stderr, shell=True)
temp_stdout.seek(0)
temp_stderr.seek(0)
all_err_output = temp_stderr.read()
allByteStrings = all_err_output.split(b'\x00')
command_error_output = allByteStrings[0]
new_working_dir_str = allByteStrings[1].decode('utf-8') # some risk in assuming index 1. What if commands sent a null char to the output?
variables_to_ignore = ['SHLVL','COLUMNS', 'LINES','OPENSSL_NO_DEFAULT_ZLIB', '_']
newdict = dict([ tuple(bs.decode('utf-8').split('=',1)) for bs in allByteStrings[2:-1]])
for (varname,varvalue) in newdict.items():
if varname not in variables_to_ignore:
if varname not in os.environ:
#print("New Variable: %s=%s"%(varname,varvalue))
os.environ[varname] = varvalue
elif os.environ[varname] != varvalue:
#print("Updated Variable: %s=%s"%(varname,varvalue))
os.environ[varname] = varvalue
deletedVars = []
for oldvarname in os.environ.keys():
if oldvarname not in newdict.keys():
deletedVars.append(oldvarname)
for oldvarname in deletedVars:
#print("Deleted environment Variable: %s"%(oldvarname,))
del os.environ[oldvarname]
if os.getcwd() != os.path.normpath(new_working_dir_str):
#print("Working directory changed to %s"%(os.path.normpath(new_working_dir_str),))
os.chdir(new_working_dir_str)
# Display output of user's command_str. Standard output and error streams are not interleaved.
print(temp_stdout.read().decode('utf-8'))
print(command_error_output.decode('utf-8'))
我正在探索使用 iPython 作为 shell 替代需要良好日志记录和操作重现性的工作流程。
我有一些非 python 二进制程序和 bash shell 命令 运行 在我的常用工作流中操纵影响后续工作的环境变量。即当 运行 从 bash 时,环境发生变化。
如何将这些案例合并到 Python / iPython 交互式 shell 中并修改会话中的环境?
让我们关注最关键的案例。 从 bash,我会做:
> sysmanager initialize foo
其中 sysmanager
是一个函数:
> type sysmanager
sysmanager is a function
sysmanager ()
{
eval `/usr/bin/sysmanagercmd bash $*`
}
我不控制二进制文件 sysmanagercmd
,它通常会对环境变量进行重要的操作。使用 eval
内置意味着这些操作会影响 shell 进程向前发展——这对设计至关重要。
如何从 Python / iPython 调用此命令具有相同的效果? python 是否有等同于 bash 的 eval
内置非 python 命令的东西?
由于没有遇到任何内置功能来执行此操作,我编写了以下函数来实现广泛的意图。环境变量的修改和工作目录的改变反映在函数returns之后的pythonshell中。 shell 别名或函数的任何修改 不会 保留,但也可以通过增强此功能来完成。
#!/usr/bin/env python3
"""
Some functionality useful when working with IPython as a shell replacement.
"""
import subprocess
import tempfile
import os
def ShellEval(command_str):
"""
Evaluate the supplied command string in the system shell.
Operates like the shell eval command:
- Environment variable changes are pulled into the Python environment
- Changes in working directory remain in effect
"""
temp_stdout = tempfile.SpooledTemporaryFile()
temp_stderr = tempfile.SpooledTemporaryFile()
# in broader use this string insertion into the shell command should be given more security consideration
subprocess.call("""trap 'printf "\0`pwd`\0" 1>&2; env -0 1>&2' exit; %s"""%(command_str,), stdout=temp_stdout, stderr=temp_stderr, shell=True)
temp_stdout.seek(0)
temp_stderr.seek(0)
all_err_output = temp_stderr.read()
allByteStrings = all_err_output.split(b'\x00')
command_error_output = allByteStrings[0]
new_working_dir_str = allByteStrings[1].decode('utf-8') # some risk in assuming index 1. What if commands sent a null char to the output?
variables_to_ignore = ['SHLVL','COLUMNS', 'LINES','OPENSSL_NO_DEFAULT_ZLIB', '_']
newdict = dict([ tuple(bs.decode('utf-8').split('=',1)) for bs in allByteStrings[2:-1]])
for (varname,varvalue) in newdict.items():
if varname not in variables_to_ignore:
if varname not in os.environ:
#print("New Variable: %s=%s"%(varname,varvalue))
os.environ[varname] = varvalue
elif os.environ[varname] != varvalue:
#print("Updated Variable: %s=%s"%(varname,varvalue))
os.environ[varname] = varvalue
deletedVars = []
for oldvarname in os.environ.keys():
if oldvarname not in newdict.keys():
deletedVars.append(oldvarname)
for oldvarname in deletedVars:
#print("Deleted environment Variable: %s"%(oldvarname,))
del os.environ[oldvarname]
if os.getcwd() != os.path.normpath(new_working_dir_str):
#print("Working directory changed to %s"%(os.path.normpath(new_working_dir_str),))
os.chdir(new_working_dir_str)
# Display output of user's command_str. Standard output and error streams are not interleaved.
print(temp_stdout.read().decode('utf-8'))
print(command_error_output.decode('utf-8'))