原文正则表达式 文件格式处理 shell脚本
gitbook访问太慢了,重置文章到此处
正则表达式
定义
正则表达式通常被用来检索、替换那些匹配某个模式的文本
不仅在linux命令中使用,在php,python等语言和notepad++等文本编辑器中也经常用于快速检索某串字符
语系对正规表示法的影响
不同语系的编码数据并不相同,如
- LANG=C 时:0 1 2 3 4 … A B C D … Z a b c d …z
- LANG=zh_TW 时:0 1 2 3 4 … a A b B c C d D … z Z
当我们使用[A-Z]提取正则表达式时,对LANG=C提取是A-Z的所有大写字母,而LANG=Zh_TW提取A-Z和a-z
不过在linux等常用情况都默认是LANG=C语系,我们不用了解太多
语法
先看下具体规则,再对vasp输出文件进行处理
特殊符号
感觉这个不是很常用
基础正规表示法字符汇整
这个好记忆,而且好用
高级用法
有点用处
注意鸟哥给的示例使用的是egrap
,grep是不支持这些语法的
grep
语法
grep [-acinv] [--color=auto] '搜寻字符串' filename
选项与参数:
-a :将 binary 文件以 text 文件的方式搜寻数据
-c :计算找到 '搜寻字符串' 的次数
-i :忽略大小写的不同,默认大小写不同
-n :顺便输出行号
-v :反向选择,亦即显示出没有 '搜寻字符串' 内容的那一行!
--color=auto :可以将找到的关键词部分加上颜色的显示喔!
示例
以我用vasp计算时某次结构优化后某OUTCAR为例
下载地址https://cndaqiang.github.io/web/file/2018/sf10/linux/OUTCAR
下载命令
wget https://cndaqiang.github.io/web/file/2018/sf10/linux/OUTCAR
使用此OUTCAR文件进行下列示例
$ grep "E-fermi" OUTCAR
#搜索计算的费米能级
E-fermi : -1.7694 XC(G=0): -4.2437 alpha+bet : -3.6065
E-fermi : -1.6967 XC(G=0): -4.2322 alpha+bet : -3.6065
E-fermi : -1.7565 XC(G=0): -4.2059 alpha+bet : -3.6065
E-fermi : -1.8226 XC(G=0): -4.2557 alpha+bet : -3.6065
E-fermi : -1.5143 XC(G=0): -4.0949 alpha+bet : -3.6065
E-fermi : -1.4183 XC(G=0): -4.0606 alpha+bet : -3.6065
E-fermi : -1.4229 XC(G=0): -4.1155 alpha+bet : -3.6065
E-fermi : -1.4078 XC(G=0): -4.1161 alpha+bet : -3.6065
$ grep -c "E-fermi" OUTCAR
8
$ grep -ci "E-fermi" OUTCAR
8
$ grep -cn "E-fermi" OUTCAR
#有参数c时,只输出结果数,不输出结果
8
$ grep -n "E-fermi" OUTCAR
#输出行号
1956: E-fermi : -1.7694 XC(G=0): -4.2437 alpha+bet : -3.6065
3601: E-fermi : -1.6967 XC(G=0): -4.2322 alpha+bet : -3.6065
5451: E-fermi : -1.7565 XC(G=0): -4.2059 alpha+bet : -3.6065
7351: E-fermi : -1.8226 XC(G=0): -4.2557 alpha+bet : -3.6065
9201: E-fermi : -1.5143 XC(G=0): -4.0949 alpha+bet : -3.6065
10852: E-fermi : -1.4183 XC(G=0): -4.0606 alpha+bet : -3.6065
12007: E-fermi : -1.4229 XC(G=0): -4.1155 alpha+bet : -3.6065
13113: E-fermi : -1.4078 XC(G=0): -4.1161 alpha+bet : -3.6065
因为我结构优化时,驰豫了8次离子步,所以有8个结果
grep支持正则表达式
特殊符号示例
$ grep "[:alpha:]-fermi" OUTCAR
grep: 字符类的语法是 [[:space:]],而非 [:space:]
$ grep "[[:alpha:]]-fermi" OUTCAR
E-fermi : -1.7694 XC(G=0): -4.2437 alpha+bet : -3.6065
E-fermi : -1.6967 XC(G=0): -4.2322 alpha+bet : -3.6065
E-fermi : -1.7565 XC(G=0): -4.2059 alpha+bet : -3.6065
E-fermi : -1.8226 XC(G=0): -4.2557 alpha+bet : -3.6065
E-fermi : -1.5143 XC(G=0): -4.0949 alpha+bet : -3.6065
E-fermi : -1.4183 XC(G=0): -4.0606 alpha+bet : -3.6065
E-fermi : -1.4229 XC(G=0): -4.1155 alpha+bet : -3.6065
E-fermi : -1.4078 XC(G=0): -4.1161 alpha+bet : -3.6065
基础正规表示法字符示例
查看最终是否收敛,收敛时,OUTCAR内会有reached required accuracy - stopping structural energy 这句,我们搜索reached
我们发现有很多,可以采用以 reached为开头搜索
$ grep "reached" OUTCAR
------------------------ aborting loop because EDIFF is reached ----------------------------------------
------------------------ aborting loop because EDIFF is reached ----------------------------------------
------------------------ aborting loop because EDIFF is reached ----------------------------------------
------------------------ aborting loop because EDIFF is reached ----------------------------------------
------------------------ aborting loop because EDIFF is reached ----------------------------------------
------------------------ aborting loop because EDIFF is reached ----------------------------------------
------------------------ aborting loop because EDIFF is reached ----------------------------------------
------------------------ aborting loop because EDIFF is reached ----------------------------------------
reached required accuracy - stopping structural energy minimisation
$ grep "^ reached" OUTCAR
reached required accuracy - stopping structural energy minimisation
#.表示一定有一个字节
$ grep "^ r.ached" OUTCAR
reached required accuracy - stopping structural energy minimisation
egrep示例高级语法
能搜到的太多了,我就只显示行数了,可以看到?(零个或1个)比+(至少1个)搜到的结果多
$ egrep -c "[0-9]+" OUTCAR
9858
$ egrep -c "[0-9]?" OUTCAR
13956
读取代码/配置跳过行首为#
的注释行
#去除#号开头
grep StateSaveLocation /etc/slurm-llnl/slurm.conf | grep -v "^ *#"
root@mommint:/opt# grep StateSaveLocation /etc/slurm-llnl/slurm.conf | grep -v "^ *#"
StateSaveLocation=/var/spool/slurm-llnl
提取空格开头,首单词确定的行
(python37) [HUAIROU cndaqiang@login01 0.00015y_y0_0.117_ramdom10k]$grep ^[[:blank:]]*temperature result | head -3
temperature = 106.78531944 K
temperature = 114.58036450 K
temperature = 149.09400799 K
更多示例
鸟哥给的示例,下载地址https://cndaqiang.github.io/web/file/2018/sf10/linux/regular_express.txt
报错
Binary file result matches
也许是因为文件太大的原因, 文本文件被识别为二进制文件, 添加-a
参数
-a
–text equivalent to –binary-files=text,即让二进制文件等价于文本。grep命令提示”binary file matches **.log”解决方法
(python37) [HUAIROU cndaqiang@login01 edt0.5RunNVE0_nofield]$grep MD result
Binary file result matches
(python37) [HUAIROU cndaqiang@login01 edt0.5RunNVE0_nofield]$grep -a MD result | head -1
MD5 check sum: 7de1f3f262afbb95e2ae740af32c06ec
sed
功能
对文件增加,替换,删除,插入,打印等命令
以行为处理单位
语法
sed支持管道语法
[root@www ~]## sed [-nefr] [动作] 文件名/管道输入
选项与参数:
-n :使用安静(silent)模式。在一般 sed 的用法中,所有来自 STDIN
的数据一般都会被列出到萤幕上。但如果加上 -n 参数后,则只有经过
sed 特殊处理的那一行(或者动作)才会被列出来。
-e :直接在命令列模式上进行 sed 的动作编辑;
-f :直接将 sed 的动作写在一个文件内, -f filename 则可以运行 filename 内的
sed 动作;
-r :sed 的动作支持的是延伸型正规表示法的语法。(默认是基础正规表示法语法)
-i :直接修改读取的文件内容,而不是由萤幕输出。
动作说明: [n1[,n2]]function
n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作
是需要在 10 到 20 行之间进行的,则『 10,20[动作行为] 』
function 有底下这些咚咚:
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行!
d :删除,因为是删除啊,所以 d 后面通常不接任何咚咚;
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
p :列印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~
s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配
正规表示法!例如 1,20s/old/new/g 就是啦!
鸟哥:sed 后面接的动作,请务必以 ‘’ 两个单引号括住喔!
示例
选项参数
-n只显示处理的行
$ ls
ls.txt nohup.out OUTCAR
#在原来的第2,3行前插入hello
$ ls |sed '2,3ihello'
ls.txt
hello
nohup.out
hello
OUTCAR
$ ls |sed -n '2,3ihello'
hello
hello
-e是默认的,直接输入动作,-f 文件名,从文件中读入动作命令
#将命令保存到sedfile文件
$ echo 2,3ihello word >sedfile
## -f 调用文件
$ ls |sed -f sedfile
ls.txt
hello word
nohup.out
hello word
OUTCAR
sedfile
-i 不显示在屏幕上
鸟哥:这功能非常有帮助!举例来说,如果你有一个 100 万行的文件,你要在第 100 行加某些文字,此时使用 vim 可能会疯掉!因为文件太大了!那怎办?就利用 sed 啊!透过 sed 直接修改/取代的功能,你甚至不需要使用 vim 去修订!很棒吧!
$ ls > ls_out_file
$ cat ls_out_file
ls_out_file
ls.txt
nohup.out
OUTCAR
sedfile
$ sed -i -f sedfile ls_out_file
$ cat ls_out_file
ls_out_file
hello word
ls.txt
hello word
nohup.out
OUTCAR
sedfile
动作参数
去除行行尾的空格和tab
cat XXX | sed 's/[ \t]*$//g' | sed 's/^[ \t]*//g'
举一个取代的例子,语法:
sed 's/要被取代的字串/新的字串/g'
我们测试晶格常数时,可以使用sed批量替换成POSCAT
https://cndaqiang.github.io/web/file/2018/sf10/linux/POSCAR
$ cat POSCAR
CoCl2 Vaccum=15
1.0
3.5269000530 0.0000000000 0.0000000000
-1.7634500265 3.0543850425 0.0000000000
0.0000000000 0.0000000000 17.6408996582
Co Cl
1 2
Direct
0.000000000 0.000000000 0.074850000
0.666670032 0.333329992 0.000000000
0.333329986 0.666669976 0.149700000
$ cat POSCAR |sed '2s/1.0/1.1/g' >POSCAR1.1
$ cat POSCAR1.1
CoCl2 Vaccum=15
1.1
3.5269000530 0.0000000000 0.0000000000
-1.7634500265 3.0543850425 0.0000000000
0.0000000000 0.0000000000 17.6408996582
Co Cl
1 2
Direct
0.000000000 0.000000000 0.074850000
0.666670032 0.333329992 0.000000000
0.333329986 0.666669976 0.149700000
其他
sed -n "1~${eachstep}p" input
从第1行开始,每隔eachstep行,进行输出。
问题
推荐使用双引号
双引号能识别变量$变量名
,单引号不识别变量
修改某个文件
错误做法
cat $global |sed "8s/${olduser}/${newuser}/g" > $global
应该
sed -i "8s/${olduser}/${newuser}/g" $global
awk
功能
相较于 sed以行为处理单位, awk 则把一行分成数个小单位来处理。
语法
awk支持管道命令
awk '条件类型1{动作1} 条件类型2{动作2} ...' filename
特殊字符转移
替换](/in
为](/out/)
,即替换markdown的url
sed -i s/\]\(\\/${in}/\]\(\\/${out}\\//g $i
sed -i s/\]\(\\/\\//\]\(\\//g $i
使用\) \] \\( \\[ \\/
分别转义) ] ( [ /
变量
awk内建变量
变量名称 | 意义 |
---|---|
NF | 每一行分成的单元数 |
NR | 目前 awk 所处理的是第几行数据 |
FS | 目前的分隔字节,默认是空白键 |
$n |
当前行的第n个单元 |
print后面默认只能识别内建变量,使用-v
指定自定义变量, 可用于循环提取每一列的各值, 如原子坐标
echo "hello world" |awk -vorder=2 '{ print $order }'
world
条件类型
逻辑类型 | 意义 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
== | 等于 |
!= | 不等于 |
&& | 与 |
\\ |
或 |
注意等于是==
动作
print $1,$2
printf "%f %f\n", $1,$2
需要自己添加换行符\n
示例
以#开头的
awk '/#/' file.out
跳过#开头
awk '!/#/' file.out
跳过#开头并输出第6个
awk '!/#/{ print $6}' file.out
从POSCAR中,提取3到5行,设置分隔符为” “,
awk的printf动作和printf有点不同,printf "格式控制",变量,变量
,使用逗号隔开变量
$ cat POSCAR
CoCl2 Vaccum=15
1.0
3.5269000530 0.0000000000 0.0000000000
-1.7634500265 3.0543850425 0.0000000000
0.0000000000 0.0000000000 17.6408996582
Co Cl
1 2
Direct
0.000000000 0.000000000 0.074850000
0.666670032 0.333329992 0.000000000
0.333329986 0.666669976 0.149700000
$ sed -n 3,5p POSCAR
3.5269000530 0.0000000000 0.0000000000
-1.7634500265 3.0543850425 0.0000000000
0.0000000000 0.0000000000 17.6408996582
#\表示换行继续输入
$ sed -n 3,5p POSCAR | awk '{FS=" "} (($1!=0 && $2==0)||$1==0) \
> {printf "%f\t%f\tNF=%i\tNR=%i\tFS=%s__\n",$1,$2,NF,NR,FS}'
3.526900 0.000000 NF=3 NR=1 FS= __
0.000000 0.000000 NF=3 NR=3 FS= __
从OUTCAR中提取费米能级
tail -1
表示最后一行,之前秋豪讲过
$ grep "E-fermi" OUTCAR
\ E-fermi : -1.7694 XC(G=0): -4.2437 alpha+bet : -3.6065
E-fermi : -1.6967 XC(G=0): -4.2322 alpha+bet : -3.6065
E-fermi : -1.7565 XC(G=0): -4.2059 alpha+bet : -3.6065
E-fermi : -1.8226 XC(G=0): -4.2557 alpha+bet : -3.6065
E-fermi : -1.5143 XC(G=0): -4.0949 alpha+bet : -3.6065
E-fermi : -1.4183 XC(G=0): -4.0606 alpha+bet : -3.6065
E-fermi : -1.4229 XC(G=0): -4.1155 alpha+bet : -3.6065
E-fermi : -1.4078 XC(G=0): -4.1161 alpha+bet : -3.6065
$ grep "E-fermi" OUTCAR |awk '{printf "%f\n",$5}'
-4.243700
-4.232200
-4.205900
-4.255700
-4.094900
-4.060600
-4.115500
-4.116100
$ grep "E-fermi" OUTCAR |awk '{printf "%f\n",$5}'|tail -1
-4.116100
从siesta输出提取偶极矩
$(NF-1)表示倒数第二列
grep "Electric dipole (a.u.)" result | awk '{printf "%f\t%f\t%f\n", $(NF-2),$(NF-1),$NF }' > dipole
补充应用
awk求和求平均
cat count.txt | awk '{sum+=$1} END {print "Average = ", sum}'
cat count.txt | awk '{sum+=$1} END {print "Average = ", sum/NR}'
grep -v "#" simulation.out | awk '{sum+=$6} END {printf "Average = %1.10e \n", sum/NR}'
方差
col=$(grep $phy $i | awk '{ print $3+1 }')
mean=$(grep -v "#" $i | awk '{sum+=$'$col'} END {printf "%1.10e", sum/NR}')
#注意mean前面不能有空格,不然下面的'$mean'会报错
std=$(grep -v "#" $i | awk '{sum+=($'$col' - '$mean')**2 } END {printf "%1.10e", sqrt(sum/(NR-1))}')
echo $i $phy mean=$mean std=$std
最大/小值
tail -n +$startline $INPUTFILE | awk 'BEGIN {max = 0} {if ($2+0 > max+0) max=$2} END {print "Max=", max}'
tail -n +$startline $INPUTFILE | awk 'BEGIN {min = 9999999} {if ($2+0 < min+0) max=$2} END {print "Min=", min}'
#同时求最大最小, BEGIN和END的用法
tail -n +$startline $INPUTFILE | awk 'BEGIN {max = -9999; min=9999} {if ($2+0 > max+0) max=$2; if ($2+0 < min+0) min=$2 } END {printf "%15.10f,%15.10f \n" , max, min }'
awk中使用系统变量
使用'$var'
分隔开即可,注意var可以var=6
但是不可以是var=' 6'
,即不能有空格
可以使用循环生成var=",$3,$5,$6"
,但是var中绝对不能出现空格
col=6
grep -v "#" simulation.out | awk '{sum+=$'$col'} END {printf "Average = %1.10e \n", sum/NR}'
awk print单引号
用单引号隔开,在用转义字符即可,"'\''%s'\''"
内部一定不要有空格!!!
tail -n +$startline $INPUTFILE | awk '{printf "'\''%s'\''",$1}'
awk的计算功能
(python37) cndaqiang@mommint:~$ echo | awk '{ print sin(3.141592653/2) , cos(3.141592653/2) }'
1 2.94897e-10
awk在每行内添加值
如每行首添加行号
awk '{print NR,"\t",$0 }' 3,3,3
awk将修改结果直接写入文件
错误示范,下面这种方式,只写入了几行,后面的数据都没有了
awk '{print NR,"\t",$0 > "3,3,3" }' 3,3,3
awk提取特定文字之间的内容
提取Begin PSP_UPF
到END_PSP
之间的内容
awk 'Begin{out=0};/END_PSP/{out=0}; {if(out == 1) {print}};\
/Begin PSP_UPF/{out=1}' $OUTFILE >$TEMP
echo
功能
echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。
不支持管道
语法
echo [-e]"要打印内容"
echo默认是禁用反斜杠转义功能的,开启使用-e
参数
\\ 反斜杠
\a 警告声音输出
\b 倒退键(backspace)
\f 清除屏幕 (form feed),(换页(FF),将光标移到下页开头)
\n 输出新的一行(光标移到下行开头)
\r 亦即 Enter 按键(光标回到当前行开头)
\t 水平的 [tab] 按键
\v 垂直的 [tab] 按键
示例
使用转移字符,需加上-e参数,最好使用双引号
$ pi=3.1415926
## 双引号可以省略,支持变量,单引号会直接输出所有内容
$ echo "$pi"
3.1415926
$ echo '$pi'
$pi
$ echo $pi
3.1415926
## 使用转移字符,需加上-e参数,最好使用双引号
$ echo $pi\t\v$pi
3.1415926tv3.1415926
$ echo -e $pi\t\v$pi
3.1415926tv3.1415926
$ echo -e "$pi\t\v$pi"
3.1415926
3.1415926
$ echo -e '$pi\t\v$pi'
$pi
$pi
不换行-e "str\c"
[cndaqiang@mgmt output_iter]$ for i in $(seq 1 20); do echo -e "$i,\c"; done | xargs
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
printf
功能
printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。
不支持管道
语法
printf '列印格式' 实际内容
选项与参数:
关於格式方面的几个特殊样式:
\a 警告声音输出
\b 倒退键(backspace)
\f 清除屏幕 (form feed),(换页(FF),将光标移到下页开头)
\n 输出新的一行(光标移到下行开头)
\r 亦即 Enter 按键(光标回到当前行开头)
\t 水平的 [tab] 按键
\v 垂直的 [tab] 按键
\xNN NN 为两位数的数字,可以转换数字成为字节。
关於 C 程序语言内,常见的变量格式
%ns 那个 n 是数字, s 代表 string ,亦即多少个字节;
%ni 那个 n 是数字, i 代表 integer ,亦即多少整数码数;
%N.nf 那个 n 与 N 都是数字, f 代表 floating (浮点),如果有小数码数,
假设我共要十个位数,但小数点有两位,即为 %10.2f 罗!
示例
pi=3.1415926
$ printf '%f\n%f' $pi $pi
3.141593
3.141593$ printf '%f\n%f\n' $pi $pi
3.141593
3.141593
## \r会,回到该列开头进行覆盖
$ printf '%f\n%2.1f\n' 123 4
123.000000
4.0
$ printf '%f\r%2.1f\n' 123 4
4.0.000000
#\t制表符,用来对齐,\v垂直制表,回车加制表
$ printf '%f\t%f\v%f\n' 3 3 3
3.000000 3.000000
3.000000
整数前置补0,如octopus的输出
for i in $(seq 0 50 13000); do echo $(printf "td.%07d\n" $i); done
sort排序
sort -n
按数字排序,-rn
反向数字排序
(python37) cndaqiang@mommint:~/work/tdpw/wannier-h2o-2020-12-18/MD/wannier/test$ ls |sort
10.vasp
11.vasp
20.vasp
2.vasp
(python37) cndaqiang@mommint:~/work/tdpw/wannier-h2o-2020-12-18/MD/wannier/test$ ls |sort -n
2.vasp
10.vasp
11.vasp
20.vasp
(python37) cndaqiang@mommint:~/work/tdpw/wannier-h2o-2020-12-18/MD/wannier/test$ ls |sort -rn
20.vasp
11.vasp
10.vasp
2.vasp
tail
从第5行开始
tail -n +5
倒数5行
tail -5
cut切文件
- 截取文件的第2列和第4列
cut -f2,4 filename
- 去文件除第3列的所有列
cut -f3 --complement filename
-d
指定定界符cat -f2 -d";" filename
- cut 取的范围
-
N-
第N个字段到结尾
-
-M
第1个字段为M
-
N-M
N到M个字段
- cut 取的单位
-
-b
以字节为单位
-
-c
以字符为单位
-
-f
以字段为单位(使用定界符)
示例:
cut -c1-5 file //打印第一到5个字符
cut -c-2 file //打印前2个字符
截取文本的第5到第7列
$echo string | cut -c5-7
paste合并文件
(python37) cndaqiang@mommint:~$ cat 1
1
2
3
(python37) cndaqiang@mommint:~$ cat 2
2
3
(python37) cndaqiang@mommint:~$ paste 1 2
1 2
2 3
3
和awk连用可以进行计算和提取数据
(python37) cndaqiang@mommint:~$ paste 1 2 | awk '{print $2*10, ($1+$2)/2}'
20 1.5
30 2.5
0 1.5
本文首发于我的博客@cndaqiang.
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!