Linux命令实践

本文记录 Linux 常用命令记录。<>为必填,[]为可选,()为注释性文字。
注:命令一般没有系统之分,但经常在ubuntu中测试,偶尔也在Centos测试。

查找系统体积最大的文件

1
2
3
4
5
find / -type f -print0 2>/dev/null | xargs -0 du -h 2>/dev/null | sort -rh | head -n 10
find / -type f -exec du -Sh {} + | sort -rh | head -n 10
find / -type f -print0 | xargs -0 du | sort -n | tail -10 | cut -f2 | xargs -I{} du -sh {}
find / -type f -ls | sort -k 7 -r -n | head -10 | column -t | awk '{print $7,$11}'

注:先用find查找(可指定目录),用排序,选中最大的N个。

1
find / -name "..."  2>&1 | grep -v "Permission denied"

查找替换

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

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

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

1
2
find ./ -name "*[*.h|.c|.cpp]"  # 注:这种形式会匹配到.sh
find ./ -name '*.cpp' -or -name '*.c' -or -name "*.h"

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

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

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

sed -i 's/intMAX/INT_MAX/g' foo.cpp # 只替换foo.cpp文件

注:可以修改grep查找路径来指定目录。
注:grep -l可查看匹配的文件名。-n可显示出匹配的行号。

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"

删除某个指定的字符串,此处为"LATLEE".(注意双引号和点号):

1
2
sed -i 's/"LATLEE".//g' foo.txt

代码格式化

去掉行尾多余空格

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

注:执行命令后,要用exit退出终端,否则意外断开连接时,程序会自动退出。

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

查找指定文件某字符串第一次出现位置:
命令: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
其它使用:通过指定行号,用此命令将大文件分割为小文件。
扩展:根据指定字符或条件来截取(如大括号,时间戳),似乎太复杂。
注意:传入 shell 变量时,需要使用双引号,单引号无法解析变量值。

分割文件

