Linux Shell脚本攻略读书笔记III —— 常用命令 I

2018-07-27  本文已影响0人  Great_Bug

根据这段时间对于本书的阅读,总结了如下一些常用的命令

  1. find
  2. ps
  3. sed
  4. 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的工作流程:

  1. 首先执行BEGIN {command} 语句块
  2. 然后从文件或者标准输入中读取一行,如果能够匹配到pattern 则执行后面的command语句
  3. 重复执行第二步,知道文件读取完毕
  4. 当读到文件末尾时,执行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

这是部分常用命令总结,后面我还会总结一下其他常用命令.

上一篇 下一篇

猜你喜欢

热点阅读