Metadata-Version: 2.1
Name: citizenshell
Version: 2.3.1
Summary: Interact with shell locally or over different connection types (telnet, ssh, serial, adb)
Home-page: https://github.com/meuter/citizenshell
Author: Cedric Meuter
Author-email: cedric.meuter@gmail.com
License: MIT
Download-URL: https://github.com/meuter/citizenshell/archive/2.3.1.tar.gz
Description: # citizenshell [![Build Status](https://travis-ci.org/meuter/citizenshell.svg?branch=master)](https://travis-ci.org/meuter/citizenshell)
        
        __citizenshell__ is a python library allowing to execute shell commands either locally or remotely over several protocols (telnet, ssh, serial or adb) using a simple and consistent API. This library is compatible with both python 2 (2.7) and 3 (>=3.4) as well as with [PyPy](https://pypy.org/). For now, it focuses on POSIX platforms like Linux and MacOS, but may be extended to work to Windows based platform in the future. It is distributed under
        [MIT](https://opensource.org/licenses/MIT) license.
        
        ## Installation
        
        __citizenshell__ can simply installed using `pip install citizenshell`
        
        ## Obtaining a shell
        
        First you need a shell. For that you have several options:
        
        1. use the built-in `LocalShell` for quick access:
        
            ```python
            from citizenshell import sh
            ```
        
        2. you can instanciate your own `LocalShell`:
        
            ```python
            from citizenshell import LocalShell
        
            shell = LocalShell()
            ```
        
        3. you can instanciate the `TelnetShell` for shell over telnet:
        
            ```python
            from citizenshell import TelnetShell
        
            shell = TelnetShell(hostname="acme.org", username="john",
                                password="secretpassword")
            ```
        
        4. you can instanciate the `SecureShell` for shell over SSH:
        
            ```python
            from citizenshell import SecureShell
        
            shell = SecureShell(hostname="acme.org", username="john",
                                password="secretpassword")
            ```
        
        5. you can instanciate the `AdbShell` for shell over ADB:
        
            - if ADB devices is reachable over TCP/IP:
        
              ```python
              from citizenshell import AdbShell
          
              shell = AdbShell(hostname="acme.org", port=5555)
              ```
        
            - if ADB device is connected via USB:
        
              ```python
              from citizenshell import AdbShell
          
              shell = AdbShell(device="1c123a09dab45cbf")
              ```
        
            - if there is only one ADB device connected via USB:
        
              ```python
              from citizenshell import AdbShell
          
              shell = AdbShell()
              ```
        
        6. you can instanciate the `SerialShell` for shell over serial line:
        
            ```python
            from serial import EIGHTBITS, PARITY_NONE
            from citizenshell import SerialShell
        
            shell = SerialShell(port="/dev/ttyUSB3", username="john",
                                password="secretpassword",
                                baudrate=115200, parity=PARITY_NONE, bytesize=EIGHTBITS)
            ```
        
        7. you can also obtain shell objects by URI using the `Shell` function:
        
            ```python
            from citizenshell import Shell
        
            localshell  = Shell()
            telnetshell = Shell("telnet://john:secretpassword@acme.org:1234")
            secureshell = Shell("ssh://john:secretpassword@acme.org:1234")
            adbshell    = Shell("adb://myandroiddevice:5555")
            adbtcpshell = Shell("adb+tcp://myandroiddevice:5555")
            adbtcpshell = Shell("adb+usb://1c123a09dab45cbf")
            serialshell = Shell("serial://jogn:secretpassword@/dev/ttyUSB3?baudrate=115200")
            ```
        
            you can also mix and match betweens providing arguments in the URI or via kwargs:
        
            ```python
            telnetshell = Shell("telnet://john@acme.org", password="secretpassword", port=1234)
            serialshell = Shell("serial://john:secretpassword@/dev/ttyUSB3", baudrate=115200)
            ```
        
        ## Using a shell
        
        Once you have shell, any shell, you can call it directly and get the standart output:
        
        ```python
        assert shell("echo Hello World") == "Hello World"
        ```
        
        You can also iterate over the standard output:
        
        ```python
        result = [int(x) for x in shell("""
            for i in 1 2 3 4; do
                echo $i;
            done
        """)]
        assert result == [1, 2, 3, 4]
        ```
        
        You don't have to wait for the command to finish to receive the output.
        
        This loop
        
        ```python
        for line in shell("for i in 1 2 3 4; do echo -n 'It is '; date +%H:%M:%S; sleep 1; done", wait=False):
            print ">>>", line + "!"
        ```
        
        would produce something like:
        
        ```text
        >>> It is 14:24:52!
        >>> It is 14:24:53!
        >>> It is 14:24:54!
        >>> It is 14:24:55!
        ```
        
        You can extract stdout, stderr and exit code seperately:
        
        ```python
        result = shell(">&2 echo error && echo output && exit 13")
        assert result.stdout() == ["output"]
        assert result.stderr() == ["error"]
        assert result.exit_code() == 13
        ```
        
        You can inject environment variable to the shell
        
        ```python
        assert shell("echo $VAR", VAR="bar") == "bar"
        ```
        
        By default, shell inherits "$CWD" from the environment (aka $PWD).
        
        Still, if ever a command needs to be run from a custom path, one
        way to achieve this is:
        
        ```python
            shell = LocalShell()
            os.chdir(first_custom_path)
            shell('first_command')
            os.chdir(second_custom_path)
            shell('second_command')
        ```
        
        This works ... but it is ugly! Two levels of abstraction are mixed.
        
        This is better:
        
        ```python
            shell = LocalShell()
            shell('first_command', cwd=first_custom_path)
            shell('second_command', cwd=second_custom_path)
        ```
        
        The shell can raise an exception if the exit code is non-zero:
        
        ```python
        assert shell("exit 13").exit_code() == 13 # will not raise any exception
        try:
            shell("exit 13", check_xc=True) # will raise an exception
            assert False, "will not be reached"
        except ShellError as e:
            assert True, "will be reached"
        ```
        
        The shell can also raise an exception if something is printed on the standard error:
        
        ```python
        shell("echo DANGER >&2").stderr() == ["DANGER"] # will not raise any exception
        try:
            shell("echo DANGER >&2", check_err=True) # will raise an exception
            assert False, "will not be reached"
        except ShellError as e:
            assert True, "will be reached"
        ```
        
        You can pull file from the remote host (for `LocalShell` it's just doing a copy):
        
        ```python
        shell("echo -n test > remote_file.txt")
        shell.pull("local_file.txt", "remote_file.txt")
        assert open("local_file.txt", "r").read() == "test"
        ```
        
        or push file to the remote host (again, for `LocalShell` it's just doing a copy):
        
        ```python
        open("local_file.txt", "w").write("test")
        shell.push("local_file.txt", "remote_file.txt")
        assert str(shell("cat remote_file.txt")) == "test"
        ```
        
        ## Logs
        
        Every shell object has a set of loggers: stdin, stderr and stdout, as well as for out of band logging message. 
        By default they are all set to `logging.CRITICAL` which does not log anything. However, this log level
        can be configured either using the `log_level=` keyword argument in the shell constructor:
        
        ```python
        from citizenshell import LocalShell
        from logging import INFO
        
        shell = LocalShell(log_level=INFO)
        ```
        
        or by calling the `set_log_level()` method:
        
        ```python
        from citizenshell import sh
        from logging import INFO
        
        sh.set_log_level(INFO)
        ```
        
        When configured with `logging.INFO`:
        - all commands are logged on stdout prefixed by a `$` and colored in cyan with `termcolor`
        - all characters produced on stdout are logged to stdout
        - all characters produced on stderr are logged to stderr and colored in red with `termcolor`
        - all out of band messages are logged to stdout prefixed with `>` and colored in yello with `termcolor`
        
        For example:
        
        ```python
        from citizenshell import LocalShell
        from logging import INFO
        
        shell = LocalShell(log_level=INFO)
        shell(">&2 echo error && echo output && exit 13")
        shell("echo Hello > /tmp/from.txt")
        shell.push("/tmp/from.txt", "/tmp/to.txt")
        ```
        
        will produce the following logs (colors are omitted):
        
        ```
        $ >&2 echo error && echo output && exit 13
        output
        error
        $ echo Hello > /tmp/from.txt
        > '/tmp/from.txt' -> '/tmp/to.txt'
        $ command -v chmod
        /bin/chmod
        $ chmod 664 '/tmp/to.txt'
        ```
        
        For more even more logs messages, `logging.DEBUG` can be used. 
        
        
Keywords: shell,telnet,adb,ssh,serial
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Topic :: Utilities
Description-Content-Type: text/markdown
