实用主义的Linux命令和Bash脚本基础入门(更新中)
Pragmatical Tutorial for Basic Linux Commands and Bash Scripts (in Updating)
Jiawei Xu, Yingqi Li
Released: 2023-10-10 / Updated: 2023-10-10
Linux系统及其自带的Bash语言是计算化学科研工作中必须掌握的基本技能。本文旨在让计算化学初学者快速熟悉Linux系统和Bash语言,从而能够尽快开展相关工作,节省时间,少走弯路。同时本文也介绍了Linux系统下安装软件、队列系统使用、监控任务等的一般做法。
本教程中,以“$”开头的代码块代表此为在Linux终端中执行的指令(不包括$其本身,只是一个指示符而已)。
1. Linux系统的基本架构
1.1 Linux系统的文件结构
不同于Windows系统常见的分盘架构,Linux系统采用树状结构。Linux采用的是一个层级式的文件系统结构,这种结构以根目录“/”为基础(所以一定不能尝试运行rm -rf /,这代表强制递归删除根目录,也就是把整个系统都删掉,里面的数据自然也全没了!),所有其他文件和目录都是从这里开始延伸。这种架构相比于Windows更加有条理。
1.2 文件路径
可以以绝对路径和相对路径两种办法来定位某一文件。
1.2.1 绝对路径
绝对路径(absolute path)指从根目录开始描述路径,例如“/hitachi/mako/is/my/waifu.py”就是waifu.py这个python脚本的绝对路径。
1.2.2 相对路径
相对路径(relative path)指以某一特定文件夹为起点开始描述路径。常见的参照物有,当前目录“.”,上一目录“..”,当前用户的/usr目录“~/”。例如“../play/genshin.sh”代表上一目录中的/play目录下的genshin.sh脚本。又例如“./arknights.sh”代表当前目录下的arknights.sh脚本。
2. Linux系统必会技能
2.2 grep (Globally search a Regular Expression and Print) 正则表达式全局搜索与输出
基本用法:grep命令用于对文本进行搜索,并默认输出匹配行。
$ grep "SCF Done:" test.log # 从Gaussian16输出文件中查找包含“SCF Done:”的所有行并输出。
$ grep "FINAL SINGLE POINT ENERGY" test.out # 从ORCA输出文件中查找包含“FINAL SINGLE POINT ENERGY”的所有行并输出。
$ grep E(CASSCF) test.out # 如果目标字符串不含有空格,也可以省略引号。
一些扩展案例:
$ grep "SCF Done:" test.log | tail -1 # 从Gaussian16输出文件中查找包含“SCF Done:”的所有行,tail控制只输出最后一次匹配的结果。
$ grep "Frequencies --" freq.log | head -1 # head控制只输出第一次匹配的结果。
$ grep "SCF Done:\|EUMP2" test.log # 查找包含“SCF Done:”或“EUMP2”的所有行。其中“|”用于并列多个关键词,“\”是转义符。
使用set将grep命令的输出按空格分割,逐单词存储。以下两行命令即可从Gaussian16的输出文件(如优化任务)中获取最后一次SCF迭代的能量结果:
$ set `grep "SCF Done:" test.log | tail -1` # 反引号(``)中的内容将会以命令形式执行,并将输出结果传递给set,取消屏幕输出。
$ echo $5 # echo表示输出,默认输出到屏幕。此处输出grep结果的第五个单词,对应于SCF能量。
类似地,以下命令可以读取最小的一个频率,可用于检查虚频:
$ set `grep "Frequencies --" freq.log | head -1`
$ echo $3
$ set `grep "Frequencies --" freq.log | head -1`; echo $3 # 用分号分隔,可在同一行中书写多行命令。
2.3 sed(Stream EDitor)流编辑器
sed最重要的功能是对文件进行操作,如插入、删除和查找替换。
# 插入
$ sed -i "1i\%nprocshared=32" test.gjf # 在第1行前插入(i),直接对文件进行操作(-i)。“\”仅为了便于观看,可省略,此处不起实际作用。
$ sed -i "1a\%nprocshared=32" test.gjf # 在第1行后插入(a)。
$ sed -i "/%chk/i %nprocshared=32" test.gjf # 查看包含“%chk”的行,在其前插入“%nprocshared=32”。
$ sed -i "1i\%nprocshared=32\n%mem=96GB" test.gjf # 插入连续多行,“\n”表示换行符。
# 删除
$ sed -i "/nproc/d" test.gjf # 删除(d)包含“nproc”的所有行。
$ sed -i "1d" test.gjf # 删除第1行。
# 查找替换
sed -i "s/nproc=2/nproc=32/g" test.gjf # 查找所有的“nproc=2”字段,并将其替换为“nproc=32”。
sed -i "/nproc/c\%nprocshared=32" test.gjf # 将包含“nproc”的整行替换(c)为“%nprocshared=32”
实例练习 现有一批Multiwfn产生的.gjf文件,需将其修改为MOKIT做GKS-EDA计算(实际上是生成XEDA输入文件)的输入文件。结构中包含1个Fe原子和H、C、N、O组成的有机配体,将Fe原子单独划分为一个片段,其余配体作为另一个片段。试实现之。
分析 需要实现以下几个功能:1. 根据实际需求设定并行核数和内存;2. 删除“%chk”行;3. 修改关键词行;4. 将Multiwfn产生.gjf的默认标题替换为“{gks}”;5. 修改电荷和自旋多重度;6. 按照片段划分规则对原子排序,并附上所属片段序号。
实现 首先考虑对其中一个输入文件完成以上工作。
3. Bash脚本基础
3.1 循环语句
3.1.1 for循环
for循环是最基本的循环语句,其基本结构为:for variable in range; do commands; done,其依次将range中的每个值赋值给循环变量variable,为其执行循环体commands所定义的命令。例如以下语句将会输出10次“Hello World!”:
for i in {1..10} # 与Python不同,Bash中的{1..10}是一个闭区间
do
echo "Hello World!"
done
Bash脚本对缩进不敏感,但为保证代码的可读性,也应合理安排缩进。以上命令较为简单,也可写成单行的形式:
for i in {1..10}; do echo "Hello World!"; done
更多的时候,循环体内部需要对循环变量的值进行引用:
for i in {1..10}; do echo $i; done # $i表示变量i的值,该命令将会逐行输出1到10共10个数字。
for i in ./*.gjf; do g16 $i; done # 变量也可以是文件名等,该命令为当前目录下所有.gjf后缀的文件运行g16任务。
结合其他Linux命令,或调用外部程序,以实现更多样化的功能:
for i in ./*.gjf
do
filename=`basename $i .gjf` # 将i的值去掉后缀.gjf,并赋值给新的变量filename。
mkdir -p $filename; mv $i $filename; cd $filename # 为每个任务创建单独的文件夹运行。
g16 $filename.gjf
cd ..
done
以下命令将会实现批量转换当前目录下的所有.fchk文件为.gjf文件:
for i in ./*.fchk
do
Multiwfn $i >> tmp << EOF # >>将Multiwfn的屏幕输出重定向至临时文件tmp;<<将后续内容重定向至Multiwfn执行。
100
2
10
n
0
q
EOF # EOF: End Of File;两个EOF之间的所有内容将被重定向至Multiwfn。
rm tmp # 删除临时文件。
done
4. 本组常用软件安装
在不使用管理员权限的情况下(很显然,不使用管理员权限的前提是在home目录下安装程序),Linux系统下软件或程序的安装,一般根据安装包的不同分为预编译版本安装和从源码安装。预编译版本的程序安装一般非常简单,解压后根据实际情况设置环境变量即可,如Gaussian16、ORCA等都提供预编译版本安装包。从源码安装的情况更为复杂,通常分为三步:
1). 配置 (configure)
许多程序对应于一个可执行文件configure,一般是已经具有可执行权限的Bash脚本。其基本命令是:
$ ./configure --prefix=/home/jwxu/programs/install_dir
选项“–prefix”指定安装目录。某些程序(如Dalton)强制要求安装目录不能与编译目录雷同,即安装目录必须与解压缩的得到目录不同,例如解压缩Dalton到~/programs/dalton-src,则安装目录必须是与之不同的~/programs/dalton等。一般总是建议使用不同的安装目录,安装完成后可以删去编译目录节省空间,但有些程序显然不能这么做,如Tcl-ChemShell,这是开发者的问题。
有时还需要提供额外的选项,根据实际情况进行处理。
2). 编译 (compile)
一般使用make命令进行编译。配置完成后会在当前目录产生一个Makefile文件,make命令根据Makefile的内容进行编译,可通过以下方式并行编译加快速度:
$ make -j # 使用全部可用线程数。
$ make -j16 # 使用16线程编译。
某些情况下并行编译可能会出错,此时应先去掉-j选项尝试进行串行编译。编译完成后,相关可执行程序和库文件已经存在于编译目录内的各个子文件夹中。
3). 安装 (install)
如程序是通过make编译的,则运行以下命令安装:
$ make install [-j]
之后相关文件被安装至配置时指定的安装目录。此时可删去编译目录以节省空间。在.bashrc或队列系统脚本中添加相关环境变量,就可以正常使用程序了。
大多数程序都是通过以上三步进行安装,但并不都是使用以上命令,如Dalton通过cmake编译,某些Python程序会提供setup.py用于安装,这在后面遇到时再进行详细解释。
4.1 Intel全家桶
Intel全家桶,一般指的是包含Intel编译器、MKL数学库、Intel MPI并行库等常用工具在内的一系列Intel产品的代称,许多常用软件在编译或运行时都会用到。以在线版为例(如机子无法联网,则下载离线版),首先到Intel官网(https://www.intel.cn/content/www/cn/zh/homepage.html)下载所需版本的Intel oneAPI Base Toolkit和Intel oneAPI HPC Toolkit,然后运行安装程序:
$ chmod +x l_BaseKit_p_2022.2.0.262.sh l_HPCKit_p_2022.2.0.191.sh
$ ./l_BaseKit_p_2022.2.0.262.sh # 按照提示进行安装。默认安装在~/intel目录下。
$ ./l_HPCKit_p_2022.2.0.191.sh
$ cd ~/intel/oneapi
$ ./modulefiles-setup.sh # 此时在~/intel/oneapi/modulefiles目录下产生了相应环境文件。
安装tcl和modules环境管理器(如已安装,可跳过此步骤)。有些情况下,在登陆节点已经安装了tcl,然而计算节点上并未安装,导致modules运行时报错找不到解释器“/usr/bin/tclsh”,对于非root用户可自行安装tcl来保证modules正常运行。注意,如果使用集群或超算,应向管理员咨询是否已经安装了相关环境,以免重复安装。
$ wget https://cfhcable.dl.sourceforge.net/project/tcl/Tcl/8.6.9/tcl8.6.9-src.tar.gz
$ tar -xf tcl8.6.9-src.tar.gz
$ cd tcl8.6.9/unix
$ ./configure --prefix=/home/jwxu/programs/tcl-8.6.9
$ make -j && make install -j # 将tcl安装到/home/jwxu/programs/tcl-8.6.9
$ wget https://newcontinuum.dl.sourceforge.net/project/modules/Modules/modules-4.2.4/modules-4.2.4.tar.gz
$ tar -xf modules-4.2.4.tar.gz
$ cd modules-4.2.4
$ ./configure --prefix=/home/jwxu/programs/modules \
--with-tcl-lib=/home/jwxu/programs/tcl-8.6.9/lib \
--with-tcl-inc=/home/jwxu/programs/tcl-8.6.9/include \
--with-tclsh=/home/jwxu/programs/tcl-8.6.9/bin/tclsh # 也可能是tclsh8.6
$ make -j # 此步骤可能会报错“未定义的...”,注释掉相关代码即可。
$ make install -j # 将modules安装到/home/jwxu/programs/modules
$ cp -r /home/jwxu/intel/oneapi/modulefiles/* /home/jwxu/programs/modules/modulefiles
# 以上将Intel相关的modules环境文件复制到默认路径。也可用以下命令替代:
$ export MODULEPATH=$MODULEPATH:/home/jwxu/intel/oneapi/modulefiles # 如用此法,可将此行命令添加至~/.bashrc文件。
$ source /home/jwxu/programs/modules/init/profile.sh # 可将此行命令添加至~/.bashrc文件。
$ module load compiler mkl mpi # 即载入Intel编译器、MKL数学库和Intel MPI并行库。
4.2 Tcl-ChemShell 3.7.1
首先安装Tcl,参见3.1中相关内容。使用Intel编译器安装串行版本的Tcl-ChemShell:
$ export CC=icc
$ export F77=ifort
$ export F90=ifort
$ export TCLROOT=/home/jwxu/programs/tcl-8.6.9
$ export LIBTCL=$TCLROOT/lib/libtcl8.6.so
$ export PATH=/home/jwxu/programs/chemsh-3.7.1/bin:/home/jwxu/programs/chemsh-3.7.1/scripts:$PATH
$ cd chemsh-3.7.1/src/config
$ ./configure
添加环境变量,或将环境变量写入投任务的脚本中:
$ export TCLROOT=/home/jwxu/programs/tcl-8.6.9
$ export LIBTCL=$TCLROOT/lib/libtcl8.6.so
$ export PATH=$TCLROOT/bin:/home/jwxu/programs/chemsh-3.7.1/bin:/home/jwxu/programs/chemsh-3.7.1/scripts:$PATH
4.3 GAMESS-US
4.4 GROMACS
这里以本组常用的GROMACS-2022.5版本为例,首先来准备/检查GROMACS安装的前置条件
1. fftw
fftw (i.e.快速傅立叶变换库)
$ wget http://www.fftw.org/fftw-3.3.8.tar.gz # 获得fftw的源代码
$ ./configure --prefix=/home/fftw338 --enable-sse2 --enable-avx --enable-float --enable-shared # --enable-avx2 可选,前提是你的cpu支持avx2,使用lscpu指令查看输出的“标记”一栏中,是否有avx2字段.
$ make -j install
2. cmake
在前面,我们已经介绍了cmake的编译
$ cmake --version # 查看cmake版本
3. cuda # 仅gpu加速版本(用于Nvidia显卡),注意,极度不推荐在不采用GPU加速的情况下以生产用途跑分子动力学程序!
# 这里介绍联网和不联网两种安装方式,均默认已经安装Nvidia驱动(两者取其一即可)
# a.联网安装(需要root和公网访问权限)
$ nvidia-smi # 如果有正常输出显卡信息,则代表Nvidia驱动安装好了,可以继续往后安装
$ sudo dnf config-manager --add-repo http://developer.download.nvidia.com/compute/cuda/repos/rhel8/x86_64/cuda-rhel8.repo # 添加Nvidia专属仓库
$ sudo dnf clean all # 添加仓库后清缓存是一个好习惯
$ sudo sudo dnf -y install cuda-toolkit-12-2 # 这里是不包括nvidia驱动的,以cuda-12.2为例
# 然后需要写cuda的环境变量,这里极度推荐使用module管理,module文件附在了本文的最后
$ module load cuda # 如果使用module管理环境变量,需要先load
$ nvcc --version # 正常输出则代表安装完毕
4. gcc
# CentOS软件仓库自带的gcc往往版本跟不上gromacs的需求,这里介绍下如何编译你想要的版本
$ wget http://ftp.gnu.org/gnu/gcc/gcc-9.5.0/gcc-9.5.0.tar.gz # 获取gcc9.5的源码
$ tar xvf gcc-9.5.0.tar.gz
$ cd gcc-9.5.0
$ ./contrib/download_prerequisites # 运行gcc自带的补全依赖脚本
$ mkdir build # 给编译产生的文件单开一个目录安放是一个好习惯,可以避免污染代码树
$ cd build
$ ../configure --prefix=/home/gcc-9.5.0 --enable-language=c,c++,fortran --disable-multilib
# 一般教程里面不会enable fortran,特别注意,这边一定要enable fortran,否则没有gfortran用
$ make -j # 如果编译报错,尝试-j12或更小并行核数,gcc编译很慢,请耐心等待
$ make install
随后安装gromacs本体,先前往gromacs官网下载程序源码(或许由于某些魔法因素,笔者只能使用局域网的wget获取源码是不可行的),传到待安装的机器上,解压,进入目录,之后:
$ mkdir build # 依旧保持好习惯
$ cd build
$ export CMAKE_PREFIX_PATH=/home/fftw338 # 此处为上一步安装fftw的路径
$ cmake .. -DCMAKE_INSTALL_PREFIX=/home/gromacs-2022.5 -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-12.2 -DGMX_GPU=CUDA
# 解释一下上面的configure,prefix是安装路径;-DCUDA_TOOLKIT_ROOT_DIR是本机CUDA-toolkit所在的路径;-DGMX_GPU=CUDA代表编译基于CUDA 的GPU加速版本
$ make install -j
# 最后,需要设置gromacs的环境变量,简单地进行的话只需要:
$ source /home/gromacs-2022.5/bin/GMXRC
# 但是依旧推荐使用modules进行管理,这样在机器中存在多个版本的时候,依旧能优雅从容地管理环境变量。
至此,gromacs-2022.5版本就安装完毕了。如果想安装更高版本,可能需要更高版本的gcc和cmake,那么只需要参照本文进行编译并用modules管理即可。
4.5 AMBER
AMBER的安装基本参照http://bbs.keinsci.com/thread-39265-1-1.html一文,此处作简单的指令备忘录,详细过程请阅读原贴,笔者感谢原贴作者的无私分享。
准备部分:
1. 一些包管理器可以解决的依赖
$ sudo dnf install -y bzip2-devel cmake3 gcc-gfortran gcc-c++ lzip tcl-devel zlib libzip-devel epel-release cmake3
2. gcc
$ gcc --version # 检查gcc是否装好并加载,版本是否过时,如否,参考上一节中gcc的装法
3. openmpi
$ wget https://download.open-mpi.org/release/open-mpi/v4.1/openmpi-4.1.6.tar.bz2 # 如果下载不了,用能用魔法的机子手动下载
$ tar xvf openmpi-4.1.6.tar.bz2
$ cd openmpi-4.1.6
$ ./configure --prefix=/home/openmpi-4.1.6 --disable-builtin-atomics
$ make all install -j
# 最后写module file即可(见附录)
4.fftw
# 上一节已经讲过fftw的简单安装,这里给一个把fftw所有精度、所有并行模式的库一次装上的脚本,解压源码后进入目录执行即可。具体内容见附录的./compile_fftw_all.sh
# 当然,也要纳入module管理
5. intel mkl & Anaconda3
# Amber需要这两个前置,前文已经讲过如何安装(见intel全家桶的安装那一小节),不赘述。
# 特别注意,Anaconda3的python版本务必小于3.11!
6. 基于Open MPI的MPI4Py
# 下载源代码:https://pypi.python.org/pypi/mpi4py
$ tar xvf mpi4py-3.0.0.tar.gz
$ cd mpi4py-3.0.0
# 下面需要用vim打开mpi.cfg,然后把[openmpi]处的mpi_dir修改成本机器上的路径。
python setup.py build --mpi=openmpi
python setup.py install --prefix=/home/mpi4py
# 然后写个module file管理环境变量即可
7. Boost
$ ./bootstrap.sh --prefix=/home/boost --with-libraries=all --with-toolset=gcc
# 然后在project-config.jam添加一行:
using mpi : /opt/openmpi/4.11.1/bin/mpicxx ;
$ ./b2 -j8 --layout=tagged link=static,shared threading=multi install
# 然后写个module file管理环境变量即可
8. CUDA
$ nvidia-smi
$ nvcc --version
# 上面两个命令都正常输出,分别说明Nvidia驱动和CUDA没有问题,可以继续安装,有问题请参考Nvidia官方文档,或上面提到的博文进行安装。
本体部分:
# 先加载下上面的所有modules
# CMakeLists.txt前面加入add_compile_definitions(THRUST_IGNORE_CUB_VERSION_CHECK)
$ cd build
# 下面是openmpi版本的编译,编辑run_cmake:
# 在Linux那段把-DCMAKE_INSTALL_PREFIX修改为想安装的目录
# -DDOWNLOAD_MINICONDA选为FALSE,
# 删除后面的-DMINICONDA_USE_PY3(如有),
# 加入-DTRUST_SYSTEM_LIBS=TRUE,
# 加入-DOPENMP=TRUE,
# 将-DMPI改成TRUE
$ ./run_cmake
$ make -j 8 # 可能会爆内存,那就把8改成4,或者干脆去掉-j,然后去以此为正当理由摸鱼,摸鱼回来就编译好了
$ make install
# 下面是CUDA版本的编译,编辑run_cmake:
# 把-DOPENMP后面改成FALSE,
# 如果有超过一张计算显卡而且想要跨卡运行-DMPI后面写TRUE,否则写FALSE,
# 加入-DCUDA=TRUE
# 加入-DCUDA_TOOLKIT_ROOT_DIR=xxx,(用which nvcc确认此路径)
# 使用NVLink的话要加-DNCCL=TRUE。
$ ./run_cmake
$ make -j 8
# 可能会爆内存,那就把8改成4,或者干脆去掉-j,然后去以此为正当理由摸鱼(CUDA版本编译时间更久,你可以摸更久的鱼),摸鱼回来就编译好了
$ make install
# 最后,设置环境变量,依旧建议用modules
附录:本节提到的所有module files
GCC
5. 队列和进程
5.1 队列系统
5.1.1 队列系统简介
5.2 进程监控
常用的进程监控命令是top和ps u,二者均会打印当前系统运行的各个进程以及唯一对应的PID号。对于PC机或未安装队列系统的小型服务器,可使用这两个命令实现进程监控,当某一进程结束后,执行后续进程。例如:
#!/bin/bash
flag=1
while [ "$flag" -eq 1 ]
do
sleep 1m # 等待1分钟。
PID=$1
PID_EXIST=$(ps u | awk '{print $2}' | grep -w $PID) # 判断当前是否存在该进程。
if [ ! $PID_EXIST ]
then
mpirun -np 24 vasp
flag=0
else
echo "Previous job unfinished. Continue waiting for 1 min..."
fi
done
假设该脚本为./monitor.sh。首先通过ps u命令查看需要被监控的进程所对应的PID号,运行./monitor.sh PID,该脚本会在后台每隔1分钟检查前序任务是否结束,当被监控的进程结束后,该脚本便会执行“mpirun -np 24 vasp”。同时,该脚本本身对应于一个“/bin/bash monitor.sh”的进程,自然也是可以被监控的,由于“mpirun -np 24 vasp”是该进程的子进程,./monitor.sh进程会在VASP运行结束后结束。虽然不知道后续VASP任务的进程号,但通过监控./monitor.sh的进程号,也就实现了对后续VASP任务的监控。