Linux命令实践

本文记录 Linux 常用命令记录。<>为必填,[]为可选,()为注释性文字。

查找替换

主要利用 sed 命令,注意,如果只打印到终端,去掉-i选项。

将当前目录所有文件的intMAX字符串替换为INTd_MAX:

1
sed -i 's/intMAX/INT_MAX /g' `grep MAX_INt ./ -rl`

注:可以修改grep查找路径来指定目录。

仅查看当前目录所有.cpp和.c文件:

1
find ./ -maxdepth 1 -name '*.cpp' -or -name '*.c'

注:使用-maxdepth 1指定搜索的目录深度。去掉的话,则是递归搜索所有子目录。

1
sed -i 's/ExecStart.*/ExecStart=\/usr\/bin\/dockerd -H fd:\/\/ --graph \/mnt\/docker/g' docker.service

将docker.service文件ExecStart那一行替换为ExecStart=/usr/bin/dockerd -H fd:// --graph /mnt/docker。注意,路径的/需要转义,即\/。添加-i可替换原文件

将当前日期作为版本号:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
verdate="`date +%y%m%d`"
version="V1.0.0."$verdate"a"

# 新建my_ver.h
verdate="`date +%y%m%d`"
version="V1.0.0."$verdate"a"

verfile=my_ver.h
touch ${verfile}

echo "#ifndef __MY_VER_H__" >> ${verfile}
echo "#define __MY_VER_H__" >> ${verfile}
echo "" >> ${verfile}
echo "#define SW_VER \"$version\"" >> ${verfile}
echo "" >> ${verfile}
echo "#endif" >> ${verfile}

# 替换my_ver.h已有的SW_VER定义
sed -i "s/SW_VER.*/SW_VER \"$version\"/g" my_ver.h

echo $version

注意,sed 中使用变量,需要用双引号。如果其中还有双引号,需要\转义。

参数cloudhub如存在,则替换 .env 文件对应的值,可输入类似 IP 地址的格式。

1
[[ ! -z $cloudhub ]] &&  sed -i "/CLOUDHUB=/c\CLOUDHUB=${cloudhub}" .env && echo "set cloudhub success"

代码格式化

去掉行尾多余空格

1
sed -i "s/[ ]*$//g" test.cpp

将tab键替换为4个空格

1
sed -i "s/\t/    /g" test.cpp

注:/t与/g之间有4个空格,表示替换为4个空格。

一个比较完整的脚本内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

# 代码格式脚本,将tab替换成4个空格,将多余的行尾空格(含tab键)去掉。

# 要格式化代码的目录
SRC_DIRS="Applications BasicModules BussinessModules Include PlatformAPI"

# 如果有些目录不希望被格式化,在-o -path后面添加具体的相对路径(如下示例的PlatformAPI/test/googletest)。
SRC_DIRS_NEW=`find $SRC_DIRS \( \
-path PlatformAPI/test/googletest \
-o -path PlatformAPI/doxygen \
\) \
-prune -o -type d -print`

# 只针对cpp/c/h三种类型
SRCS=`find $SRC_DIRS_NEW -maxdepth 1 -name '*.cpp' -or -name '*.c' -or -name '*.h'`

for file in $SRCS;
do
echo "formatting" $file
#sed -i "s/[ ]*$//g" $file
#sed -i "s/\t/ /g" $file
done;

统计代码数量

统计当前目录所有.h .cpp文件文件行数(其它类型类推):

1
2
3
find . ! -name "." -name "*[.h|.hpp|.c|.cpp]"|xargs cat|grep -v ^$|wc -l

find . ! -name "." -name "*[.h|.hpp|.c|.cpp]" -type f | xargs cat | wc -l

统计当前目录.cpp文件个数

1
find . ! -name "." -name "*[.h|.hpp|.c|.cpp]"|wc -l

遗留:.h会匹配.sh文件,.c会匹配.cpp文件。

程序后台执行

有时需要在本地使用ssh连接到linux上执行命令或程序,但当连接断开时(断网或本机关机),程序会退出,使用nohup可以将程序放到后台执行,并且不随外界影响,当然,自身系统挂掉除外。示例:

1
nohup xxx & >> /tmp/output.txt

查找文件指定字符串出现第一次或最后一次位置

查找指定文件某字符串第一次出现位置:
命令:cat <文件名称> | grep -n "<字符串>" | head -n 1
示例:

1
cat log.txt | grep -n "MISCONF Redis is configured to save RDB snapshots" | head -n 1

