Linux 命令收藏

一些很有用的 Linux 命令

并行

# 耗时 5 分半
ls *.kg| xargs sed -i 1d

# 耗时 40 秒
ls *.kg| xargs -P 28 sed -i 1d

# -P 0 表示使用所有拥有的核来并行
# 耗时 40 秒
ls *.kg| xargs -P 0 sed -i 1d

grep

基本用法

Usage: grep [OPTION]… PATTERN [FILE]…
Example: grep -i ‘hello world’ menu.h main.c

字段

-r 遍历某个文件夹,而不是只搜索某个文件
-n 显示行号

搜索整个 OpenFOAM solvers 文件夹,找到所有文件内容包含 LES 的文件

grep -rn LES $FOAM_SOLVERS

find

基本用法

查找目录:find (查找范围) -name ‘查找关键字’ -type d
查找文件:find (查找范围) -name 查找关键字

搜索默认将在当前目录中开始

查找当前目录下 abort 开头的文件

find ./ -name 'abort*'
# or
find ./ -name abort*

但是上边这个命令会输出子文件夹里边的 abort* 文件。
我只想输出当前目录下的呢?

find . -maxdepth 1 -name abort*

查找 OpenFOAM solvers 文件夹,找到所有文件名包含 fvPatch 的文件

find $FOAM_SOLVERS -name “*fvPatch*”

find & rename

find . -type f -name sourcePV -execdir mv sourcePV SourcePV \;

find & grep

find -name "fvSolution" | xargs grep PBiCGStab

find & rm

find . -name 'C7H16*' | xargs rm
# 这个比较高效
find . -name 'C7H16*' -print0 | xargs -0 rm
# 删除除了 `C12` 开头以外的其它文件
find . -name '[^C][^1][^2]*' -print0 | xargs -0 rm -rf

find & sed

# 找到所有的 *.C *.H 文件,删除行尾空格和 tab
find . -name *.[C\|H] -exec sed -i "s/[ \t]*$//g" {} +
# 找到所有的 *.C *.H 文件,将所有 tab 替换成 4 个空格
find . -name *.[C\|H] -exec sed -i "s/\t/ /g" {} +

高级用法

在 Linux 库中搜索文件时,有时会提示没有权限,可以将这些提示重定向到 / dev/null

find -name libgcc.so 2>/dev/null

对于文件夹

find -name 'C7H16*' | xargs rm -r

sed

Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]

常用 OPTION

  • -i 直接修改文件
  • -r use extended regular expressions in the script

文件操作

  • d 删除
  • a 在下一行新增
  • i 在上一行插入
  • c 以行为单位的取代
  • s 替换

操作实例

删除行

删除第一行

sed -i '1d' <filename>

删除最后一行

sed -i '$d' <filename>

删除第 N 行
sed -i ‘Nd’

删除 1 到 20 行,Linux 下标是从 1 开始的

sed -i '1,20d' <filename>

从第 1 行开始, 每隔 2 行删除,即删除 1··3··5··7·· 行。

sed -i 1~2d <filename>

删除空行

sed '/^\s*$/d' fileName

删除重复行

awk '!a[$0]++' fileName

删除文件中含特定字符串(如 abc)的行

#这个操作好像不能使用 -i ,只能把结果重定向到一个新的文件
sed -e '/abc/d' fileName >fileName_NEW

#这样是不对的,生成的 fileName 是一个空文件
sed -e '/abc/d' fileName >fileName

删除文件中包含某个关键字开头的所有行

sed -i '/^abc/d' <fileName>

删除 m 字符结尾的所有行

sed '/m$/d' <fileName>

删除 x 或者 m 字符结尾的所有行

sed '/[xm]$/d' <fileName>

替换字符串

sed -r -i 's/被替换字符/替换后的字符/g' <filename>

如果被替换的字符串中有变量,把最外层的单引号改成双引号

var=300
sed -r "s/200/$var/g" <filename>

删除某些字符串,即把它替换成空

sed -r -i 's/欲删除字符串//g' <filename>

删除行尾空格

删除行尾 tab

最对某行进行操作

# 只对第一行进行操作
sed -i '1s / 被替换字符串 / 新字符串 / g'

我定义的函数,用于清理 OpenFOAM 代码

