用Python寫個Linux系統命令

這篇文章介紹如何寫個系統命令以及我爲什麼要寫命令

“一切皆文件”是linux的基本哲學之一,我們在linux下執行的諸如ls之類的命令實際上都是去執行了系統上的某個文件,which命令可以查看到我們執行的命令對應的是系統上的哪個文件,例如常用的ls命令實際上就是執行了/bin/ls這個文件

root@ops-coffee:~# which ls
/bin/ls

基於此,我們就知道了定義一個命令很簡單,只需要寫個可執行的文件就行了,python的標準模塊argparse就可以幫助我們快速方便的構建一個用戶友好的命令

argparse

相比於自己實現個命令文件,argparse模塊能夠自動生成幫助和使用手冊,並在用戶傳入無效參數時報錯。一個簡單的示例如下

#!/usr/bin/env python3
# coding:utf8

import argparse

parser = argparse.ArgumentParser(description='整數處理')
parser.add_argument('integers', type=int, help='要處理整數')

args = parser.parse_args()
print(args.integers)

這個示例的意思是接收一個數字,並將這個數字輸出,接下來看一下詳細的解釋

首先創建了一個ArgumentParser對象,ArgumentParser對象有很多參數可以選擇,這裏的description定義在參數幫助文檔之前顯示的文本,通常用來定義這個程序做什麼以及怎麼做

parser = argparse.ArgumentParser(description='整數處理')

然後通過add_argumentArgumentParser對象添加參數,第一個參數integers爲參數名,type指定類型爲inthelp指定這個字段的幫助信息

parser.add_argument('integers', type=int, help='要處理的整數')

通過調用ArgumentParser對象的parse_args方法返回一個具有所有參數屬性的對象

args = parser.parse_args()

最後通過args.參數名獲取到傳入的參數值

print(args.integers)

執行

我們將以上文件命名爲opscoffee,並賦予執行權限,放在系統環境變量/bin下,就可以當作命令直接執行了

# chmod +x opscoffee
# mv opscoffee /bin/

如果直接執行opscoffee命令的話將會收到一個報錯,提示你必須有一個參數integers

# opscoffee
usage: opscoffee [-h] integers
opscoffee: error: the following arguments are required: integers

同時也通過usage告訴了你這個命令的用法,默認有一個-h參數可以打印命令幫助

# opscoffee -h
usage: opscoffee [-h] integers

整數處理

positional arguments:
  integers    要處理的整數

optional arguments:
  -h, --help  show this help message and exit

這就是使用argparse模塊的好處,自動生成幫助,提供友好的使用體驗

參數

argparse能實現的遠不止於此,還有更加強大的功能,主要在於add_argument方法參數的運用,以下以一些例子來學習下一些常用的參數

可選參數

當我們在參數名前添加-或者--時,argparse會默認認爲這是一個可選參數,可以不傳值,例如

parser.add_argument('--age', type=int, help='年齡')

可選參數當沒有傳值時的默認值爲None,可以通過default來設置默認值

parser.add_argument('--age', type=int, default=37, help='年齡')

當沒有參數--age時,顯示default設置的值(沒有設置default則顯示None),有--age則顯示--age指定的值

# opscoffee
37
# opscoffee --age 38
38

如果你想讓可選參數也變成必選的,則只需要設置required=True即可

parser.add_argument('--age', type=int, default=37, required=True, help='年齡')

type

type用來指定參數的類型,允許任何類型檢查和類型轉換,例如strintfloatopen甚至是你自定義的方法都可以。

#!/usr/bin/env python3
# coding:utf8

import argparse

def even(string):
    value = int(string)

    if value%2!=0:
        msg = "%r 不是偶數" % string
        raise argparse.ArgumentTypeError(msg)
    return value

parser = argparse.ArgumentParser(description='整數處理')
parser.add_argument('integers', type=even, help='要處理的整數')

args = parser.parse_args()
print(args.integers)

以上命令接收一個參數integers,並將其type設置爲了自定義方法even,這個方法會判斷用戶輸入的數字是否是偶數,如果不是則報錯,執行結果如下

# opscoffee 1
usage: opscoffee [-h] integers
opscoffee: error: argument integers: '1' 不是偶數
# opscoffee 2
2

choices

choices參數可以限制參數的範圍,例如我們只想讓用戶從opscoffee兩個參數中選擇一個輸入,則可以這樣用

