自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

用Python創(chuàng)建你自己的Shell

開發(fā) 后端
我們不能否認(rèn)將整個程序當(dāng)作純粹的功能發(fā)揮作用的能力,以及將一個程序的輸出傳遞到另一個程序的自然程度。因此,我想知道,我們能否將bash的某些功能與Python結(jié)合起來。

 [[380870]]

介紹

很多人討厭bash腳本。每當(dāng)我要做最簡單的事情時,我都必須查閱文檔。如何將函數(shù)的參數(shù)轉(zhuǎn)發(fā)給子命令?如何將字符串分配給變量,然后作為命令調(diào)用該字符串?如何檢查兩個字符串變量是否相等?如何分割字符串并獲得后半部分?等等。不是我找不到這些答案,而是每次都必須查找它們。

但是,我們不能否認(rèn)將整個程序當(dāng)作純粹的功能發(fā)揮作用的能力,以及將一個程序的輸出傳遞到另一個程序的自然程度。因此,我想知道,我們能否將bash的某些功能與Python結(jié)合起來?

基礎(chǔ)知識

讓我們從一個類開始。這是一個簡單的方法,將其初始化參數(shù)保存到局部變量,然后使用subprocess.run對其自身進(jìn)行延遲求值并保存結(jié)果。 

  1. import subprocess  
  2. class PipePy:  
  3.     def __init__(self, *args):  
  4.         self._args = args  
  5.         self._result = None  
  6.     def _evaluate(self):  
  7.         if self._result is not None:  
  8.             return  
  9.         self._result = subprocess.run(self._args,  
  10.                                       capture_output=True 
  11.                                       text=True 
  12.      @property  
  13.     def returncode(self):  
  14.         self._evaluate()  
  15.         return self._result.returncode  
  16.     @property  
  17.     def stdout(self):  
  18.         self._evaluate()  
  19.         return self._result.stdout  
  20.     def __str__(self):  
  21.         return self.stdout  
  22.     @property  
  23.     def stderr(self):  
  24.         self._evaluate()  
  25.         return self._result.stderr 

我們讓它旋轉(zhuǎn)一下: 

  1. ls = PipePy('ls')  
  2. ls_l = PipePy('ls', '-l')  
  3. print(ls)  
  4. <<< files.txt  
  5. # ... main.py  
  6. # ... tags  
  7. print(ls_l)  
  8. <<< total 16  
  9. # ... -rw-r--r-- 1 kbairak kbairak  125 Jan 22 08:53 files.txt  
  10. # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb  1 21:54 main.py  
  11. # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb  1 21:54 tags 

使其看起來更像“命令式”

不用每次我們要自定義命令時都去調(diào)用PipePy。 

  1. ls_l = PipePy('ls', '-l')  
  2. print(ls_l) 

相當(dāng)于 

  1. ls = PipePy('ls')  
  2. print(ls('-l')) 

換句話說,我們要使: 

  1. PipePy('ls', '-l') 

相當(dāng)于 

  1. PipePy('ls')('-l') 

值得慶幸的是,我們的類創(chuàng)建了惰性對象這一事實在很大程度上幫助了我們: 

  1. class PipePy:  
  2.     # __init__, etc  
  3.     def __call__(self, *args):  
  4.         args = self._args + args  
  5.         return self.__class__(*args)  
  6. ls = PipePy('ls')  
  7. print(ls('-l'))  
  8. <<< total 16  
  9. # ... -rw-r--r-- 1 kbairak kbairak  125 Jan 22 08:53 files.txt  
  10. # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb  1 21:54 main.py 
  11. # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb  1 21:54 tags 

關(guān)鍵字參數(shù)

如果要向ls傳遞更多參數(shù),則可能會遇到--sort = size。我們可以輕松地執(zhí)行l(wèi)s('-l','--sort = size')。我們可以做得更好嗎? 

  1.  class PipePy:  
  2. -    def __init__(self, *args):  
  3. +    def __init__(self, *args, **kwargs):  
  4.          self._args = args  
  5. +        self._kwargs = kwargs  
  6.          self._result = None  
  7.      def _evaluate(self):  
  8.          if self._result is not None:  
  9.              return  
  10. -        self._result = subprocess.run(self._args,  
  11. +        self._result = subprocess.run(self._convert_args(),  
  12.                                        capture_output=True 
  13.                                        text=True 
  14. +    def _convert_args(self):  
  15. +        args = [str(arg) for arg in self._args]  
  16. +        for key, value in self._kwargs.items(): 
  17. +            keykey = key.replace('_', '-')  
  18. +            args.append(f"--{key}={value}")  
  19. +        return args  
  20. -    def __call__(self, *args):  
  21. +    def __call__(self, *args, **kwargs):  
  22.          args = self._args + args  
  23. +        kwargs = {**self._kwargs, **kwargs}  
  24. -        return self.__class__(*args)  
  25. +        return self.__class__(*args, **kwargs)  
  26.      # returncode, etc 

讓我們來旋轉(zhuǎn)一下: 

  1. print(ls('-l'))  
  2. <<< total 16  
  3. # ... -rw-r--r-- 1 kbairak kbairak  125 Jan 22 08:53 files.txt  
  4. # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb  1 21:54 main.py  
  5. # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb  1 21:54 tags  
  6. print(ls('-l', sort="size"))  
  7. <<< total 16  
  8. # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb  1 21:54 main.py  
  9. # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb  1 21:54 tags  
  10. # ... -rw-r--r-- 1 kbairak kbairak  125 Jan 22 08:53 files.txt 

Piping

事情開始變得有趣起來。我們的最終目標(biāo)是能夠做到: 

  1. ls = PipePy('ls')  
  2. grep = PipePy('grep')  
  3. print(ls | grep('tags'))  
  4. <<< tags 

我們的過程是:

1、讓__init__和__call__方法接受一個僅用于關(guān)鍵字的新_pipe_input關(guān)鍵字參數(shù),該參數(shù)將保存在self上。

2、在評估期間,如果設(shè)置了_pipe_input,它將作為輸入?yún)?shù)傳遞給subprocess.run。

3、重寫__or__方法以將左操作數(shù)的結(jié)果作為pipe輸入傳遞給右操作數(shù)。 

  1.  class PipePy:  
  2. -    def __init__(self, *args, **kwargs):  
  3. +    def __init__(self, *args, _pipe_input=None, **kwargs):  
  4.          self._args = args  
  5.          self._kwargs = kwargs  
  6. +        self._pipe_input = _pipe_input  
  7.          self._result = None   
  8. -    def __call__(self, *args, **kwargs):  
  9. +    def __call__(self, *args, _pipe_input=None, **kwargs):  
  10.          args = self._args + args  
  11.          kwargs = {**self._kwargs, **kwargs}  
  12. -        return self.__class__(*args, **kwargs)  
  13. +        return self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs) 
  14.       def _evaluate(self):  
  15.          if self._result is not None:  
  16.              return  
  17.          self._result = subprocess.run(self._convert_args(),  
  18. +                                      input=self._pipe_input,  
  19.                                        capture_output=True 
  20.                                        text=True 
  21. +    def __or__(left, right):  
  22. +        return right(_pipe_input=left.stdout) 

讓我們嘗試一下(從之前稍微修改命令以證明它確實有效): 

  1. ls = PipePy('ls')  
  2. grep = PipePy('grep') 
  3. print(ls('-l') | grep('tags'))  
  4. <<< -rw-r--r-- 1 kbairak kbairak 1838 Feb  1 21:54 tags 

讓我們添加一些簡單的東西

1、真實性: 

  1. class PipePy:  
  2.     # __init__, etc  
  3.     def __bool__(self):  
  4.         return self.returncode == 0 

現(xiàn)在我們可以作出如下處理: 

  1. git = PipePy('git')  
  2. grep = PipePy('grep')  
  3. if git('branch') | grep('my_feature'):  
  4.     print("Branch 'my_feature' found") 

2、讀取/寫入文件: 

  1. class PipePy:  
  2.     # __init__, etc  
  3.     def __gt__(self, filename):  
  4.         with open(filename, 'w') as f:  
  5.             f.write(self.stdout)  
  6.     def __rshift__(self, filename):  
  7.         with open(filename, 'a') as f: 
  8.             f.write(self.stdout)  
  9.     def __lt__(self, filename):  
  10.         with open(filename) as f:  
  11.             return self(_pipe_input=f.read()) 

現(xiàn)在可以作出如下操作: 

  1. ls = PipePy('ls')  
  2. grep = PipePy('grep')  
  3. cat = PipePy('cat')  
  4. ls > 'files.txt'  
  5. print(grep('main') < 'files.txt')  
  6. <<< main.py  
  7. ls >> 'files.txt'  
  8. print(cat('files.txt'))  
  9. <<< files.txt  
  10. # ... main.py  
  11. # ... tags  
  12. # ... files.txt  
  13. # ... main.py  
  14. # ... tags 

3、迭代 

  1. class PipePy:  
  2.     # __init__, etc  
  3.     def __iter__(self):  
  4.         return iter(self.stdout.split()) 

現(xiàn)在可以作出如下操作: 

  1. ls = PipePy('ls')  
  2. for name in ls:  
  3.     print(name.upper())  
  4. <<< FILES.TXT  
  5. # ... MAIN.PY 
  6. # ... TAGS 

4、表格: 

  1. class PipePy:  
  2.     # __init__, etc  
  3.     def as_table(self):  
  4.         lines = self.stdout.splitlines()  
  5.         fields = lines[0].split()  
  6.         result = []  
  7.         for line in lines[1:]:  
  8.             item = {}  
  9.             for i, value in enumerate(line.split(maxsplit=len(fields) - 1)):  
  10.                 item[fields[i]] = value  
  11.             result.append(item)  
  12.         return result 

現(xiàn)在可以作出下面操作: 

  1. ps = PipePy('ps')  
  2. print(ps)  
  3. <<<     PID TTY          TIME CMD  
  4. # ...    4205 pts/4    00:00:00 zsh  
  5. # ...   13592 pts/4    00:00:22 ptipython  
  6. # ...   16253 pts/4    00:00:00 ps  
  7. ps.as_table()  
  8. <<< [{'PID': '4205', 'TTY': 'pts/4', 'TIME': '00:00:00', 'CMD': 'zsh'},  
  9. # ...  {'PID': '13592', 'TTY': 'pts/4', 'TIME': '00:00:22', 'CMD': 'ptipython'},  
  10. # ...  {'PID': '16208', 'TTY': 'pts/4', 'TIME': '00:00:00', 'CMD': 'ps'}] 

5、普通bash實用程序:

在子進(jìn)程中更改工作目錄不會影響當(dāng)前的腳本或python shell。與更改環(huán)境變量相同,以下內(nèi)容不是PipePy的補(bǔ)充,但很不錯: 

  1. import os  
  2. cd = os.chdir  
  3. export = os.environ.__setitem__  
  4. pwd = PipePy('pwd')  
  5. pwd  
  6. <<< /home/kbairak/prog/python/pipepy  
  7. cd('..')  
  8. pwd  
  9. <<< /home/kbairak/prog/python 

使事情看起來更shell-like

如果我在交互式shell中,則希望能夠簡單地鍵入ls并完成它。 

  1. class PipePy:  
  2.     # __init__, etc  
  3.     def __repr__(self):  
  4.         return self.stdout + self.stderr 

交互式shell 

  1. >>> ls = PipePy('ls')  
  2. >>> ls  
  3. files.txt  
  4. main.py  
  5. tags 

我們的實例是惰性的,這意味著如果我們對它們的結(jié)果感興趣,則將對它們進(jìn)行評估,此后不再進(jìn)行評估。如果我們只是想確保已執(zhí)行該操作怎么辦?例如,假設(shè)我們有以下腳本: 

  1. from pipepy import PipePy  
  2. tar = PipePy('tar')  
  3. tar('-xf', 'some_archive.tar')  
  4. print("File extracted") 

該腳本實際上不會執(zhí)行任何操作,因為tar調(diào)用實際上并未得到評估。我認(rèn)為一個不錯的慣例是,如果不帶參數(shù)調(diào)用__call__強(qiáng)制求值: 

  1.  class PipePy:  
  2.      def __call__(self, *args, _pipe_input=None, **kwargs):  
  3.          args = self._args + args  
  4.          kwargs = {**self._kwargs, **kwargs}  
  5. -        return self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs)  
  6. +        result = self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs)  
  7. +        if not args and not _pipe_input and not kwargs:  
  8. +            result._evaluate()  
  9. +        return result 

