用 Python 创建你自己的 Shell
Python中文社区
共 1138字,需浏览 3分钟
· 2021-02-05
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
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():
+ key = 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.txt
ls = PipePy('ls')
grep = PipePy('grep')
print(ls | grep('tags'))
# <<< tags
__init__
和__call__
方法接受一个仅用于关键字的新_pipe_input
关键字参数,该参数将保存在self
上。_pipe_input
,它将作为输入参数传递给subprocess.run
。__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, **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
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")
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
# ... tags
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
# ... TAGS
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'}]
bash
实用程序:import os
cd = os.chdir
export = os.environ.__setitem__
pwd = PipePy('pwd')
pwd
# <<< /home/kbairak/prog/python/pipepy
cd('..')
pwd
# <<< /home/kbairak/prog/python
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, **kwargs)
+ result = self.__class__(*args, _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, **kwargs)
+ result = self.__class__(*args,
+ _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)
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')()
更多阅读
特别推荐
点击下方阅读原文加入社区会员
评论
真高!比亚迪员工爆料比亚迪在越南的薪资水平:基本工资480万,全勤奖35万,交通补助20万,餐补110万,每周6天,每天10小时
上一篇:某大公司为逼迫员工离职,竟然把他的工位安排到厕所旁,没想到他直接开始记录领导的如厕时间,还发到公司大群...对此,你怎么看?--完--PS:欢迎在留言区留下你的观点,一起讨论提高。如果今天的文章让你有新的启发,欢迎转发分享给更多人。全文完,感谢你的耐心阅读。如果你还想看到我的文章,请一定给本
开发者全社区
0
某大公司为逼迫员工离职,竟然把他的工位安排到厕所旁,没想到他直接开始记录领导的如厕时间,还发到公司大群...
上一篇:字节的跳动职级与薪资(2024年)我们与公司间的合作,宛如两艘船只在茫茫大海上相互依靠,共同抵御风浪,携手驶向成功的彼岸。然而,当航向开始产生分歧,或是波涛汹涌的风浪改变了我们的初衷,我们或许应当冷静地选择和平分手,而非在风雨中硬撑。最近,一位网友的遭遇引起了广大职场人的关注和热议。这位网友
开发者全社区
0
金融研究 | 使用Python测量关键审计事项的「信息含量」
Tips: 公众号推送后内容只能更改一次,且只能改20字符。如果内容出问题,或者想更新内容, 只能重复推送。为了更好的阅读体验,建议阅读本文博客版, 链接地址https://textdata.cn/blog/2023-01-13-information-content-of-critical-aud
大邓和他的Python
0
我看阿里的年终奖总算发了!
到4月底了,这两天看朋友圈,发现阿里的年终奖终于发了,问了问老同学,也从网上检索了不少信息,基本搞清楚了阿里今年的年终奖情况。近来来阿里一些集团对绩效等级做了较大的调整,以前的旧绩效系统中,绩效分为3.25、3.5、3.75、4和5五个等级,其中4和5是较高绩效等级,较少见。而且之前3.5绩效内部划
公子龙
0
CVPR 2024|大视觉模型的开山之作!无需任何语言数据即可打造大视觉模型
↑ 点击蓝字 关注极市平台作者丨科技猛兽编辑丨极市平台极市导读 本文提出一种序列建模 (sequential modeling) 的方法,不使用任何语言数据,训练大视觉模型。>>加入极市CV技术交流群,走在计算机视觉的最前沿本文目录1 序列建模打造大视觉模型(来自 U
极市平台
1
金融研究(更新) | 使用Python构建关键审计事项的「信息含量」
Tips: 公众号推送后内容只能更改一次,且只能改20字符。如果内容出问题,或者想更新内容, 只能重复推送。为了更好的阅读体验,建议阅读本文博客版, 链接地址https://textdata.cn/blog/2023-01-13-information-content-of-critical-aud
大邓和他的Python
0
字节的跳动职级与薪资(2024年)
上一篇:阿里公布年终奖,P7, 3.5+,22W年终奖,还有35W长期现金激励,真香字节跳动自2012年3月成立以来,已经迅速成长为一个全球性的科技公司。其产品和服务已经遍布全球150多个国家与地区,并且支持超过75种不同的语言。在字节跳动的官方网站上,列出了一系列引人注目的产品和服务,包括但不限于
开发者全社区
0
偷偷告诉你如何一台电脑开多个微信!
大家好,我是轩辕。前几天在粉丝群里,有人问我是怎么在一台电脑上同时登录两个微信的?正好之前写过一篇文章,分析过原理,分享给没看过的小伙伴学习一下。手机端多开微信估计很多人都知道,像华为、小米等手机系统都对此做了支持,不过在运行Windows系统的电脑上怎么启动两个微信呢?其实很简单,你只需要写一个批
编程技术宇宙
0