我们什么场景下会使用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的方式
网友评论