python 自动交互shell_Riposte:使用Python编写的交互式Shell工具

Riposte是一个基于Python的交互式Shell工具。它允许你轻松地将应用程序封装在定制的交互式shell中。关于构建交互式解释器(REPL)的常见繁琐工作已经被考虑到了,因此你可以专注于应用程序的特定域逻辑。

安装

该软件包可在PyPI上使用,因此请使用pip进行安装:pip install riposte

Riposte支持Python 3.6及更高版本。

使用示例from riposte import Riposte

calculator = Riposte(prompt="calc:~$ ")

MEMORY = []

@calculator.command("add")

def add(x: int, y: int):

result = f"{x} + {y} = {x + y}"

MEMORY.append(result)

calculator.success(result)

@calculator.command("multiply")

def multiply(x: int, y: int):

result = f"{x} * {y} = {x * y}"

MEMORY.append(result)

calculator.success(result)

@calculator.command("memory")

def memory():

for entry in MEMORY:

calculator.print(entry)

calculator.run()calc:~$ add 2 2

[+] 2 + 2 = 4

calc:~$ multiply 3 3

[+] 3 * 3 = 9

calc:~$ memory

2 + 2 = 4

3 * 3 = 9

calc:~$

命令

首先,你需要注册一些命令以使REPL可操作。可以通过Riposte.command装饰器可以添加命令,并使用处理函数对其进行绑定。from riposte import Riposte

repl = Riposte()

@repl.command("hello")

def hello():

repl.success("Is it me you looking for?")

repl.run()riposte:~ $ hello

[+] Is it me you looking for?

另外Riposte.command接受一些可选参数:description 几个描述命令的词,你可以在以后用它来构建有意义的帮助

guides定义如何解释传递的参数

自动补全

Riposte支持命令的Tab键自动补全功能(tab-completion)。你可以以与注册命令类似的方式注册completer函数,只需使用Riposte.complete装饰器,并将其指向特定命令即可。from riposte import Riposte

repl = Riposte()

START_SUBCOMMANDS = ["foo", "bar"]

@repl.command("start")

def start(subcommand: str):

if subcommand in START_SUBCOMMANDS:

repl.status(f"{subcommand} started")

else:

repl.error("Unknown subcommand.")

@repl.complete("start")

def start_completer(text, line, start_index, end_index):

return [

subcommand

for subcommand in START_SUBCOMMANDS

if subcommand.startswith(text)

]

repl.run()

补全功能由TAB键触发。每个补全函数都应返回有效选项列表,并接受以下参数:text 行中的最后一个单词

line 整行的行内容

start_index 该行中最后一个单词的起始索引

end_index 该行中最后一个单词的结束索引

在我们的例子中:riposte:~ $ start batext -> "ba"

line -> "start ba"

start_index -> 6

end_index -> 8

有了这些信息,你可以为每个命令构建自定义的completer函数。

Guides

Guides是一种说明命令应如何解释用户通过提示传递的参数的方法。Riposte依靠类型提示(Type Hints)来做到这一点。from riposte import Riposte

repl = Riposte()

@repl.command("guideme")

def guideme(x: int, y: str):

repl.print("x:", x, type(x))

repl.print("y:", y, type(y))

repl.run()riposte:~ $ guideme 1 1

x: 1

y: 1

在这两种情况下,我们都将value 1作为x和y传递。基于参数的类型提示,传递的参数在x的情况下被解释为int,在y的情况下被解释为str。你也可以将该技术用于不同的类型。from riposte import Riposte

repl = Riposte()

@repl.command("guideme")

def guideme(x: dict, y: list):

x["foo"] = "bar"

repl.print("x:", x, type(x))

y.append("foobar")

repl.print("y:", y, type(y))

repl.run()riposte:~ $ guideme "{'bar': 'baz'}" "['barbaz']"

x: {'bar': 'baz', 'foo': 'bar'}

y: ['barbaz', 'foobar']

另一种更为强大的定义guides用于处理函数参数的方法是,直接从Ricoste.command装饰器定义它。在本例中,以这种方式定义的guide优先于类型提示。from riposte import Riposte

repl = Riposte()

@repl.command("guideme", guides={"x": [int]})

def guideme(x):

repl.print("x:", x, type(x))

repl.run()riposte:~ $ guideme 1

x: 1

为什么这种方式更加强大?因为通过这种方式可以让你链接不同的guides,其中一个guide的输出是另一个guide的输入,创建验证或将输入转换为更复杂的类型。from collections import namedtuple

from riposte import Riposte

from riposte.exceptions import RiposteException

from riposte.guides import literal

repl = Riposte()

def non_negative(value: int):

if value < 0:

raise RiposteException("Value can't be negative")

return value

Point = namedtuple("Point", ("x", "y"))

def get_point(value: dict):

return Point(**value)

@repl.command("guideme",

guides={"x": [int, non_negative], "y": [literal, get_point]})

def guideme(x, y):

repl.print("x:", x, type(x))

repl.print("y:", y, type(y))

repl.run()riposte:~ $ guideme -1 '{"x": 1, "y": 2}'

[-] Value can't be negative

riposte:~ $ guideme 1 '{"x": 1, "y": 2}'

x: 1

y: Point(x=1, y=2)

riposte:~ $