查找指定文件某字符串最后一次出现位置:
命令:cat <文件名称> | grep -n "<字符串>" | tail -n 1
示例:

1
cat log.txt | grep -n "MISCONF Redis is configured to save RDB snapshots" | tail -n 1

解释:
先用cat命令查看文件内容,接着用grep搜索指定的字符串,注意要添加”-n”选项以便显示匹配字符串在文件中的行号,最后用head或tail显示查找到的内容的开头部分或结尾部分字符串,”-n 1”表示只显示一行,故能实现显示第一次或最后一次字符串。打开文件(notepad++或vs code使用ctrl+g快捷键)定位到行号即可。综上,使用管道可实现目的。

ls排序

默认:按字符升序,注:如文件名为1、2、10、20,排序后变成1、10、2、20

按时间:
ls -lt # 时间新的,排在前面,对比:ls -l、ls -lt
ls -ltr # 时间新的,排在后面

按大小排序:
ls -Slh # 由大到小,h表示以K、M、G单位显示
ls -Srlh # 由小到大,即文件体积大的在后面,在文件数量多时建议使用

截取文件指定行数

背景:日志文件体积太庞大,需要截取A行到B行之间的内容。
命令:sed -n 'A,Bp' foo.txt >> output.txt
示例,截取foo.txt文件第100行到200行:sed -n '100, 200p' foo.txt >> output.txt
其它使用:通过指定行号,用此命令将大文件分割为小文件。
扩展:根据指定字符或条件来截取(如大括号,时间戳),似乎太复杂。

分割文件

背景:文件太大(如GB级别),一般编辑器打不开,需要分割之。
命令:split -a <后缀个数> -d(后缀为数字) -b <每个文件的容量> -l <每个文件行数> <要分割的文件> [分割后文件前缀]
示例:后缀为4个数字,按1MB分割,默认前缀为xsplit -a 4 -d -b 1M aaa.txt
后缀为4个数字,按1万行分割,前缀为testsplit -a 4 -d -l 10000 aaa.txt test

遗留:似乎无法指定后缀

使用read进行命令行交互

背景:使用脚本安装软件,有些目录需要确认,使用read等待用户输入信息。

1
2
3
4
5
6
7
8
echo "Is this ok? [Y]/n"
read -r confirm #! 此处会等待输入y或n
#confirm='n' #!! 如果不需要,则直接赋值为y或n即可
if [[ "${confirm}" =~ ^[nN]$ ]]; then
echo "Aborting."
exit 0
fi
echo "OK"

打包压缩指定大小

背景:github单个文件体积要小于100MB,过大无法上传,需要分割。(其它场合亦然)
命令分解:先解压,再分割。

1
2
3
4
tar jcf arm-unknown-linux-gnueabihf.tar.bz2 arm-unknown-linux-gnueabihf/
split -b 60M -d -a 1 arm-unknown-linux-gnueabihf.tar.bz2 arm-unknown-linux-gnueabihf.tar.bz2.

cat arm-unknown-linux-gnueabihf.tar.bz2.* | tar xj # 解压

查看oom得分

1
2
3
4
5
6
7
#!/bin/bash
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do
printf "%2d %5d %s\n" \
"$(cat $proc/oom_score)" \
"$(basename $proc)" \
"$(cat $proc/cmdline | tr '\0' ' ' | head -c 50)"
done 2>/dev/null | sort -nr | head -n 40

一条命令定时执行

查看某个进程的信息,循环N次。注:理论上应该死循环的,先用for

1
for i in {1..10000}; do ps aux | grep mytest; sleep 1; done

拷贝时忽略子目录

拷贝test目录到aaa目录,忽略test的downloads和output:

1
2
cd test
rsync -a --exclude downloads/ --exclude output/ --exclude build/ . ../aaa || exit

注:忽略多个目录,需要用 –exclude 。目标目录不存在会自动创建。

查看cpu核心数量

1
cat /proc/cpuinfo | grep processor | wc -l

service操作

1
2
3
4
5
6
7
8
9
vi /etc/systemd/system/edgecore.service 

systemctl daemon-reload

systemctl status edgecore

systemctl start edgecore
由于edgecore无法自行决定日志文件(可能为bug),需要从service中获取,命令:
journalctl -u edgecore > log.txt

临时

关于PID,如果在终端执行程序A,A的父进程为bash(或sh),因为是终端启动的。如果A创建子进程B,B的父进程为A,如果A创建B后自行退出,则B的父进程为1(由init接管)。如果在B中kill掉A,则A、B同时退出。