美文网首页
在shell脚本中执行python的几种方式

在shell脚本中执行python的几种方式

作者: 东方胖 | 来源:发表于2021-07-14 20:58 被阅读0次

    我们什么场景下会使用shell语言

    shell语言是一种广泛用例Unix-like系统的包装脚本
    一般工程实践中,我们会在以下场景中需要使用它:

    • 1、在OS中执行一组操作系统的动作集,例如移动,复制文件,查找文件,替换文件中的字符串等,我们会将命令组合成一个shell脚本
    • 2、工具的运维,例如某个程序的start ,stop等等
    • 3、系统运维工具,比如定时的文件清理,资源利用信息上报等等
    • 4、运行一组测试,测试我们的web服务
    • 5、其他处理小数据量,处理微型逻辑的需要

    如果我们的代码量超过了100行,考虑用python是更好的选择

    为什么会遇到shell语言中嵌入python脚本的问题

    典型的场景就是,解析一个返回的json串,使用shell处理实在是有点麻烦,但是我们想快速验证一些想法,我们当然可以改成python脚本来做这件事,但是脚本已经写好了头部和尾部,就差一个数据解析了。这个时候避免写复杂的sed和awk处理命令,使用python的json库处理会很轻巧;

    另一个理由,也许这个功能的使用方式已经被固定了,上游的程序正是需要一个shell的命令,比如一个流水线CI环节的bash插件,在Jenkins套件搭建的持续集成可能有大量的这种bash脚本。

    下面我们考虑这样的典型场景:
    从http接口中获取一个接口的返回数据,然后进行处理。
    这个返回是一个比较复杂的json结构,如何进行解析?

    例如我们要处理一个这种形状的json串,它从一个远程的web服务返回回来,我们要找到名字为Zhangsan的年龄

    {
        "status": 0,
        "time": "2020-02-08",
        "datas": [
            {
                 "name": "Zhangsan",
                 "age":18,
                 "heigth" : 170,
                 "address":"Shenzhen", 
                "email": "zhangsan@google.com"
            },
            {
                "name": "Lisi",
                "age": 20,
                "heigth": 185,
                "address": "Nanjing",
                "email": "lisi@google.com"
            }
        ]
    }
    
    

    使用sed和awk来解决

    # !/bin/bash
    
    

    但我们知道,使用python解析json串会更方便,编程也是十分自然,所以一种合理的思路是在shell脚本中嵌入python逻辑来进行json解析

    嵌入python脚本有几种方法

    方案一:
    直接在shell语句中堆python语句,把结构写到控制台,后续的shell程序可以从管道中从stdin获取输出

    x='{"key1": "value1", "key2":"value2"}'
    python -c "import sys,json;sys.stdout.write(json.dumps(json.loads('$x')));"
    

    这个方法显然对简单的json结构很凑效,但是对于我们的例子不太好操作,我们可以使用下面的方式获取到第一组数据的age,但其实是hardcode,只适合返回结构里只有一组数据的情况,倘若http的接口返回的数据比较复杂,这里就很难办了,我们试图写个循环,会把python -c 后面的代码串搞的很复杂。

    #!/bin/bash
    
    function GetPersonAgeFromResp() {
        local data=$1
        if which python;
        then
            local data=$1
            python -c "import sys,json;sys.stdout.write(json.dumps(json.loads('$data').get('datas')[0].get('age'));"
            return 0
       else
           return 1
    }
    
    

    所以,又有一种更加用echo命令把python语句逐行回显在stdin,然后用管道重定向作为python的输入的方法,如下:

    echo 命令将python命令陈列,阅读清晰,维护简单,如果有什么问题可以比较直观得去排查
    个人比较推荐下面这两种方案:
    方法二:

    #!/bin/bash
    d='{"status":0,"time":"2020-02-08","datas":[{"name":"Zhangsan","age":18,"heigth":170,"address":"Shenzhen","email":"zhangsan@google.com"},{"name":
    "Lisi","age":20,"heigth":185,"address":"Nanjing","email":"lisi@google.com"}]}'
    function GetPersonAgeFromResp() {
          (
           local name=$1
           echo "import sys,json"
           echo "x=0;datas=json.loads('$2').get('datas')"
           echo "for data in datas:"
           echo "    if data.get('name') == '$name':"
           echo "        x=data.get('age');break;"
           echo "print(x)" 
         ) | python
        }
    
    GetPersonAgeFromResp 'Zhangsan' $d
    

    输出
    $ 18

    方法三:
    直接用 cat << EOF > filename.py [statements] EOF 将python语句放在一起,组织到一个文件中,然后执行,
    除了一个外层控制,写的基本上是python代码。
    这种情况适合python脚本比较长的情况。

    当然不能大量这样写,否则为什么不把所有任务写成python脚本?

    cat << EOF > /tmp/parse_json.py
    
    import json
    import sys
    import os
    
    p = {
        "name": sys.argv[1],
        "key": sys.argv[2],
        "raw_data" : sys.argv[3]
    }
    
    data_object = json.loads(p["raw_data"]).get('datas')
    for o in data_object:
        if p['key'] in o:
            age=o[p['key']]
            sys.stdout.write(str(age) + '\n')
            sys.exit(0)
    
    sys.stdout.write('Could not find the key: %s\n' % p['key'])
    sys.exit(1)
    
    EOF
    
    python /tmp/parse_json.py "zhangsan" "age" $d
    python /tmp/parse_json.py "zhangsan" "haha" $d
    

    输出

    $ 18
    Could not find the key: haha
    

    最后

    如果脚本和json串的规模很大,应当考虑放弃shell脚本,使用python脚本会更自然更快捷一些,除非你的上下文真的被固定了,可以考虑在shell脚本中嵌入python的方式

    相关文章

      网友评论

          本文标题:在shell脚本中执行python的几种方式

          本文链接:https://www.haomeiwen.com/subject/ybnypltx.html