这只是一个简单的函数调用,其中输入字符串被传递给链中的第一个引导函数。在这种情况下,调用如下所示:non_negative(int("-1")) # guide chain for parameter `x`

get_point(literal('{"x": 1, "y": 2}')) # guide chain for parameter `y`

打印

Riposte内置线程安全打印方法:print

info

error

status

success

每个方法都遵循Python内置print()函数的签名。除了print之外,所有这些都提供与其名称相对应的信息着色( informative coloring)。

我们强烈建议你使用我们的线程安全打印API,但如果你知道自己在做什么,并且100%的确定,那么线程执行在你应用程序生命周期的某个阶段将永远不会出现, 你可以使用Python的内置print()函数。

扩展 PrinterMixin

如果要更改现有方法的样式或添加自定义方法,你可以对PrinterMixin类进行扩展。from riposte import Riposte

from riposte.printer.mixins import PrinterMixin

class ExtendedPrinterMixin(PrinterMixin):

def success(self, *args, **kwargs): # overwriting existing method

self.print(*args, **kwargs)

def shout(self, *args, **kwargs): # adding new one

self.print((*args, "!!!"), **kwargs)

class CustomRiposte(Riposte, ExtendedPrinterMixin):

pass

repl = CustomRiposte()

@repl.command("foobar")

def foobar(message: str):

repl.shout(message)

自定义 PrinterMixin

对现有的打印API不满意吗?没关系,你也可以使用PrinterBaseMixin及其线程安全_print方法从头开始构建自己的打印API。from riposte import Riposte

from riposte.printer.mixins import PrinterBaseMixin

class CustomPrinterMixin(PrinterBaseMixin):

def ask(self, *args, **kwargs): # adding new one

self._print((*args, "???"), **kwargs)

def shout(self, *args, **kwargs): # adding new one

self._print((*args, "!!!"), **kwargs)

class CustomRiposte(Riposte, CustomPrinterMixin):

pass

repl = CustomRiposte()

@repl.command("foobar")

def foobar(message: str):

repl.shout(message)

repl.ask(message)

repl.success(message) # It'll raise exception as it's no longer available

使用 Pallete 对输出着色

如果你想在输出中添加一些颜色,可以使用Pallete。from riposte import Riposte

from riposte.printer import Palette

repl = Riposte()

@repl.command("foo")

def foo(msg: str):

repl.print(Palette.GREEN.format(msg)) # It will be green

Pallete目前支持的颜色如下:GREY

RED

GREEN

YELLOW

BLUE

MAGENTA

CYAN

WHITE

BOLD

History

命令历史记录存储在.riposte文件的HOME目录中。默认长度为100行。可以使用history_file和history_length参数更改这两个设置。from pathlib import Path

from riposte import Riposte

repl = Riposte(

history_file=Path.home() / ".custom_history_file",

history_length=500,

)

Prompt

默认提示符为riposte:~ $你也可以自定义:from riposte import Riposte

repl = Riposte(prompt="custom-prompt >>> ")

repl.run()

你还可以通过覆盖Riposte.prompt属性,基于某个对象的状态动态解析提示布局。在以下示例中,我们将根据MODULE值确定prompt:from riposte import Riposte

class Application:

def __init__(self):

self.module = None

class CustomRiposte(Riposte):

@property

def prompt(self):

if app.module:

return f"foo:{app.module} > "

else:

return self._prompt # reference to `prompt` parameter.

app = Application()

repl = CustomRiposte(prompt="foo > ")

@repl.command("set")

def set_module(module_name: str):

app.module = module_name

repl.success("Module has been set.")

@repl.command("unset")

def unset_module():

app.module = None

repl.success("Module has been unset.")

repl.run()foo > set bar

[+] Module has been set.

foo:bar > unset

[+] Module has been unset.

foo >

Banner# banner.py

from riposte import Riposte

BANNER = """ _ _ _ _ _ _ _ _ _

| | | | | | | | | | | | | | | |

| |_| | ___| | | ___ | | | | ___ _ __| | __| | |

| _ |/ _ \ | |/ _ \ | |/\| |/ _ \| '__| |/ _` | |

| | | | __/ | | (_) | \ /\ / (_) | | | | (_| |_|

\_| |_/\___|_|_|\___/ \/ \/ \___/|_| |_|\__,_(_)

Welcome User Hello World v1.2.3

"""

repl = Riposte(banner=BANNER)

@repl.command("hello")

def hello():

repl.print("Hello World!")

repl.run()$ python banner.py

_ _ _ _ _ _ _ _ _

| | | | | | | | | | | | | | | |

| |_| | ___| | | ___ | | | | ___ _ __| | __| | |

| _ |/ _ \ | |/ _ \ | |/\| |/ _ \| '__| |/ _` | |

| | | | __/ | | (_) | \ /\ / (_) | | | | (_| |_|

\_| |_/\___|_|_|\___/ \/ \/ \___/|_| |_|\__,_(_)

Welcome User Hello World v1.2.3

riposte:~ $

项目状态

Riposte项目目前正处于开发阶段。未来可能会有一些重大变化,尽管这里出现的很多概念已在routerploit开发过程中经过了实战测试。

致谢

*参考来源:GitHub,FB小编secist编译,转载请注明来自FreeBuf.COM

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值