如何使用 Python 检查字符串是否是有效的 shell 命令?
How to check if a string is a valid shell command using Python?
我正在制作一个程序,为 Windows 中的标准命令 shell 添加额外的功能。例如,键入 google
后跟关键字将打开一个新选项卡,其中 Google 搜索这些关键字等。只要输入不引用我创建的自定义函数,它就会被处理为使用 subprocess.call(rawCommand, shell=True)
.
的 shell 命令
既然我想预测我的输入何时不是有效命令并且 return 类似于 f"Invalid command: {rawCommand}"
,我应该怎么做?
到目前为止,我已经尝试 subprocess.call(rawCommand)
这也是 return 标准输出和退出代码。所以看起来像这样:
>>> from subprocess import call
>>> a, b = call("echo hello!", shell=1), call("xyz arg1 arg2", shell=1)
hello!
'xyz' is not recognized as an internal or external command,
operable program or batch file.
>>> a
0
>>> b
1
我只想接收该退出代码。关于如何执行此操作的任何想法?
有些 shell 具有 语法-检查模式(例如、bash -n
),但这是唯一的错误形式这与“尝试执行命令并查看会发生什么”是分开的。定义更大的 class 的“即时”错误是 fraught proposition:如果 echo hello; ./foo
无效,因为 foo
无法作为命令找到,那么 false && ./foo
,它永远不会尝试 运行,或者 cp /bin/ls foo; ./foo
,它可能会成功(或可能无法复制)? eval $(configure_shell); foo
可能会或可能不会操纵 PATH
以找到 foo
呢? foo || install_foo
可能会出现故障?
因此,预期 失败在任何意义上都是不可能的:您唯一真正的选择是 捕获 命令的 output/error(如评论中所述)并以一些有用的方式报告它们。
如果你有一天想要处理编码错误,取回你运行命令的结果,有一个超时或决定哪些退出代码不是 0 可能不会触发错误(我是看着你,java 运行时!),这里有一个完成这项工作的完整函数:
import os
from logging import getLogger
import subprocess
logger = getLogger()
def command_runner(command, valid_exit_codes=None, timeout=300, shell=False, encoding='utf-8',
windows_no_window=False, **kwargs):
"""
Whenever we can, we need to avoid shell=True in order to preseve better security
Runs system command, returns exit code and stdout/stderr output, and logs output on error
valid_exit_codes is a list of codes that don't trigger an error
windows_no_window will hide the command window (works with Microsoft Windows only)
Accepts subprocess.check_output arguments
"""
# Set default values for kwargs
errors = kwargs.pop('errors', 'backslashreplace') # Don't let encoding issues make you mad
universal_newlines = kwargs.pop('universal_newlines', False)
creationflags = kwargs.pop('creationflags', 0)
if windows_no_window:
creationflags = creationflags | subprocess.CREATE_NO_WINDOW
try:
# universal_newlines=True makes netstat command fail under windows
# timeout does not work under Python 2.7 with subprocess32 < 3.5
# decoder may be unicode_escape for dos commands or utf-8 for powershell
output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=shell,
timeout=timeout, universal_newlines=universal_newlines, encoding=encoding,
errors=errors, creationflags=creationflags, **kwargs)
except subprocess.CalledProcessError as exc:
exit_code = exc.returncode
try:
output = exc.output
except Exception:
output = "command_runner: Could not obtain output from command."
if exit_code in valid_exit_codes if valid_exit_codes is not None else [0]:
logger.debug('Command [%s] returned with exit code [%s]. Command output was:' % (command, exit_code))
if isinstance(output, str):
logger.debug(output)
return exc.returncode, output
else:
logger.error('Command [%s] failed with exit code [%s]. Command output was:' %
(command, exc.returncode))
logger.error(output)
return exc.returncode, output
# OSError if not a valid executable
except (OSError, IOError) as exc:
logger.error('Command [%s] failed because of OS [%s].' % (command, exc))
return None, exc
except subprocess.TimeoutExpired:
logger.error('Timeout [%s seconds] expired for command [%s] execution.' % (timeout, command))
return None, 'Timeout of %s seconds expired.' % timeout
except Exception as exc:
logger.error('Command [%s] failed for unknown reasons [%s].' % (command, exc))
logger.debug('Error:', exc_info=True)
return None, exc
else:
logger.debug('Command [%s] returned with exit code [0]. Command output was:' % command)
if output:
logger.debug(output)
return 0, output
用法:
exit_code, output = command_runner('whoami', shell=True)
我正在制作一个程序,为 Windows 中的标准命令 shell 添加额外的功能。例如,键入 google
后跟关键字将打开一个新选项卡,其中 Google 搜索这些关键字等。只要输入不引用我创建的自定义函数,它就会被处理为使用 subprocess.call(rawCommand, shell=True)
.
既然我想预测我的输入何时不是有效命令并且 return 类似于 f"Invalid command: {rawCommand}"
,我应该怎么做?
到目前为止,我已经尝试 subprocess.call(rawCommand)
这也是 return 标准输出和退出代码。所以看起来像这样:
>>> from subprocess import call
>>> a, b = call("echo hello!", shell=1), call("xyz arg1 arg2", shell=1)
hello!
'xyz' is not recognized as an internal or external command,
operable program or batch file.
>>> a
0
>>> b
1
我只想接收该退出代码。关于如何执行此操作的任何想法?
有些 shell 具有 语法-检查模式(例如、bash -n
),但这是唯一的错误形式这与“尝试执行命令并查看会发生什么”是分开的。定义更大的 class 的“即时”错误是 fraught proposition:如果 echo hello; ./foo
无效,因为 foo
无法作为命令找到,那么 false && ./foo
,它永远不会尝试 运行,或者 cp /bin/ls foo; ./foo
,它可能会成功(或可能无法复制)? eval $(configure_shell); foo
可能会或可能不会操纵 PATH
以找到 foo
呢? foo || install_foo
可能会出现故障?
因此,预期 失败在任何意义上都是不可能的:您唯一真正的选择是 捕获 命令的 output/error(如评论中所述)并以一些有用的方式报告它们。
如果你有一天想要处理编码错误,取回你运行命令的结果,有一个超时或决定哪些退出代码不是 0 可能不会触发错误(我是看着你,java 运行时!),这里有一个完成这项工作的完整函数:
import os
from logging import getLogger
import subprocess
logger = getLogger()
def command_runner(command, valid_exit_codes=None, timeout=300, shell=False, encoding='utf-8',
windows_no_window=False, **kwargs):
"""
Whenever we can, we need to avoid shell=True in order to preseve better security
Runs system command, returns exit code and stdout/stderr output, and logs output on error
valid_exit_codes is a list of codes that don't trigger an error
windows_no_window will hide the command window (works with Microsoft Windows only)
Accepts subprocess.check_output arguments
"""
# Set default values for kwargs
errors = kwargs.pop('errors', 'backslashreplace') # Don't let encoding issues make you mad
universal_newlines = kwargs.pop('universal_newlines', False)
creationflags = kwargs.pop('creationflags', 0)
if windows_no_window:
creationflags = creationflags | subprocess.CREATE_NO_WINDOW
try:
# universal_newlines=True makes netstat command fail under windows
# timeout does not work under Python 2.7 with subprocess32 < 3.5
# decoder may be unicode_escape for dos commands or utf-8 for powershell
output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=shell,
timeout=timeout, universal_newlines=universal_newlines, encoding=encoding,
errors=errors, creationflags=creationflags, **kwargs)
except subprocess.CalledProcessError as exc:
exit_code = exc.returncode
try:
output = exc.output
except Exception:
output = "command_runner: Could not obtain output from command."
if exit_code in valid_exit_codes if valid_exit_codes is not None else [0]:
logger.debug('Command [%s] returned with exit code [%s]. Command output was:' % (command, exit_code))
if isinstance(output, str):
logger.debug(output)
return exc.returncode, output
else:
logger.error('Command [%s] failed with exit code [%s]. Command output was:' %
(command, exc.returncode))
logger.error(output)
return exc.returncode, output
# OSError if not a valid executable
except (OSError, IOError) as exc:
logger.error('Command [%s] failed because of OS [%s].' % (command, exc))
return None, exc
except subprocess.TimeoutExpired:
logger.error('Timeout [%s seconds] expired for command [%s] execution.' % (timeout, command))
return None, 'Timeout of %s seconds expired.' % timeout
except Exception as exc:
logger.error('Command [%s] failed for unknown reasons [%s].' % (command, exc))
logger.debug('Error:', exc_info=True)
return None, exc
else:
logger.debug('Command [%s] returned with exit code [0]. Command output was:' % command)
if output:
logger.debug(output)
return 0, output
用法:
exit_code, output = command_runner('whoami', shell=True)