parser.add_argument('site', choices=['ops','coffee'], help='Site')

那麼當我們輸入的內容不是opscoffee時,則報錯

# opscoffee cn
usage: opscoffee [-h] {ops,coffee}
opscoffee: error: argument site: invalid choice: 'cn' (choose from 'ops', 'coffee')

nargs

通常我們在argparse中定義的參數數量與傳入的參數數量應當相等,但有些時候我們需要接收未知數量的參數,nargs就可以幫助我們

nargs支持以下值:N(整數)、'?''*''+'argarse.REMAINDER

N: 表示N個參數會被聚集到一個列表中,例如

import argparse

parser = argparse.ArgumentParser(description='整數處理')
parser.add_argument('integers', type=int, nargs=2, help='要處理的整數')

args = parser.parse_args()
print(args.integers)

將輸出

# opscoffee 9 10 
[9, 10]

'?': 表示使用一個或不使用參數,當不傳參數時,默認爲None

parser.add_argument('integers', type=int, nargs='?', help='要處理的整數')

'*': 表示使用所有參數,參數個數可以爲0

parser.add_argument('integers', type=int, nargs='*', help='要處理的整數')

'+':'*'類似,但至少要有一個參數,否則將會報錯

parser.add_argument('integers', type=int, nargs='+', help='要處理的整數')

自定義命令

先說我爲什麼要寫個系統命令?

文章『Probius:一個功能強大的自定義任務系統』中介紹了我們的自定義任務系統,這個系統可以用來編排任務,而在編排CICD任務中會用到配置文件,我們的配置文件都是通過Kerrigan配置中心來管理的,目前獲取配置中心的配置主要有兩種方法

1.  配合confd服務自動拉取更新,詳細內容可以查看這篇文章:中小團隊落地配置中心詳解

2.  配置中心提供API,可以通過API獲取配置內容

腳本里如果想要使用配置中心的配置,則只能通過API的方式去獲取,這樣就要在每個需要用到配置的地方寫一段代碼來獲取及處理,不僅會出現大量的重複代碼,並且非常的不優雅,更爲重要的是請求API的Token將會出現在腳本里,帶來一定的安全風險

基於以上考慮,寫個自定義命令來做這件事情更爲妥當,於是便寫了下邊這個命令

#!/usr/bin/env python3
# coding:utf8

# 這是一個系統命令用來獲取kerrigan配置中心的配置並寫入本地文件,需要將此文件copy到目錄/bin下

import sys
import argparse
import requests

parser = argparse.ArgumentParser(description='獲取配置中心Kerrigan配置')

parser.add_argument('configkey', type=str, help='配置中心中文件的Key')
parser.add_argument('localfile', type=str, help='保存到本地文件的路徑')

args = parser.parse_args()

# 獲得傳入的參數
configkey = args.configkey
localfile = args.localfile

header = {
    'Authorization': 'Token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.JTdCJTIyZXhwJTIyJTNBMTkwNjcwNzAyMCUyQyUyMmlhdCUyMiUzQTE1OTEzNDcwMjAlMkMlMjJkYXRhJTIyJTNBJTdCJTIydXNlcm5hbWUlMjIlM0ElMjJwcm9iaXVzQG9wcy1jb2ZmZWUuY24lMjIlN0QlN0Q.ops1ZNhq19XSEL2PUo-iQqzbhimDnpFiYc_7EUXftF4'}

uri = 'http://kerrigan.ops-coffee.cn/api/config/?key=' + configkey
r = requests.get(uri, headers=header)

if r.json()['state']:
    content = r.json()['message']['content']

    try:
        with open(localfile, 'w') as f:
            f.write(content)

        sys.exit(0)
    except Exception as e:
        print('write local file failed: ', str(e))
        sys.exit(3)
else:
    print('get config failed: ', r.json()['message'])
    sys.exit(1)

以上代碼的意思是根據傳入的key和file路徑,去配置中心獲取對應配置文件的內容並寫入到本地file中。需要注意的是exit,返回合適的退出狀態是個很好的習慣,這樣我們就可以通過$?來獲取命令執行成功還是失敗

將此文件命名爲getconfig並移動到/bin目錄下添加執行權限,就可以在系統任何地方使用getconfig命令了

# getconfig /conf/coffee/prod/docker/Dockerfile  /home/project/coffee/Dockerfile
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章