Linux Shell脚本攻略读书笔记III —— 常用命令 I
根据这段时间对于本书的阅读,总结了如下一些常用的命令
- find
- ps
- sed
- awk
下面我就来说说她们
find
find 命令的工作方式如下: 沿着文件层次结构向下遍历,匹配符合条件的文件,执行相应的操作.默认时打印出文件或者目录
基础语法为:
$ find base_path # base_path 可以是任意位置 例如 /home 会从home文件夹开始向下一次查找
如下图[find 1]所示
find 1
find命令有多个选项配合过滤文件以及文件夹例如:
-type 选项可以指定文件类型
文件类型 | 类型参数 |
---|---|
普通文件 | f |
符号链接 | l |
目录 | d |
字符设备 | c |
块设备 | b |
套接字 | s |
FIFO | p |
$ find /home -type d #这里只会打印出类型是文件夹的信息
$ find /home -type f #这里只会打印出普通文件的信息
-name 指定待查文件名的模式. 可以使通配符
$ find -name '*.txt' #查询后缀为txt的文件
可以在name选项使用逻辑操作符 如下 :
$ find /home \( -name '*.txt' -o -name '*.sh' \) #查询后缀为txt或者sh的文件
-regex 使用正则表达式查找文件
$ find /home -regex '.*\(\.txt\|\.sh\)$' # 查询满足后缀为txt或者sh的文件
-mindepth -maxdepth 指定查找的目录深度
$ find . -mindepth 2 -maxdepth 5 #表示向下查询深度从第二层目录开始 到 第五层目录
根据文件时间戳过滤
时间类型 | 参数类型 |
---|---|
访问时间 | -atime |
修改时间 | -mtime |
变化时间 | -ctime |
配合整数参数指定天数.在参数前可使用+ - 表示大于 小于
例如:
$ find /home -atime +7 #表示找出访问时间超过7天的文件
$ find /home -mtime -7 #表示找出访问时间在7天以内的文件
$ find /home -ctime 7 #表示找出文件元数据修改时间等于7天的
可以使用对应的 -amin -mmin -cmin 以分钟为记时单位的选项
-newer 选项可以指定一个参照文件 以该参照文件的最近修改时间进行比较
例如:
$ find /home -newer xxx.txt #找出比xxx.txt文件修改时间更近的文件
-size选项 指定文件大小进行过滤
$ find . -type f -size 2k #删除大小为2k的普通文件
$ find . -type f -size +2k #删除大小大于2k的普通文件
$ find . -type f -size -2k #删除大小小于2k的普通文件
字节单位 | 描述 |
---|---|
b | 块(512字节) |
c | 字节 |
w | 子(2字节) |
k | 1024字节 |
M | 1024k字节 |
G | 1024M字节 |
利用find执行相应的操作
find命令可以对其所查询到的文件执行相应的操作
-delete 命令可以删除查询到的文件 例如:
$ find /home -type f -name '*.txt' -delete #意为删除/home目录下后缀为txt的普通文件
find 命令可以通过-exec选项指定要做的操作 例如:
$ find /home -type f -name 'xxx.txt' -exec rm {} \; #这里表示删除xxx.txt文件 {}表示查询到的文件名 这里使用\ 对分号进行转义 否则shell会其视为find命令结束导致-exec无法获得参数
若find查询到的文件很多,可能会造成不小的开销那么这时候可以在命令最后使用+ 生成一份包含所有搜索结果的列表 一次性执行 例如:
$ find /home -type f -name '*txt' -exec rm {} +
PS
ps命令可以报告活跃进程的相关信息.包括,拥有进程的用户,进程的起始时间,进程对应的命令路径,PID,进程所属的终端(TTY),进程使用的内存,进程占用的CPU等。
执行如下:
$ps #ps默认显示当前终端所启动的进程,所显示的信息也比较少 如下
PID TTY TIME CMD
1050 ttys000 0:00.10 /Applications/iTerm.app/Contents/MacOS/iTerm2 --server login -fp KCZhang
1052 ttys000 0:00.61 -zsh
2626 ttys001 0:00.27 /bin/zsh --login -i
742 ttys007 0:00.08 /bin/zsh --login -i
选项 -e(every) 和 -ax(all) 能够输出系统中运行的所有进程的信息 如下
$ ps -e
$ ps -ef
$ ps -ax
由于ps -ef 获取的信息量很大,通常会通过-o选项指定列进行过滤 如下:
ps -eo comm,pcpu | head -5 #找出前五个进程 的comm和cpu使用率
COMM %CPU
/sbin/launchd 0.0
/usr/sbin/syslog 0.0
/usr/libexec/Use 0.0
/usr/libexec/kex 0.0
-o 参数列表如下
参数 | 描述 |
---|---|
pcpu | cpu使用率 |
pid | 进程id |
ppid | 父进程id |
pmem | 内存占用率 |
comm | 可执行文件名 |
cmd | 简单命令 |
user | 启动进程的用户 |
nice | 优先级 |
time | 累计的cpu时间 |
etime | 启动进程后的运行时长 |
tty | 所关联的tty设备(终端) |
euid | 有效用户id |
stat | 进程状态 |
ps -u 可根据用户进行过滤
$ ps -u root #查询有root用户创建的进程
-u选项如果配合-e选项 则不会有过滤效果 因为-e会显示所有的进程
PS内容进行排序
ps -eo comm,pcpu --sort -pcpu #找出所有进程的comm pcpu 字段并按照pcpu进行降序排列 -pcpu表示降序 +pcpu表示升序
ps -t 可根据tty进行过滤
ps -t tty1 #显示tty设备为TTY1的信息 -t选项也不能和-e一起使用
-LF选项显示进程的线程数以及线程id
如下列举出线程数量最多的前五个进程:
ps -eLF --sort -nlwp | head -5
ps -C 可以根据command name进行过滤
sed
sed是流编辑器的缩写, 一般用于文本替换。
sed 可以使用一个字符串来替换匹配模式. 模式可以使字符串和正则表达式
sed 's/pattern/replace_str/' file
也可以从stdin读取
cat file | sed 's/pattern/replace_str/'
一般情况下,sed只是修改输出的内容并不会改变文本本身的数据。当然可以使用-i选项修改文件本身的数据。
sed -i 's/pattern/replace_str/' file
注意,在使用-i选项的时候只能使用file不能通过管道传递标准输入了
以上的命令以及选项都只会匹配替换首次匹配上的内容,如果要替换所有匹配的内容需要这么做:
sed 's/pattern/replace_str/g' file
sed命令也可以结合正则表达式进行替换操作例如:
$ sed 's/^$/d' file #删除所有的空格
在sed中,可以用&指代模式所匹配到的字符串,这样就能在替换字符串时使用已经匹配到的内容,例如:
$ echo this is an example | sed 's/\w\+/[&]/g'
[this] [is] [an] [example]
还可以用\1 指代出现在括号中的部分正则表达式所匹配到的内容 例如:
$ echo this is digit 7 in number | sed 's/digit \([0-9]\)/\1/'
this is 7 in number
这里就把digit 7 替换为了 7
继续:
$ echo seven SEVEN | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
SEVEN seven
这里\2表示第二个子匹配 \1 表示第一个子匹配 所以在输出进行了交换
sed也可以组合多个表达式进行替换 多个模式之间可以用;分隔 或者使用| 或者使用-e选项 例子如下:
$ echo abc | sed 's/a/A/' | sed 's/c/C/'
AbC
$ echo abc | sed 's/a/A/;s/c/C/'
AbC
$echo abc | sed -e 's/a/A/' -e 's/c/C/'
AbC
一般情况下sed 表达式内容使用单引号引用,如果会使用到变量那么使用双引号即可 如下:
$ text=Hello
$ echo Hello world | sed "s/$text/HELLO/"
HELLO world
awk
awk是一个解释器,他能够解释并执行程序,支持关联数组,递归函数,条件语句等
awk 脚本的结构如下:
awk 'BEGIN{ print "start" } pattern { commands } END{ print 'end'}' file
awk也能从标准输入stdin中读入读取输入
下面的命令就利用了awk语法特性实现了文本行数的获取:
$ awk 'BEGIN { i=0 } {i++} END {print i}' filename
解释一下awk的工作流程:
- 首先执行BEGIN {command} 语句块
- 然后从文件或者标准输入中读取一行,如果能够匹配到pattern 则执行后面的command语句
- 重复执行第二步,知道文件读取完毕
- 当读到文件末尾时,执行END{command} 语句
BEGIN 语句块在awk开始从输入流中读取之前执行,这个部分是可选的,一般会把变量初始化,表格表头信息放在这一部分定义
END语句块于BEGIN对应,它在awk读取完毕之后执行。
awk pattern可以使用正则表达式进行行的过滤,如下:
$ echo -e "123\nabc" > awk_test.txt
$ awk 'BEGIN{print "start"} /[a-z]+/ {print} END {print "end"} '
start
abc
end
$awk 'BEGIN{print "start"} /[0-9]+/ {print} END {print "end"} '
start
123
end
awk的特殊变量
变量 | 描述 |
---|---|
NR | 表示记录编号 |
NF | 字段数量 |
$0 | 包含当前记录的文本内容 |
$1 | 包含记录第一个字段的内容 |
$2 | 包含记录第二个字段的内容 |
例如
$ echo -e "line1 f2 f3\nline2 f4 f5" | awk '{print "Line no : "NR", No of fields:"NF", $0="$0",$1="$1""}'
Line no : 1, No of fields:3, $0=line1 f2 f3,$1=line1
Line no : 2, No of fields:3, $0=line2 f4 f5,$1=line2
$(NF) 可以获取到最后一个字段
$(NF-1) 获取倒数第一个字段
例如:
$ echo "This is an example" | awk '{print $(NF)}'
example
$ echo "This is an example" | awk '{print $(NF-1)}'
an
awk可以根据选项-v将外部的变量传递到awk使用
例如
$ VAR=1000
$ echo "VAR" | awk -v var1=$VAR '{print $0 var1}'
VAR1000
还有另一种方式如下:
$ VAR=1000
$ echo "VAR" | awk '{print $0 var1}' var1=$VAR
VAR1000
利用getline 读取行
getline 用于读取当前的行,若在BEGIN中使用,则在主语句块中会过滤掉getline获取到的行。例如:
$ echo -e "123\nabc\n456\ndef" | awk 'BEGIN{getline;} {print}'
abc
456
def
注意到在begin中我使用了getline;主语句块中print打印行,但是第一行并没有打印出来,因为在BEGIN中使用了getline这时候主语句块所读取流就不会有第一行了.所以getline用来处理表格头部信息等类似数据十分有用
利用过滤条件进行处理 例如
$ echo -e "123\nabc\n456\ndef" | awk 'BEGIN{getline;} NR < 3 {print}'
abc
这里通过NR (行号) 进行过滤,由于BEGIN中使用了getline因此只打印了abc
awk 每行记录默认使用空格作为分隔符 如下:
$ echo -e "12 3\na bc\n4 56\nd ef" | awk '{print $1}'
12
a
4
d
也可以通过-F选项设置分隔符例如:
$ echo -e "1x2\naxb\n3x4" | awk -F "x" '{print $1}'
1
a
3
还有第二种写法在BEGIN语句块中使用FS,如下
$ echo -e "1x2\naxb\n3x4" | awk 'BEGIN{FS="x"} {print $1}'
1
a
3
在awk中使用循环进行输出 例子如下:
准备测试文本:
$ cat awk_loop.txt
tw:chengdu:china
tw:wuhai:china
tw:xi'an:china
$ awk 'BEGIN{FS=":"}{name[$2]=$3} END{for (i in name) {print i,name[i]}}' awk_loop.txt
wuhai china
xi'an china
chengdu china
以上的命令使用了关联数组以及for循环 打印测试文本
awk内建函数一览:
函数名 | 参数 | 描述 |
---|---|---|
length() | string | 返回字符串的长度 |
index() | string,search_string | 返回search_string 在string中出现的位置 |
split() | string, array,delimiter | 以delimiter作为分隔符将结果存入array |
substr() | string, start-position,length | 从start-postion截取字符串长度为length |
sub() | regex,replacement,string | 将regex匹配到的内容替换为replacement |
gsub() | regex,replacement,string | 将所有匹配到的结果替换为replacement |
match() | string,regex | 判断string中能否匹配到string中的任何内容,若是则返回非0 反之 返回0 |
实例如下:
split:
$ echo -e "tw:chengdu:china" | awk 'BEGIN{FS=":"}{split($0,name,":")} END{for(i in name) {print name[i]}}'
substr:
$ echo -e "tw:chengdu:china" | awk 'BEGIN{FS=":"}{print substr($0,1,10)}'
tw:chengdu
sub:
$ echo "tw:tw:chengdu:china" | awk 'sub(/tw/,"thoughtworks",$0)'
thoughtworks:tw:chengdu:china
gsub:
$ echo "tw:tw:chengdu:china" | awk 'sub(/tw/,"thoughtworks",$0)'
thoughtworks:thoughtworks:chengdu:china
match
$ echo "tw:tw:chengdu:china" | awk '{print match($0,/tw/)}'
1
这是部分常用命令总结,后面我还会总结一下其他常用命令.