背景:文件太大(如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

遗留:似乎无法指定后缀
NOTE:如果要合并,则使用cat * > a.txt,不过似乎不好指定顺序。

在指定行数追加字符串

如:在 a.txt 的第10行后面插入 COMMIT;

1
sed -i '10a\COMMIT;' ver.sql

目的:每隔1万行插入COMMIT;,使用 shell 获取行号,但用上面语句失败。转换思路:
先按1万行分割,得到 x 开头的文件(注:此时没有后缀),在文件末追加字符串,再合并。

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

split -a 4 -d -l 10000 $1

for i in `ls x*`;
do
#echo $i
mv $i $i.sql
echo "COMMIT;" >> $i.sql
cat $i.sql >> new_$1
done

使用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 。目标目录不存在会自动创建。

查找忽略指定目录

1
find ./ -name '*.cpp' -or -name '*.c' -not -path '**/11.serialport_linux/**' ! -path "*/*socket*/*"

注:似乎直接指定目录无效果,前后要添加*not!等效。多个目录要分别指定。

查看cpu核心数量

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

查看已删除的文件占用的进程(?如何描述)

1
2
3
lsof | grep delete

删除:lsof | grep delete | awk '{print $2}' | xargs kill -9

service操作

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

systemctl daemon-reload

systemctl status edgecore

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

用indent代码格式化

查找某目录下所有源码,转换成linux格式,再格式化。

1
2
find ./  -name '*.cpp' -or -name '*.c' -or -name "*.h" | xargs dos2unix 
find ./ -name '*.cpp' -or -name '*.c' -or -name "*.h" | xargs indent -linux -bad -bap -bl -nce -cdw -bli0 -cli0 -cbi0 -ncs -lp -l200 -npcs -saf -sai -saw -i4 -nut

临时

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

windows 重定向:

1
2
./foo 1>a.txt 2>&1

注:界面不显示信息,待改

shell 脚本参数截取

目的:从第N个参数开始遍历。相当于前N个固定,后N个不固定,如IP地址列表。

1
2
3
4
5
# 从第3个开始
for var in "${@:3}";
do
echo $var
done

root密码输入

问题:linux输入密码不回显,有时不清楚输入了什么内容。如果多次尝试失败,系统可能会拒绝登录。可以在命令行显式指定密码。eg:

1
2
3
4
echo "123456" | sudo -S ls   # 先指定密码,如果正确,再切换。

sudo -s

字符串截取

总则:使用${xxx}形式截取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var=https://latelee.org/foo.html

删除指定字符的左侧字符,-- 使用#:
echo ${var#*//}
表示删除`//`左侧所有字符,即删除 http://。结果:latelee.org/foo.html

删除最后出现的字符前的字符,(反向查找第一个字符) ——使用##:
echo ${var##*/}
表示删除最后一个`/`前面所有字符。结果:foo.html

接上,保留左侧,删除右侧,类似取前缀,——使用 %:
echo ${var%/*}
表示删除最后一个`/`后的所有字符,结果:https://latelee.org

删除右侧,保留左侧, ——使用 %%
echo ${var%%/*}
表示删除`/`右侧所有字符,保留左侧。结果:http:。

按个数从左侧截取,——使用冒号`:` :
echo ${var:0:5}
从0开始截取5个字符,结果:https

从左侧第几个字符开始,直到结束:
echo ${var:8}
从第8个字符开始,直到结束。结果:latelee.org/foo.html

与上类似,但从右侧算起。——使用`0-`:
echo ${var:0-8:3}
从右侧向前算第8个字符,截取9个字符。结果:foo

与上类似,从右侧算,直到结束:
echo ${var:0-8}
结果:foo.html

注:(左边的第一个字符是用 0 表示,右边的第一个字符用 0-1 表示)

字符串补齐

1
2
3
4
5
6
7
i=0
COUNT=1
while [ $i -le $COUNT ]
do
caridx=`printf "%04d_1" "$i"`
echo $caridx
done

与 C 语言的 printf 类似
shell 时间计算。支持 minute、hour,默认为时间后,时间前使用 ago,以下为当前时间前后:

1
2
3
time1=$(date -d "10 minute ago" +"%Y-%m-%d %H:%M:%S")
time2=$(date -d "10 minute" +"%Y-%m-%d %H:%M:%S")
exTime=$(date -d "1 hour" +"%Y-%m-%d %H:%M:%S")

查看动态库函数名称

1
nm libfoo.so | awk '{print $3}' | c++filt.exe  | tee cfunc.txt

先查动态库的函数名,过滤得到第3列(真正函数名),去掉c++修饰,保存文件。

C++函数名去掉装饰(mangled、demangled)

压缩包添加密码

1
zip -q -r -P 123456 foo.zip foo

注:不同时间压缩的zip文件,其二进制可能会不同(计算md5会有差异),因其考虑了文件访问时间。保存二进制相同,使用:

1
zip -qr -X -D foo.zip foo

根据偏移量定位文件

以 txt 文件为例,先用 hexdump 输出到指定文件,如:

1
hexdump foo.out  -C > a.txt

根据 a.txt 中的偏移地址计算即可。该文件示例:

1
2
3
4
5
6
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010 02 00 3e 00 01 00 00 00 b7 38 40 00 00 00 00 00 |..>......8@.....|
00000020 40 00 00 00 00 00 00 00 b8 01 03 00 00 00 00 00 |@...............|
00000030 00 00 00 00 40 00 38 00 09 00 40 00 26 00 25 00 |....@.8...@.&.%.|
00000040 06 00 00 00 05 00 00 00 40 00 00 00 00 00 00 00 |........@.......|
00000050 40 00 40 00 00 00 00 00 40 00 40 00 00 00 00 00 |@.@.....@.@.....|

清缓存

1
2
sudo -s
echo 3 >/proc/sys/vm/drop_caches

停止指定进程

停掉包含demo-0.0.1的进程:

1
kill $(ps aux | grep demo-0.0.1 | grep -v "grep" | tr -s ' '| cut -d ' ' -f 2)

获取某个运行的进程信息,并 kill 掉,一行 shell :

1
[[ $(ps aux | grep demo*.jar | grep -v 'grep' | awk '{print $2;}') != "" ]] && kill -9 $(ps aux | grep demo*.jar | grep -v 'grep' | awk '{print $2;}') 

注:如果使用 if 判断的话,当不存在进程时会报语法错误,故只用一行shell解决。

修改字符集

1
2
3
4
localectl set-locale LANG=en_US.UTF-8
localectl set-locale LANG=zh_CN.UTF-8
临时生效的:
export LANG=en_US.UTF-8

加密压缩解压

对当前 workshop目录加密压缩并解压,密码为123456。

1
2
3
tar -cjf - workshop | openssl des3 -salt -k 123456 -out workshop.tar.bz2

openssl des3 -d -k 123456 -salt -in workshop.tar.bz2 | tar xjf -