ofFormat () {
# 删除行尾空格和 tab
find . -name *.[C\|H] -exec sed -i "s/[ \t]*$//g" {} +
# 将所有 tab 替换成 4 个空格
find . -name *.[C\|H] -exec sed -i "s/\t/ /g" {} +
}

扩展用法

修改软链接对应的真实文件

sed -i --follow-symlinks "操作" settings.csf

匹配关键字,然后替换下一行的某个关键字

# 找到 BOUNDARY_INLETTEMPERATURE 的下一行,将 900 替换为 ${temperature} 这个变量的值
sed -i --follow-symlinks "/BOUNDARY_INLETTEMPERATURE/{n;s/900/${temperature}/;}" $case/settings.csf

匹配关键字,然后替换掉整个下一行

# 将 BOUNDARY_INLETTEMPERATURE 的下一行,修改为 985
sed -i --follow-symlinks '/BOUNDARY_INLETTEMPERATURE/!b;n;c985' $case/settings.csf

但是将下一行改为变量,还有问题

sed -i --follow-symlinks '/BOUNDARY_INLETTEMPERATURE/!b;n;c${pressure}' $case/settings.csf

这个只会将下一行改为 ${temperature} 这个字符串,而不是这个变量的值。

替换多行


### 生成 C list
python<<EOF
import numpy as np
x=np.linspace(0,1,51)
y=x*x
f = open("Clist", 'w+')
for y in y:
print>>f,y
EOF

# 将换行符替换成 #,将 abc 缩为一行。
abc=`awk '{print $1}' Clist|tr "\n" "#"`
# 将 C_list 下面的括号中的所有行,替换成 abc。
sed -i -e '{:begin; /)/! { $! { N; b begin }; }; s/C_list\n(.*)/C_list\n(\n'${abc}')/; };' ./file
# # 替换成换行符,就是将 abc 还原成了一个多行数据。
sed -i 's/#/\n/g' ./fileName

这里用 python 先生成一个数组,然后输出到文件,然后读取这个文件,读取出来是一列数据,我打算将这一列数据替换掉某个文件中的一列其它数据。

修改文件中包含某个字符串的那一行

适合在脚本中修改 OpenFOAM 的参数设置,不过现在 OpenFOAM 有了自带的脚本来干这件事:foamDictionary

参考

#!/bin/bash
#PBS -N caseName
#PBS -l nodes=2:ppn=10
#PBS -j oe
#PBS -l walltime=150:30:00

cd $PBS_O_WORKDIR
NP=`cat $PBS_NODEFILE|wc -l`

# 取得行号
n=`grep -n "numberOfSubdomains" system/decomposeParDict | awk -F':' '{print $1}'`
# 这里表示修改第 n 行,c 表示修改某一行
sed -i "$[n]c numberOfSubdomains $NP;" system/decomposeParDict


blockMesh
decomposePar -force
mpirun -np $NP -hostfile $PBS_NODEFILE reactingFoam -parallel 2>&1 | tee run.log

这里的 NP 就是所使用的核心数,这里等于 20,对应脚本中的 nodes=2:ppn=10

替换某列

把第 79 列替换成空格,这里的 点 表示任意单个字符

sed -i 's/./ /79' <filename>

同时替换多个

用法

ls *.kg | xargs sed -i 's / 被替串 1 / 新串 1/g;s / 被替串 2 / 新串 2/;s / 被替串 3 / 新串 3/;s / 被替串 4 / 新串 4/'

举例
但是它不适合替换多个相同的字符,只会替换第一个,因为没有加 g 吗?

# 匹配所有 .kg 为后缀的文件
# 将 temperature [K] 替换为 T
# 将 ProdRateProgVar [kg/m^3s] 替换为 ProdRateProgVar
# 将 density [kg/m^3] 替换为 rho
# 将 SumMH [J/m^3s] 替换为 HeatRelease
# 将 massfraction- 替换为空
ls *.kg | xargs sed -i '1s/temperature \[K\]/T/g;1s/ProdRateProgVar [kg/m^3s]/ProdRateProgVar/;1s/density [kg/m^3]/rho/;1s/SumMH [J/m^3s]/HeatRelease/'

注意事项

