1.14 批处理脚本:for
这一节最重要的不是语法本身,而是一个意识:
如果你发现自己在重复做同一类操作,十有八九可以写成
for循环。
我这里不想只讲一个“教科书式”的最小例子,而是直接结合一些真实会出现的命令来讲。
因为对科研计算来说,for 循环最常见的用途不是刷算法题,而是:
- 对很多个目录做同一件事
- 对很多个文件做同一件事
- 批量提取结果
- 批量重命名
1. 最小结构先看懂
最基本的样子是:
for i in *; do
echo $i
done你可以先这样理解:
for i in ...:把一批对象一个个取出来i:当前这个对象的名字do ... done:对每个对象执行同样的操作
如果是:
for i in {1..5}; do
echo $i
done那就是依次处理:
12345
2. 最常见:对每个目录复制或移动文件
这一类在科研里非常高频。
例如你有很多个计算目录,想把每个目录下的 CONTCAR 统一复制出来:
for i in *; do
cp $i/CONTCAR zzz/$i.vasp
done这条命令的意思是:
- 遍历当前目录下的每个子目录
i - 把
$i/CONTCAR复制出来 - 并命名成
zzz/$i.vasp
这类操作很适合:
- 批量收集结构
- 批量整理输出文件
- 批量汇总结果
你历史里还有很多同类例子,例如:
for i in */; do
cp INCAR KPOINTS POTCAR $i
done这类写法就是:
- 对每个计算目录统一分发输入文件
3. 第二类:批量重命名
这也是 for 循环最常见的场景之一。
例如:
for i in *; do
mv $i ${i/COOH_m_}
done这条命令的意思可以先粗略理解成:
- 遍历当前目录下的每个文件或目录
- 把名字里的
COOH_m_去掉 - 再用新名字重命名
同类的还有:
for i in *; do
mv $i ${i/.vasp}
done这类通常是在:
- 去掉某个前缀
- 去掉某个后缀
- 把旧命名改成新命名
所以你会发现,前一节讲的字符串处理,在这里就真正派上用场了。
4. 第三类:批量提取结果
这类操作非常像“手工抄表”,只是交给 shell 去做。
例如:
for i in *; do
echo -n $i,
tail -n 1 ./$i/OSZICAR | awk '{print $5}'
done这条命令是在做:
- 先输出目录名
- 再取每个目录里
OSZICAR的最后一行 - 然后用
awk取其中一列
结果通常就会变成一行一行的:
dir1, ...
dir2, ...
dir3, ...这就是很典型的:
- 批量取能量
- 批量取磁矩
- 批量生成 csv 风格日志
你历史里还有更完整的写法:
for i in *; do
echo -n $i,
tail -n 1 ./$i/OSZICAR | awk '{print $5}'
done > ../quad.csv也就是把结果直接写进一个汇总文件。
5. 第四类:从给定列表中批量处理
有时候你不是要遍历所有文件,而是只处理一小部分指定对象。
例如:
for i in 0.12_-0.02_90 0.12_0.02_90 0.10_0.02_90; do
mv $i.vasp sec
done这条命令的思路是:
- 明确写出一组名字
- 只处理这几个对象
这类很适合:
- 手动挑出一批异常点
- 单独整理一组结果
- 对少数几个结构做额外操作
另一类很常见的是从文件里读列表:
for i in $(cat sec); do
rm $i.vasp
done这类写法你一定会经常见到,但也要先知道:
- 它简单
- 但如果名字里有空格,会有坑
所以新手阶段先会看懂它就够了。
6. 第五类:嵌套循环
当目录结构再复杂一点时,就会出现“目录里还有很多子目录”的情况。
例如:
for j in *; do
cd $j
for i in *.vasp; do
echo -n $j,${i/.vasp},
head -n 1 $i | awk '{print $2}'
done
cd ..
done你可以先这样理解:
- 外层循环
j:遍历一级目录 - 内层循环
i:遍历每个一级目录里的文件
所以嵌套循环适合处理:
- 多层目录
- 多组结构
- 每个体系下面还有很多子任务
当然,这种写法也更容易出错,尤其是里面夹着 cd 的时候。
所以对新手来说,先做到:
- 看懂嵌套循环在干什么
- 写的时候保持结构清楚
这就已经够好了。
7. 一个很实用的建议
你历史里的很多命令都说明了一件事:
for循环最有价值的地方,不是语法,而是把重复劳动压成一条命令。
但也正因为它威力很大,所以更要小心。
特别是涉及:
rmmvsed -i
这类会直接改文件的命令时,建议你先做两步:
第一步:先 echo
for i in *; do
echo mv $i ${i/.vasp}
done先不要真执行,只把将要执行的命令打印出来看看对不对。
第二步:确认没问题再去掉 echo
这比直接一把梭安全得多。
8. 这一节最少要做到什么
- 知道
for ... do ... done是批处理循环 - 看懂“对每个目录复制文件”这种最常见写法
- 看懂“批量重命名”这种最常见写法
- 看懂“批量提取结果”这种最有科研味的写法
- 知道危险命令最好先
echo预演
下一篇
继续看 1.15 终端环境优化。