您现在的位置是:亿华云 > 数据库
用Python创建你自己的Shell
亿华云2025-10-03 06:26:26【数据库】7人已围观
简介介绍很多人讨厌bash脚本。每当我要做最简单的事情时,我都必须查阅文档。如何将函数的参数转发给子命令?如何将字符串分配给变量,然后作为命令调用该字符串?如何检查两个字符串变量是否相等?如何分割字符串并
介绍
很多人讨厌bash脚本。建自己每当我要做最简单的建自己事情时,我都必须查阅文档。建自己如何将函数的建自己参数转发给子命令?如何将字符串分配给变量,然后作为命令调用该字符串?建自己如何检查两个字符串变量是否相等?如何分割字符串并获得后半部分?等等。不是建自己我找不到这些答案,而是建自己每次都必须查找它们。
但是建自己,我们不能否认将整个程序当作纯粹的建自己功能发挥作用的能力,以及将一个程序的建自己输出传递到另一个程序的自然程度。因此,建自己我想知道,建自己我们能否将bash的建自己某些功能与Python结合起来?
基础知识
让我们从一个类开始。这是建自己一个简单的方法,将其初始化参数保存到局部变量,建自己然后使用subprocess.run对其自身进行延迟求值并保存结果。
import subprocess class PipePy: def __init__(self, *args): self._args = args self._result = None def _evaluate(self): if self._result is not None: return self._result = subprocess.run(self._args, capture_output=True, text=True) @property def returncode(self): self._evaluate() return self._result.returncode @property def stdout(self): self._evaluate() return self._result.stdout def __str__(self): return self.stdout @property def stderr(self): self._evaluate() return self._result.stderr我们让它旋转一下:
ls = PipePy(ls) ls_l = PipePy(ls, -l) print(ls) # <<< files.txt # ... main.py # ... tags print(ls_l) # <<< total 16 # ... -rw-r--r-- 1 kbairak kbairak 125 Jan 22 08:53 files.txt # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb 1 21:54 main.py # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb 1 21:54 tags使其看起来更像“命令式”
不用每次我们要自定义命令时都去调用PipePy。
ls_l = PipePy(ls, -l) print(ls_l)相当于
ls = PipePy(ls) print(ls(-l))换句话说,我们要使:
PipePy(ls, -l)相当于
PipePy(ls)(-l)值得庆幸的是,服务器租用我们的类创建了惰性对象这一事实在很大程度上帮助了我们:
class PipePy: # __init__, etc def __call__(self, *args): args = self._args + args return self.__class__(*args) ls = PipePy(ls) print(ls(-l)) # <<< total 16 # ... -rw-r--r-- 1 kbairak kbairak 125 Jan 22 08:53 files.txt # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb 1 21:54 main.py # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb 1 21:54 tags关键字参数
如果要向ls传递更多参数,则可能会遇到--sort = size。我们可以轻松地执行ls(-l,--sort = size)。我们可以做得更好吗?
class PipePy: - def __init__(self, *args): + def __init__(self, *args, **kwargs): self._args = args + self._kwargs = kwargs self._result = None def _evaluate(self): if self._result is not None: return - self._result = subprocess.run(self._args, + self._result = subprocess.run(self._convert_args(), capture_output=True, text=True) + def _convert_args(self): + args = [str(arg) for arg in self._args] + for key, value in self._kwargs.items(): + keykey = key.replace(_, -) + args.append(f"--{ key}={ value}") + return args - def __call__(self, *args): + def __call__(self, *args, **kwargs): args = self._args + args + kwargs = { **self._kwargs, **kwargs} - return self.__class__(*args) + return self.__class__(*args, **kwargs) # returncode, etc让我们来旋转一下:
print(ls(-l)) # <<< total 16 # ... -rw-r--r-- 1 kbairak kbairak 125 Jan 22 08:53 files.txt # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb 1 21:54 main.py # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb 1 21:54 tags print(ls(-l, sort="size")) # <<< total 16 # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb 1 21:54 main.py # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb 1 21:54 tags # ... -rw-r--r-- 1 kbairak kbairak 125 Jan 22 08:53 files.txtPiping
事情开始变得有趣起来。我们的最终目标是能够做到:
ls = PipePy(ls) grep = PipePy(grep) print(ls | grep(tags)) # <<< tags我们的过程是:
1、让__init__和__call__方法接受一个仅用于关键字的新_pipe_input关键字参数,该参数将保存在self上。
2、在评估期间,如果设置了_pipe_input,它将作为输入参数传递给subprocess.run。
3、重写__or__方法以将左操作数的结果作为pipe输入传递给右操作数。
class PipePy: - def __init__(self, *args, **kwargs): + def __init__(self, *args, _pipe_input=None, **kwargs): self._args = args self._kwargs = kwargs + self._pipe_input = _pipe_input self._result = None - def __call__(self, *args, **kwargs): + def __call__(self, *args, _pipe_input=None, **kwargs): args = self._args + args kwargs = { **self._kwargs, **kwargs} - return self.__class__(*args, **kwargs) + return self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs) def _evaluate(self): if self._result is not None: return self._result = subprocess.run(self._convert_args(), + input=self._pipe_input, capture_output=True, text=True) + def __or__(left, right): + return right(_pipe_input=left.stdout)让我们尝试一下(从之前稍微修改命令以证明它确实有效):
ls = PipePy(ls) grep = PipePy(grep) print(ls(-l) | grep(tags)) # <<< -rw-r--r-- 1 kbairak kbairak 1838 Feb 1 21:54 tags让我们添加一些简单的东西
1、真实性:
class PipePy: # __init__, etc def __bool__(self): return self.returncode == 0现在我们可以作出如下处理:
git = PipePy(git) grep = PipePy(grep) if git(branch) | grep(my_feature): print("Branch my_feature found")2、读取/写入文件:
class PipePy: # __init__, etc def __gt__(self, filename): with open(filename, w) as f: f.write(self.stdout) def __rshift__(self, filename): with open(filename, a) as f: f.write(self.stdout) def __lt__(self, filename): with open(filename) as f: return self(_pipe_input=f.read())现在可以作出如下操作:
ls = PipePy(ls) grep = PipePy(grep) cat = PipePy(cat) ls > files.txt print(grep(main) < files.txt) # <<< main.py ls >> files.txt print(cat(files.txt)) # <<< files.txt # ... main.py # ... tags # ... files.txt # ... main.py # ... tags3、迭代
class PipePy: # __init__, etc def __iter__(self): return iter(self.stdout.split())现在可以作出如下操作:
ls = PipePy(ls) for name in ls: print(name.upper()) # <<< FILES.TXT # ... MAIN.PY # ... TAGS4、表格:
class PipePy: # __init__, etc def as_table(self): lines = self.stdout.splitlines() fields = lines[0].split() result = [] for line in lines[1:]: item = { } for i, value in enumerate(line.split(maxsplit=len(fields) - 1)): item[fields[i]] = value result.append(item) return result现在可以作出下面操作:
ps = PipePy(ps) print(ps) # <<< PID TTY TIME CMD # ... 4205 pts/4 00:00:00 zsh # ... 13592 pts/4 00:00:22 ptipython # ... 16253 pts/4 00:00:00 ps ps.as_table() # <<< [{ PID: 4205, TTY: pts/4, TIME: 00:00:00, CMD: zsh}, # ... { PID: 13592, TTY: pts/4, TIME: 00:00:22, CMD: ptipython}, # ... { PID: 16208, TTY: pts/4, TIME: 00:00:00, CMD: ps}]5、普通bash实用程序:
在子进程中更改工作目录不会影响当前的脚本或python shell。与更改环境变量相同,以下内容不是PipePy的补充,亿华云计算但很不错:
import os cd = os.chdir export = os.environ.__setitem__ pwd = PipePy(pwd) pwd # <<< /home/kbairak/prog/python/pipepy cd(..) pwd # <<< /home/kbairak/prog/python使事情看起来更shell-like
如果我在交互式shell中,则希望能够简单地键入ls并完成它。
class PipePy: # __init__, etc def __repr__(self): return self.stdout + self.stderr交互式shell
>>> ls = PipePy(ls) >>> ls files.txt main.py tags我们的实例是惰性的,这意味着如果我们对它们的结果感兴趣,则将对它们进行评估,此后不再进行评估。如果我们只是想确保已执行该操作怎么办?例如,假设我们有以下脚本:
from pipepy import PipePy tar = PipePy(tar) tar(-xf, some_archive.tar) print("File extracted")该脚本实际上不会执行任何操作,因为tar调用实际上并未得到评估。我认为一个不错的惯例是,如果不带参数调用__call__强制求值:
class PipePy: def __call__(self, *args, _pipe_input=None, **kwargs): args = self._args + args kwargs = { **self._kwargs, **kwargs} - return self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs) + result = self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs) + if not args and not _pipe_input and not kwargs: + result._evaluate() + return result因此在编写脚本时,如果要确保实际上已调用命令,则必须用一对括号来调用它:
from pipepy import PipePy tar = PipePy(tar) -tar(-xf, some_archive.tar) +tar(-xf, some_archive.tar)() print("File extracted")但是,我们还没有解决问题。考虑一下:
date = PipePy(date) date # <<< Mon Feb 1 10:43:08 PM EET 2021 # Wait 5 seconds date # <<< Mon Feb 1 10:43:08 PM EET 2021不好!date没有改变。date对象将其_result保留在内存中。随后的评估实际上不会调用该命令,而只是返回存储的值。
一种解决方案是通过使用空括号来强制创建副本:
date = PipePy(date) date() # <<< Mon Feb 1 10:45:09 PM EET 2021 # Wait 5 seconds date() # <<< Mon Feb 1 10:45:14 PM EET 2021另一个解决方案是:由PipePy构造函数返回的高防服务器实例不应该是惰性的,但由__call__调用返回的实例将是惰性的。
class PipePy: - def __init__(self, *args, _pipe_input=None, **kwargs): + def __init__(self, *args, _pipe_input=None, _lazy=False, **kwargs): self._args = args self._kwargs = kwargs self._pipe_input = _pipe_input + self._lazy = _lazy self._result = None def __call__(self, *args, _pipe_input=None, **kwargs): args = self._args + args kwargs = { **self._kwargs, **kwargs} - result = self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs) + result = self.__class__(*args, + _pipe_input_pipe_input=_pipe_input, + _lazy=True, + **kwargs) if not args and not _pipe_input and not kwargs: result._evaluate() return result def _evaluate(self): - if self._result is not None: + if self._result is not None and self._lazy: return self._result = subprocess.run(self._convert_args(), input=self._pipe_input, capture_output=True, text=True)旋转一下:
date = PipePy(date) date # <<< Mon Feb 1 10:54:09 PM EET 2021 # Wait 5 seconds date # <<< Mon Feb 1 10:54:14 PM EET 2021并且可以预见的是,使用空调用的返回值将具有之前的行为:
date = PipePy(date) d = date() d # <<< Mon Feb 1 10:56:21 PM EET 2021 # Wait 5 seconds d # <<< Mon Feb 1 10:56:21 PM EET 2021没关系 您不会期望d会更新其值。
越来越危险
好吧,ls(-l)不错,但是如果我们像人类一样简单地做ls -l,那就太好了。嗯,我有个主意:
class PipePy: # __init__, etc def __sub__(left, right): return left(f"-{ right}")现在可以作如下操作:
ls = PipePy(ls) ls - l # <<< total 16 # ... -rw-r--r-- 1 kbairak kbairak 46 Feb 1 23:04 files.txt # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb 1 21:54 main.py # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb 1 21:54 tags我们还有一步:
l = l ls -l现在无济于事:
import string for char in string.ascii_letters: if char in locals(): continue locals()[char] = char class PipePy: # __init__, etc更危险的事情
用locals()给了我一个灵感。为什么我们必须一直实例化PipePy?我们无法在路径中找到所有可执行文件,并根据它们创建PipePy实例吗?我们当然可以!
import os import stat for path in os.get_exec_path(): try: names = os.listdir(path) except FileNotFoundError: continue for name in names: if name in locals(): continue if x in stat.filemode(os.lstat(os.path.join(path, name)).st_mode): locals()[name] = PipePy(name)因此,现在,将我们拥有的所有内容都放在一个python文件中,并删除脚本(这是实际bash脚本的转录):
from pipepy import mysqladmin, sleep, drush, grep for i in range(10): if mysqladmin(ping, host="mysql_drupal7", user="user", password="password"): break sleep(1)() # Remember to actually invoke if not drush(status, bootstrap) | grep(-q, Successful): drush(-y, site-install, standard, db_url="mysql://user:password@mysql_drupal7:3306/drupal", acount_pass="kbairak")() # Remember to actually invoke drush(en, tmgmt_ui, tmgmt_entity_ui, tmgmt_node_ui)()很赞哦!(6)
相关文章
- 企业应该如何购买、部署和应用边缘计算
- NVIDIA 为全球企业带来生成式 AI 推出用于创建大型语言模型和视觉模型的云服务
- 全新Dell PowerEdge服务器聚焦可持续创新赋能下一代双碳绿色数据中心
- 联想毛世杰:数实融合引爆创新,行业元宇宙引领行业发展新趋势
- 2022年全球数据中心资本支出增至2410亿美元
- Dell PowerEdge新一代服务器正式发布:立足企业需求打造端到端的产品和解决方案,助推企业数字化转型
- 数据管理创新在数据中心效率中的作用
- 国产芯片再迎利好!中国移动成功研制可重构5G射频收发芯片“破风8676”
- 戴尔科技:加码科技创新,助推数字化转型
- NVMe-oF如何改变数据中心存储架构