在PostgreSQL中使用 plpythonu 调用系统命令

1 minute read

背景

有些时候,为了方便调度,可能需要在数据库中执行一些系统命令。

例如清理OSS对象存储,查看磁盘使用量等。

使用plpythonu,可以简便的调用系统命令。

例子

1、执行命令,返回命令的返回值。

create or replace function exec_cmd1(cmd text) returns int as $$    
import os      
v = os.system('ls ' + cmd)  
return v  
$$ language plpythonu;    

2、执行命令,返回命令的标准输出。

create or replace function exec_cmd2(cmd text) returns setof text as $$    
import os      
v = os.popen('ls ' + cmd)  
return v  
$$ language plpythonu;    

3、查询举例

postgres=# select exec_cmd1('-laZ /data01') ;  
 exec_cmd1   
-----------  
         0  
(1 row)  
  
postgres=# select rtrim(exec_cmd2('-laZ /data01'), E'\n') ;  
                                   rtrim                                      
----------------------------------------------------------------------------  
 drwxr-xr-x  root     root     ?                                .  
 dr-xr-xr-x. root     root     system_u:object_r:root_t:s0      ..  
 drwx------  root     root     ?                                lost+found  
 drwx------  postgres postgres ?                                pg_root1921  
 drwx------  postgres postgres ?                                pg_root1922  
 drwxr-xr-x  postgres postgres ?                                pg_rpm  
(6 rows)  

安全

1、小心注入,与SQL注入类似例子:

postgres=# select rtrim(exec_cmd2('-laZ /data01; df -h;'), E'\n') ;  
                                   rtrim                                      
----------------------------------------------------------------------------  
 drwxr-xr-x  root     root     ?                                .  
 dr-xr-xr-x. root     root     system_u:object_r:root_t:s0      ..  
 drwx------  root     root     ?                                lost+found  
 drwx------  postgres postgres ?                                pg_root1921  
 drwx------  postgres postgres ?                                pg_root1922  
 drwxr-xr-x  postgres postgres ?                                pg_rpm  
 文件系统        容量  已用  可用 已用% 挂载点  
 /dev/vda1        40G  2.6G   35G    7% /  
 devtmpfs        487M     0  487M    0% /dev  
 tmpfs           497M  108K  497M    1% /dev/shm  
 tmpfs           497M  388K  496M    1% /run  
 tmpfs           497M     0  497M    0% /sys/fs/cgroup  
 /dev/vdb1       200G  3.6G  197G    2% /data01  
 tmpfs           100M     0  100M    0% /run/user/0  
(14 rows)  

参考

python调用Shell脚本:os.system(cmd)或os.popen(cmd)

python调用Shell脚本,有两种方法:os.system(cmd)或os.popen(cmd),前者返回值是脚本的退出状态码,后者的返回值是脚本执行过程中的输出内容。实际使用时视需求情况而选择。

现假定有一个shell脚本test.sh:

#!/bin/bash  
  
1. echo "hello world!"  
  
2. exit 3  
  
os.system(cmd):  

该 方法在调用完shell脚本后,返回一个16位的二进制数,低位为杀死所调用脚本的信号号码,高位为脚本的退出状态码,即脚本中“exit 1”的代码执行后,os.system函数返回值的高位数则是1,如果低位数是0的情况下,则函数的返回值是0×100,换算为10进制得到256。

如果我们需要获得os.system的正确返回值,那使用位移运算可以还原返回值:

1. >>>  n = os.system(test.sh)  
  
2. >>> n >> 8  
  
3. >>> 3  
  
os.popen(cmd):  

这种调用方式是通过管道的方式来实现,函数返回一个file-like的对象,里面的内容是脚本输出的内容(可简单理解为echo输出的内容)。使用os.popen调用test.sh的情况:

python调用Shell脚本,有两种方法:os.system(cmd)或os.popen(cmd),前者返回值是脚本的退出状态码,后者的返回值是脚本执行过程中的输出内容。实际使用时视需求情况而选择。

明显地,像调用”ls”这样的shell命令,应该使用popen的方法来获得内容。

Flag Counter

digoal’s 大量PostgreSQL文章入口