遇到特殊字符,在前边加反斜杠 \
空格不算特殊字符,前边不需要反斜杠。

sed ‘s///g’ 与 sed ‘s///’ 的区别

  • 加 g,匹配每一行有行首到行尾的所有字符
  • 不加 g,匹配每一行的行首开始匹配,匹配到第一个符合的字段,就会结束,跳到下一行

awk

基本用法:

awk -F "用于分隔的符号或者字符串" '{print $ 分隔后你需要的列号}'

awk 提取某一列字符, 如:

 stp=1    dt=1.00e-09 s    ord= 1    time=1e-06 ms	T_max=  897.7 K	T_st= 875.1 K

有一个 log 文件,格式的每一行如上所示,现在需要提取 time 到一个新的文件,代码如下:

grep stp log |awk -F "="'{print $5}'|awk -F"ms"'{print $1}' >time

解释,grep 是获取 log 文件中含有 stp 的所有行。
awk 的主要功能是对列操作,这里按 “等于” 号将每一行分隔为若干列。
然后取第 5 列,获得字符串 1e-06 ms T_max
最后将 1e-06 ms T_max 按 ms 分为两列,取第 1 列,即我们需要的 “时间”。

列数为变量时如何使用:

awk -v N=274 '{print $N}' <fileName>

获取第 274 列数据。

排序

sort [OPTION]... [FILE]

-g 可以对科学计数法进行从小到大排序
-r 逆序

循环、自增操作

for i in `seq 1 100`
do
echo $i
done

for i in 1 2 4 8; do
echo $i
done
files=`ls Pre*`
for file in $files
do
echo $file
done
x=0
for i in `seq 1 100`
x=`expr $x + 1`
done

break 跳出循环。
continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

字符串判断

参考:https://blog.csdn.net/Mr_LeeHY/article/details/76383091

# 字符串相同
if [ "$A" = "$B" ];then
echo =
fi

# 字符串不相同
if [ "$A" != "$B" ];then
echo !=
fi

# 模式匹配
if [[ "$A" == a* ]];then
echo "[[ ==a* ]]" # 如果$A 是a开头的字符串,则匹配成功,注意一定要使用上边的双括号[[]]
fi

# 字符串不为空,长度不为0
if [ -n "$A" ];then
echo "[ -n ]"
fi

# 字符串为空.就是长度为0.
if [ -z "$A" ];then
echo "[ -z ]"
fi

判断大小

整数间的判断

T=999
T_ext=1200

if [ $T -lt $T_ext ]; then
echo "T < T_ext"
fi

if [ `expr $T \< $T_ext` -eq 1 ]; then # 需要转义<,否则认为是一个重定向符号
echo "T < T_ext"
fi

实数间的判断

T=9999.0
T_ext=1200.0
if [ $(echo "$T> $T_ext" | bc) -eq 1 ]; then
echo "T > T_ext"
fi
# 不支持科学计数法,默认 if 不成立,即跳过科学计数

PS:科学计数法转十进制:
方法 1: printf "%f\n" 3.1188622400e+06
方法 2: echo 3.1188622400e+06 | awk '{printf("%f\n",$0)}'

删除所有文件,except some file

find ./ -name '[^C][^1][^2]*' | xargs rm -rf # 删除除了 C12 开头以外的其它文件

数组

index,bash 从 0 开始,zsh 从 1 开始。
${abc[i]} 表示数组 abc 的第 i 个分量

来自别的程序输出的数组

timelist=`foamListTimes`
for time in $timelist; do
echo time=$time
done

指定数组并遍历

allThreads=(1 2 4 8 16 32 64 128)

for t in ${allThreads[@]}; do
echo t= $t
done

for i in ${!allThreads[@]}; do
echo allThreads[i]= ${allThreads[$i]}
done

从文件读取一个数组

mapfile -t List < List_file
echo List = ${List[@]}

计算最大值

max=`printf '%s\n' "${List[@]}" | awk '$1 > m || NR == 1 { m = $1 } END { print m }'`

实数运算

整数的加减乘除:

方法一:

b=$((5*5+5-6/2))
echo $b
输出:27

方法二:

