(本文基于Ansible 2.7)
Ansible的Shell模块(lib/ansible/modules/commands/shell.py)中几乎不包含python代码,注释表明其实际上运行的是command模块:
# # There is no actual shell module source, when you use 'shell' in ansible,
# it runs the 'command' module with special arguments and it behaves differently.
# See the command source and the comment "#USE_SHELL".
command模块(lib/ansible/modules/commands/command.py)实际上是通过调用AnsibleModule.run_command()来运行命令的:
在195行,为shell赋值:
257-263行
if not module.check_mode:
rc, out, err = module.run_command(args, executable=executable, use_unsafe_shell=shell, encoding=None, data=stdin)
elif creates or removes:
rc = 0
out = err = b'Command would have run if not in check mode'
else:
module.exit_json(msg="skipped, running in check mode", skipped=True)
其中,use_unsafe_shell=shell这个参数的处理,在lib/ansible/module_utils/basic.py的2742-2768行,与本文主题相关度不高,代码就不贴了。
那么shell模块是如何与command模块建立起联系,_uses_shell又是从哪里传入的呢?答案是,通过plugin。
下面是lib/ansible/plugins/action/shell.py的源码:
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.plugins.action import ActionBase
from ansible.utils.vars import merge_hash
class ActionModule(ActionBase):
def run(self, tmp=None, task_vars=None):
del tmp # tmp no longer has any effect
# Shell module is implemented via command
self._task.action = 'command'
self._task.args['_uses_shell'] = True
command_action = self._shared_loader_obj.action_loader.get('command',
task=self._task,
connection=self._connection,
play_context=self._play_context,
loader=self._loader,
templar=self._templar,
shared_loader_obj=self._shared_loader_obj)
result = command_action.run(task_vars=task_vars)
return result
可以看到_uses_shell值被显式指定为True。