Python调用shell脚本命令的方法

本文基于Python和shell脚本语法编写了根据CONTCAR/POSCAR文件批量计算键长的脚本。

Python调用shell脚本命令的方法:

  1. os.system
    1
    2
    3
    4
    os.system("rm atom_* extract.sh")  # 删除以atom_开头的文件和extract.sh文件
    os.system("bash extract.sh") # 执行无外参的extract.sh脚本文件
    os.system("bash extract.sh tonny") # 执行extract.sh文件,并将tonny字符串传给extract.sh脚本
    os.system("python test.py tonny") #执行test.py文件,并将tonny字符串传给test.py
  2. os.popen 可以将执行结果返回给一个对象
    1
    2
    files = os.popen('bash -c "find -type f -name CONTCAR"')  # 列出当前目录下所有含有CONTCAR文件的文件路径,并返回files对象
    file_path = files.read().split() # 读取files对象中的内容,并切割字符串
  3. subprocess库
    1
    2
    3
    4
    5
    cmd1 = "sed -n 6p " + CONTCAR + " > atom_names"
    subprocess.call(cmd1,shell=True)
    subprocess.run("rm atom_* vect",shell=True) # delete temporary files
    # find folders containing CONTCAR file and save file path into a list
    files = subprocess.Popen('bash -c "find -type f -name CONTCAR"',stdout=subprocess.PIPE).stdout.read().decode(encoding="utf-8").split()
  4. envy模块

脚本内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#########################################################################################
#### usage: python dist_in_CONTCAR.py Atom1 Atom2 [Atom3 [Atom4]] ####
#### example: python dist_in_CONTCAR.py Pt1 O1 ####
#### python dist_in_CONTCAR.py Pt1 O1 O2 ####
#### python dist_in_CONTCAR.py N1 Pt1 O1 O2 ####
#### The Atom Names are specified same with those Atom Names shown in VESTA software ####
#########################################################################################
import subprocess
import numpy as np
import sys


def dist(a,b):
""" read coordinates of two points and return distance of two points"""
"""vector module --> |A - B|"""
# distance = np.sqrt(np.dot(a - b, a - b))
distance = np.linalg.norm(a-b)
return distance


def angle(a,b,c):
"""read coordinates of three points and return angle of <a_b_c"""
"""cos_AB = A * B / (|A| * |B|)"""
cos_abc = np.dot(a-b, c-b) / (np.linalg.norm(a-b) * np.linalg.norm(c-b))
angle_abc = np.arccos(cos_abc) * 180 / np.pi
return angle_abc


def diheral_angle(a,b,c,d):
"""read coordinates of four points and return diheral angle of plane abc and bcd"""
NormVect_abc = np.cross(b-a,c-a) # NormalVector = A X B
NormVect_bcd = np.cross(b-c,d-c)
cosA = np.dot(NormVect_abc,NormVect_bcd) / (np.linalg.norm(NormVect_abc) * np.linalg.norm(NormVect_bcd))
angle_A = np.arccos(cosA) * 180 / np.pi
if angle_A > 90:
return 180 - angle_A
else:
return angle_A


def file_process(CONTCAR):
"""read data from CONTCAR file and return the distance between ATOM1 and Atom2"""

#1. use bash script to extract atom coordinate, atom name and cell parameter into temporary files
cmd1 = "sed -n 6p " + CONTCAR + " > atom_names"
cmd2 = "sed -n 7p " + CONTCAR + " > atom_nums"
cmd3 = "sed -n 3,5p " + CONTCAR + " > vect"
cmd4 = "sed 1,8d " + CONTCAR + " | grep -v E > atom_coors"
subprocess.call(cmd1,shell=True)
subprocess.call(cmd2,shell=True)
subprocess.call(cmd3,shell=True)
subprocess.call(cmd4,shell=True)

#2. data process
matrix_trans = np.loadtxt('vect') # read cell parameters into an array
atom_nums = np.loadtxt('atom_nums').astype(int) # read atom nums into an array
coor = np.loadtxt('atom_coors') # read atom coordinates into an array
with open('atom_names','r') as f:
atom_names = f.readline().split() # read atom names into a list
name = []
for i in range(len(atom_names)):
for j in range(atom_nums[i]):
name.append(atom_names[i]+str(j+1)) # add nums on atoms and save into name list

subprocess.run("rm atom_* vect",shell=True) # delete temporary files
return matrix_trans,coor,name


def distAB(CONTCAR,Atom1,Atom2):
matrix_trans,coor,name = file_process(CONTCAR)
a = np.dot(matrix_trans.T,coor[name.index(Atom1)])
b = np.dot(matrix_trans.T,coor[name.index(Atom2)])
dist_value = dist(a,b)
return dist_value


def angleABC(CONTCAR,Atom1,Atom2,Atom3):
matrix_trans,coor,name = file_process(CONTCAR)
a = np.dot(matrix_trans.T,coor[name.index(Atom1)])
b = np.dot(matrix_trans.T,coor[name.index(Atom2)])
c = np.dot(matrix_trans.T,coor[name.index(Atom3)])
angle_value = angle(a,b,c)
return angle_value


def diheralABCD(CONTCAR,Atom1,Atom2,Atom3,Atom4):
matrix_trans,coor,name = file_process(CONTCAR)
a = np.dot(matrix_trans.T,coor[name.index(Atom1)])
b = np.dot(matrix_trans.T,coor[name.index(Atom2)])
c = np.dot(matrix_trans.T,coor[name.index(Atom3)])
d = np.dot(matrix_trans.T,coor[name.index(Atom4)])
angle_value = diheral_angle(a,b,c,d)
return angle_value


#1. find folders containing CONTCAR file and save file path into a list
ArgvNum = len(sys.argv)
Atom = []
for i in range(ArgvNum):
Atom.append(sys.argv[i])
cmd5 = 'bash -c "find -type f -name CONTCAR"'
files = subprocess.Popen(cmd5,stdout=subprocess.PIPE).stdout.read().decode(encoding="utf-8").split()
#2. for each CONTCAR file, calculate the distance between two atoms
value_list = []
file_name_list = []
for CONTCAR in files:
file_name_list.append(CONTCAR.lstrip('./').rstrip('CONTCAR').replace('/','__'))
if ArgvNum == 3:
value = distAB(CONTCAR,Atom[1],Atom[2])
elif ArgvNum == 4:
value = angleABC(CONTCAR,Atom[1],Atom[2],Atom[3])
elif ArgvNum == 5:
value = diheralABCD(CONTCAR,Atom[1],Atom[2],Atom[3],Atom[4])
else:
break
value_list.append(value)
#3. write the file name and distance value into a file 'dist_between_Atom1_and_Atom2.dat'
with open('output.dat', 'w') as file:
file.write("species"+" " * 13 + "values\n")
for i in range(len(files)):
file.write("{:<20}{:>.3f}\n".format(file_name_list[i],value_list[i]))