因此在編寫腳本時,如果要確保實際上已調(diào)用命令,則必須用一對括號來調(diào)用它: 

  1.  from pipepy import PipePy  
  2.  tar = PipePy('tar')  
  3. -tar('-xf', 'some_archive.tar')  
  4. +tar('-xf', 'some_archive.tar')()  
  5.  print("File extracted") 

但是,我們還沒有解決問題??紤]一下: 

  1. date = PipePy('date')  
  2. date  
  3. <<< Mon Feb  1 10:43:08 PM EET 2021  
  4. # Wait 5 seconds  
  5. date  
  6. <<< Mon Feb  1 10:43:08 PM EET 2021 

不好!date沒有改變。date對象將其_result保留在內(nèi)存中。隨后的評估實際上不會調(diào)用該命令,而只是返回存儲的值。

一種解決方案是通過使用空括號來強(qiáng)制創(chuàng)建副本: 

  1. date = PipePy('date')  
  2. date()  
  3. <<< Mon Feb  1 10:45:09 PM EET 2021  
  4. # Wait 5 seconds  
  5. date()  
  6. <<< Mon Feb  1 10:45:14 PM EET 2021 

另一個解決方案是:由PipePy構(gòu)造函數(shù)返回的實例不應(yīng)該是惰性的,但由__call__調(diào)用返回的實例將是惰性的。 

  1.  class PipePy:  
  2. -    def __init__(self, *args, _pipe_input=None, **kwargs):  
  3. +    def __init__(self, *args, _pipe_input=None_lazy=False, **kwargs):  
  4.          self._args = args  
  5.          self._kwargs = kwargs  
  6.          self._pipe_input = _pipe_input  
  7. +        self._lazy = _lazy  
  8.          self._result = None  
  9.      def __call__(self, *args, _pipe_input=None, **kwargs):  
  10.          args = self._args + args  
  11.          kwargs = {**self._kwargs, **kwargs}  
  12. -        result = self.__class__(*args, _pipe_input_pipe_input=_pipe_input, **kwargs)  
  13. +        result = self.__class__(*args, 
  14. +                                _pipe_input_pipe_input=_pipe_input,  
  15. +                                _lazy=True 
  16. +                                **kwargs) 
  17.          if not args and not _pipe_input and not kwargs:  
  18.              result._evaluate()  
  19.          return result  
  20.      def _evaluate(self):  
  21. -        if self._result is not None:  
  22. +        if self._result is not None and self._lazy:  
  23.              return  
  24.          self._result = subprocess.run(self._convert_args(),  
  25.                                        input=self._pipe_input,  
  26.                                        capture_output=True 
  27.                                        text=True

旋轉(zhuǎn)一下: 

  1. date = PipePy('date')  
  2. date  
  3. <<< Mon Feb  1 10:54:09 PM EET 2021  
  4. # Wait 5 seconds  
  5. date  
  6. <<< Mon Feb  1 10:54:14 PM EET 2021 

并且可以預(yù)見的是,使用空調(diào)用的返回值將具有之前的行為: 

  1. date = PipePy('date') 
  2. d = date()  
  3.  
  4. <<< Mon Feb  1 10:56:21 PM EET 2021  
  5. # Wait 5 seconds  
  6.  
  7. <<< Mon Feb  1 10:56:21 PM EET 2021 

沒關(guān)系 您不會期望d會更新其值。

越來越危險

好吧,ls('-l')不錯,但是如果我們像人類一樣簡單地做ls -l,那就太好了。嗯,我有個主意: 

  1. class PipePy:  
  2.     # __init__, etc  
  3.     def __sub__(left, right):  
  4.         return left(f"-{right}") 

現(xiàn)在可以作如下操作: 

  1. ls = PipePy('ls')  
  2. ls - 'l'  
  3. <<< total 16  
  4. # ... -rw-r--r-- 1 kbairak kbairak   46 Feb  1 23:04 files.txt  
  5. # ... -rw-r--r-- 1 kbairak kbairak 5425 Feb  1 21:54 main.py  
  6. # ... -rw-r--r-- 1 kbairak kbairak 1838 Feb  1 21:54 tags 

我們還有一步: 

  1. l = 'l'  
  2. ls -l 

現(xiàn)在無濟(jì)于事: 

  1. import string  
  2. for char in string.ascii_letters:  
  3.     if char in locals():  
  4.         continue 
  5.     locals()[char] = char  
  6. class PipePy:  
  7.     # __init__, etc 

更危險的事情

用locals()給了我一個靈感。為什么我們必須一直實例化PipePy?我們無法在路徑中找到所有可執(zhí)行文件,并根據(jù)它們創(chuàng)建PipePy實例嗎?我們當(dāng)然可以! 

  1. import os  
  2. import stat  
  3. for path in os.get_exec_path():  
  4.     try:  
  5.         names = os.listdir(path)  
  6.     except FileNotFoundError:  
  7.         continue  
  8.     for name in names:  
  9.         if name in locals():  
  10.             continue  
  11.         if 'x' in stat.filemode(os.lstat(os.path.join(path, name)).st_mode):  
  12.             locals()[name] = PipePy(name) 

因此,現(xiàn)在,將我們擁有的所有內(nèi)容都放在一個python文件中,并刪除腳本(這是實際bash腳本的轉(zhuǎn)錄): 

  1. from pipepy import mysqladmin, sleep, drush, grep  
  2. for i in range(10):  
  3.     if mysqladmin('ping',  
  4.                   host="mysql_drupal7" 
  5.                   user="user" 
  6.                   password="password"):  
  7.         break  
  8.     sleep(1)()  # Remember to actually invoke  
  9. if not drush('status', 'bootstrap') | grep('-q', 'Successful'):  
  10.     drush('-y', 'site-install', 'standard',  
  11.           db_url="mysql://user:password@mysql_drupal7:3306/drupal" 
  12.           acount_pass="kbairak")()  # Remember to actually invoke  
  13. drush('en', 'tmgmt_ui', 'tmgmt_entity_ui', 'tmgmt_node_ui')()  

 

責(zé)任編輯:龐桂玉 來源: Python中文社區(qū) (ID:python-china)
相關(guān)推薦

2016-07-29 11:06:48

編程PythonShell

2021-05-26 10:21:31

Python音樂軟件包

2018-03-22 11:00:45

PythonRSS

2014-03-06 09:23:19

Git服務(wù)器Github

2018-10-19 09:50:15

Linuxman手冊Linux命令

2017-05-23 14:34:58

python大數(shù)據(jù)UUID

2022-02-15 09:40:45

提示符Starship

2017-03-07 17:12:46

LinuxUbuntu發(fā)行版

2012-02-27 13:56:19

Java服務(wù)器

2022-05-17 12:45:31

LinuxLinux發(fā)行版

2021-01-06 18:10:22

ShellLoki系統(tǒng)運維

2021-06-23 16:40:58

JavaTomcatWeb

2016-09-27 11:31:34

JavaScript編程語言

2011-05-17 10:46:14

TAP

2021-08-10 09:01:48

Python網(wǎng)絡(luò)爬蟲自動化

2018-05-21 14:44:33

LinuxshellPython

2019-04-08 16:41:55

Oomox圖形應(yīng)用Linux

2019-04-04 14:23:08

GTK2GTK3Linux

2024-02-23 08:36:34

Python鼠標(biāo)鍵盤

2015-08-12 13:24:00

點贊
收藏

51CTO技術(shù)棧公眾號