i=5
c=`expr $i / 2`
echo $c
输出:2,可以看出,这里不支持小数
PS: 操作符号两边要有空格,这里的 “/” 也可以是“+”“-”“*”,* 不行!

小数点位数

time=0.00015
time=$(printf "%.4f" `echo "$time + 0.0001"|bc`)
echo $time

输出:0.0002
保留 4 位小数,后边直接截断,不进行四舍五入。

time=0.00015
time=$(echo $time 0.0001 | awk '{ printf"%0.4f\n",$1+$2}')#time+0.0001
echo $time

输出:0.0003
四舍五入,保留 4 位小数。

浮点数的加减乘除:

方法一:

c=$(echo "5.01-4*2.0"|bc)
echo $c
输出:-2.99

例外:c=$(echo "1/1000"|bc) 输出为 0,因为它是小于 1 的小数。如何才能正确输出小数呢?

方法二:

c=$(awk 'BEGIN{print 7.01*5-4.01}')
echo $c
输出:31.04
c=$(awk 'BEGIN{print 1/1000}')
echo $c
输出:0.001

例外:我想输出从 0 到 1 之间的等间距小数

for i in `seq 0 10`
do
c=$(awk 'BEGIN{print $i/10}')
echo $c
done
输出 11 个 0
for i in `seq 1 10`
do
c=$(printf "%.4f" `echo "$i / 10"|bc`)
echo $c
done
依然输出一堆 0.0000,只有最后一个是 1.0000
for i in `seq 1 10`
do
c=$(echo $i 10 | awk '{ printf"%0.4f\n",$1/$2}')
echo $c
done
这个才是正常输出的。

修改文件名

先来点简单的操作

增加后缀

find  . -type f -exec mv '{}' '{}'.old \;

增加前缀

for files in $(ls *.png)
do mv $files "pic."$files
done

去除前缀
???
去除后缀
???
网址

再来点高级的

将文件名拆分,然后重组,如:

ls fileName | sed -r -n 's/(.*) 分隔字符 (.*)/mv & 替换后的字符 \ 1\2/e'
# 这里表示将原文件名按‘分隔字符’拆分为两部分,分别由 (.*) 对应,也对应 \1 和 \2。
# 后边的部分即为新文件名,可以添加任意字符在 \ 1 \2 的任何位置,也可以不要 \2,即删去 “分隔字符” 及其后边的部分。

举例:

ls chi* | sed -r -n 's/.*tf(.*)/mv & \1/e'
# 分隔为 tf 之后部分,全部保留,即保留 tf 后边的字符串
# 原文件名:chi23.0769tf0363to0900Tst2227.kg
# 操作后:0363to0900Tst2227.kg

#  .* 加不加括号的区别在于,加括号后,才计入分隔的部分中;
# 如果不加括号,仅仅表示中间还有若干字符,但是不计入分隔后的部分。

ls chi* | sed -r -n 's/(.*)tf.*/mv & \1/e'
# 分隔为 tf 之前部分,全部保留,即删去 tf 以及后边的所有字符串。
# 原文件名:chi23.0769tf0363to0900Tst2227.kg
# 操作后:chi23.0769

ls chi* | sed -r -n 's/(.*)tf.*.kg(.*)/mv & \1\2/e'
# 分隔为 tf 之前,和 .kg 之后
# 这里分隔后,\1 表示 chi23.0769,即前边那个 (.*) 对应的部分,\2 表示'AAA',即后边那个 (.*) 对应的部分。
# 原文件名:chi23.0769tf0363to0900Tst2227.kgAAA
# 操作后:chi23.0769AAA


ls chi* | sed -r -n 's/(.*)tf(.*).kg(.*)/mv & \1\2\3/e'
# tf 和 .kg 把原文件名分隔为三部分,全部保留。
# 原文件名:chi23.0769tf0363to0900Tst2227.kgBBB
# 操作后:chi23.07690363to0900Tst2227BBB

字符串模糊匹配与精确匹配

if [["$HOSTNAME" = tetralith*]];then
echo tet
fi

if ["$HOSTNAME" = "b-an01.hpc2n.umu.se"];then
echo keb
fi
文章作者: Yan Zhang
文章链接: https://openfoam.top/linuxLearning/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 OpenFOAM 成长之路
您的肯定会给我更大动力~