How to answer to prompts automatically with python fabric?

后端 未结 6 1231
一个人的身影
一个人的身影 2020-11-29 23:42

I want to run a command which prompts me to enter yes/no or y/n or whatever. If I just run the command local(\"my_command\") then it stops and asks me for input

相关标签:
6条回答
  • 2020-11-30 00:22

    Starting from version 1.9, Fabric includes a way of managing this properly.

    The section about Prompts in the Fabric documentation says:

    The prompts dictionary allows users to control interactive prompts. If a key in the dictionary is found in a command’s standard output stream, Fabric will automatically answer with the corresponding dictionary value.

    You should be able to make Fabric automatically answer prompts like this:

    with settings(prompts={'Do you want to continue [Y/n]? ': 'Y'}):
        run('apt-get update')
        run('apt-get upgrade')
    
    0 讨论(0)
  • 2020-11-30 00:28

    I have used simple echo pipes to answer prompts with Fabric.

    run('echo "yes\n"| my_command')
    
    0 讨论(0)
  • 2020-11-30 00:28

    Note: this answer is several years old, and in the mean time fabric has (interestingly similar looking) implementation of this. See the answer by @timothée-jeannin below.

    See https://stackoverflow.com/a/10007635/708221

    pip install fexpect

    from ilogue.fexpect import expect, expecting, run 
    
    prompts = []
    prompts += expect('What is your name?','John')
    prompts += expect('Are you at stackoverflow?','Yes')
    
    with expecting(prompts):
        run('my_command')
    

    Fexpect adds answering to prompts to fabric with use of pexpect

    0 讨论(0)
  • 2020-11-30 00:30

    To expand a bit on Timothée's excellent answer, here's the code that Fabric uses when checking the prompts dictionary.

    def _get_prompt_response(self):
        """
        Iterate through the request prompts dict and return the response and
        original request if we find a match
        """
        for tup in env.prompts.iteritems():
            if _endswith(self.capture, tup[0]):
                return tup
        return None, None
    

    Fabric uses .endswith for its check, so make sure you include trailing spaces in the string you use as a key in the prompts dictionary.

    For example - let's say you are trying to automate the Django test database prompt

    Type 'yes' if you would like to try deleting the test database 'test_my_app', or 'no' to cancel:

    All we need is enough of the end of the prompt so that it is unique. Include trailing spaces.

    django_test_database_prompt = "or 'no' to cancel: "
    #         won't work without this trailing space ^
    
    with settings(
        prompts={django_test_database_prompt : 'yes'}
    ):
        run('%s %s' % (virtualenv_python_path,
                       test_runner_file_path,
                      )
           )
    
    0 讨论(0)
  • 2020-11-30 00:30

    Putting this as an answer though its a comment from @BobNadler

    run("yes | my_command");

    0 讨论(0)
  • 2020-11-30 00:40

    In Fabric 2.1, this can be accomplished using the auto-respond example that is available through the invoke package (a dependency of Fabric 2.1):

    >>> from invoke import Responder
    >>> from fabric import Connection
    >>> c = Connection('host')
    >>> sudopass = Responder(
    ...     pattern=r'\[sudo\] password:',
    ...     response='mypassword\n',
    ... )
    >>> c.run('sudo whoami', pty=True, watchers=[sudopass])
    [sudo] password:
    root
    <Result cmd='sudo whoami' exited=0>
    

    Note that this is not limited to sudo passwords and can be used anywhere where you have a pattern to match for and a canned response (that may not be a password).

    There are a couple of tips:

    1. pty=True is NOT necessary but could be important because it makes the flow seem more realistic. e.g. if you had a prompt expecting a yes/no answer to proceed, without it(pty=True) your command would still run; except, your choice/input(specified by response) won't be shown as typed as the answer as one might expect
    2. The pattern specified within the Responder can often include spaces at the end of the line so try adding spaces when the watcher doesn't seem to match.
    3. According to the note discussed at the end of the watcher docs:

      The pattern argument to Responder is treated as a regular expression, requiring more care (note how we had to escape our square-brackets in the above example) but providing more power as well.

      So, don't forget to escape (using backslashes) where necessary.

    0 讨论(0)
提交回复
热议问题