该系列是基于牛客Shell题库,针对具体题目进行查漏补缺,学习相应的命令。
刷题链接:牛客题霸-Shell篇。
该系列文章都放到专栏下,专栏链接为:《专栏:Linux》。欢迎关注专栏~
本文知识预告:
- 本文首先总结了
awk
命令中的各个内置函数的用法;
- 然后给出了五种题目的解决方法,需要使用到之前学习到的正则表达式。
写一个bash脚本以实现一个需求,去掉输入中含有B和b的单词。假设输入如下:
big
nowcoder
Betty
basic
你的脚本获取以上输入应当输出:
nowcoder test
说明:你可以不用在意输出的格式,空格和换行都行
相关命令学习
awk
:文本和数据进行处理的编程语言
awk
命令来自于三位创始人”Alfred Aho,Peter Weinberger, Brian Kernighan “的姓氏缩写,其功能是用于对文本和数据进行处理的编程语言。使用awk
命令可以让用户自定义函数或正则表达式对文本内容进行高效管理,与sed
、grep
并称为Linux系统中的文本三剑客。
语法格式:awk 参数 文件
常用参数:
参数 |
功能 |
-F |
指定输入时用到的字段分隔符 |
-v |
自定义变量 |
-f |
从脚本中读取awk 命令 |
-m |
对val 值设置内在限制 |
常用的awk
内置变量:
awk
语法由一系列条件和动作组成,在花括号内可以有多个动作,多个动作之间用分号分隔,在多个条件和动作之间可以有若干空格,也可以没有。
变量名称 |
说明 |
FILENAME |
当前输入文档的文件名 |
FNR |
当前输入文档的当前行号,尤其当多个输入文档时有用 |
FS |
设置字段分隔符,默认为空格或制表符 |
NF |
当前记录(行)的字段(列)个数 |
NR |
输入数据流的当前记录数(行号) |
OFS |
输出字段分隔符,默认为空格 |
ORS |
输出记录分隔符,默认为换行符 |
RS |
输入记录分隔符,默认为换行符 |
awk
是一种处理文本文件的编程语言,文件的每行数据都被称为记录,默认以空格或制表符为分隔符,每条记录被分成若干字段(列),awk
每次从文件中读取一条记录。
例子:
- 仅显示指定文件中第1、2列的内容(默认以空格为间隔符):
lucky@DESKTOP-VQ8KID4:~/shell$ awk '{print $1,$2}' nowcoder.txt
#include <iostream>
using namespace
int main()
{
int a
int b
cout <<
return 0;
}
- 以冒号为间隔符,仅显示指定文件中第1列的内容:
lucky@DESKTOP-VQ8KID4:~/shell$ awk -F : '{print $1,$2}' /etc/passwd
root x
daemon x
bin x
...
tcpdump x
sshd x
landscape x
pollinate x
lucky x
/etc/passwd
文件中的内容由:
分隔开。
- 以冒号为间隔符,显示系统中所有UID号码大于500的用户信息(第3列):
lucky@DESKTOP-VQ8KID4:~/shell$ awk -F : '$3>=500' /etc/passwd
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
lucky:x:1000:1000:,,,:/home/lucky:/bin/bash
- 仅显示指定文件中含有指定关键词
main
的内容:
lucky@DESKTOP-VQ8KID4:~/shell$ awk '/main/{print}' nowcoder.txt
int main()
- 以冒号为间隔符,仅显示指定文件中最后一个字段的内容:
lucky@DESKTOP-VQ8KID4:~/shell$ awk -F : '{print $NF}' /etc/passwd
/bin/bash
/usr/sbin/nologin
/usr/sbin/nologin
...
/usr/sbin/nologin
/bin/false
/bin/bash
- 输出行号,
NR
将所有文件的数据视为一个数据流,而FNR
则是将多个文件的数据视为独立的若干个数据流,遇到新文件时行号从1开始重新递增。
lucky@DESKTOP-VQ8KID4:~$ awk '{print NR}' first.txt three.sh
1
2
3
lucky@DESKTOP-VQ8KID4:~$ awk '{print FNR}' first.txt three.sh
1
1
2
grep
:强大的文本搜索工具
grep
来自于英文词组“global search regular expression and print out the line”的缩写,意思是用于全面搜索的正则表达式,并将结果输出。人们通常会将grep
命令与正则表达式搭配使用,参数作为搜索过程中的补充或对输出结果的筛选,命令模式十分灵活。
与之容易混淆的是egrep
命令和fgrep
命令。如果把grep
命令当作是标准搜索命令,那么egrep
则是扩展搜索命令,等价于“grep -E
”命令,支持扩展的正则表达式。而fgrep
则是快速搜索命令,等价于“grep -F
”命令,不支持正则表达式,直接按照字符串内容进行匹配。
语法格式: grep [参数] 文件
常用参数:
参数 |
功能 |
-i |
忽略大小写 |
-c |
只输出匹配行的数量 |
-l |
只列出符合匹配的文件名,不列出具体的匹配行 |
-n |
列出所有的匹配行,显示行号 |
-h |
查询多文件时不显示文件名 |
-s |
不显示不存在、没有匹配文本的错误信息 |
-v |
显示不包含匹配文本的所有行 |
-w |
匹配整词 |
-x |
匹配整行 |
-r |
递归搜索 |
-q |
禁止输出任何结果,已退出状态表示搜索是否成功 |
-b |
打印匹配行距文件头部的偏移量,以字节为单位 |
-o |
与-b 结合使用,打印匹配的词据文件头部的偏移量,以字节为单位 |
-F |
匹配固定字符串的内容 |
-E |
支持扩展的正则表达式 |
- 搜索某个文件中,包含某个关键词的内容:
lucky@DESKTOP-VQ8KID4:~/shell$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
- 搜索某个文件中,以某个关键词开头的内容:
lucky@DESKTOP-VQ8KID4:~/shell$ grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash
- 搜索多个文件中,包含某个关键词的内容:
root@DESKTOP-VQ8KID4:~# grep lucky /etc/passwd /etc/shadow
/etc/passwd:lucky:x:1000:1000:,,,:/home/lucky:/bin/bash
/etc/shadow:lucky:$6$SBxuPYFLSnBcfbHN$OkFFnnJCpf2P4OLOnnaWXMq.xbmgL3H5aRy4nkEkk/.8VHABaKDS6MdYm3UR3TpHZplAl5HVyffI8nbLlAAoh1:19256:0:99999:7:::
- 搜索多个文件中,包含某个关键词的内容,不显示文件名称:
root@DESKTOP-VQ8KID4:~# grep -h lucky /etc/passwd /etc/shadow
lucky:x:1000:1000:,,,:/home/lucky:/bin/bash
lucky:$6$SBxuPYFLSnBcfbHN$OkFFnnJCpf2P4OLOnnaWXMq.xbmgL3H5aRy4nkEkk/.8VHABaKDS6MdYm3UR3TpHZplAl5HVyffI8nbLlAAoh1:19256:0:99999:7:::
- 输出在某个文件中,包含某个关键词行的数量:
root@DESKTOP-VQ8KID4:~# grep -c root /etc/passwd /etc/shadow
/etc/passwd:1
/etc/shadow:1
- 搜索某个文件中,包含某个关键词位置的行号及内容:
lucky@DESKTOP-VQ8KID4:~/shell$ grep -n int nowcoder.txt
3:int main()
5: int a = 10;
6: int b = 100;
- 搜索某个文件中,不包含某个关键词的内容:
lucky@DESKTOP-VQ8KID4:~/shell$ grep -v int nowcoder.txt
#include <iostream>
using namespace std;
{
cout << "a + b:" << a + b << endl;
return 0;
}
- 搜索当前工作目录中,包含某个关键词内容的文件,未找到则提示:
root@DESKTOP-VQ8KID4:/# grep -l root *
grep: bin: Is a directory
grep: boot: Is a directory
grep: dev: Is a directory
grep: etc: Is a directory
grep: home: Is a directory
init
grep: lib: Is a directory
grep: lib32: Is a directory
...
grep: tmp: Is a directory
grep: usr: Is a directory
grep: var: Is a directory
- 搜索当前工作目录中,包含某个关键词内容的文件,未找到不提示:
lucky@DESKTOP-VQ8KID4:~/shell$ grep -sl main *
nowcoder.txt
- 递归搜索,不仅搜索指定目录,还搜索其内子目录内是否有关键词文件:
root@DESKTOP-VQ8KID4:/# grep -srl root /etc
/etc/services
/etc/ltrace.conf
/etc/systemd/logind.conf
/etc/crontab
/etc/newt/palette.ubuntu
/etc/xattr.conf
/etc/apparmor.d/tunables/home
...
- 搜索某个文件中,精准匹配到某个关键词的内容(搜索词应与整行内容完全一样才会显示,有别于一般搜索):
lucky@DESKTOP-VQ8KID4:~/shell$ grep -x "return 0;" nowcoder.txt
lucky@DESKTOP-VQ8KID4:~/shell$ grep -x " return 0;" nowcoder.txt
return 0;
- 判断某个文件中,是否包含某个关键词,通过返回状态值输出结果(0为包含,1为不包含),方便在Shell脚本中判断和调用:
lucky@DESKTOP-VQ8KID4:~/shell$ grep -q return nowcoder.txt
lucky@DESKTOP-VQ8KID4:~/shell$ echo $? # 包含
0
lucky@DESKTOP-VQ8KID4:~/shell$ grep -q returns nowcoder.txt
lucky@DESKTOP-VQ8KID4:~/shell$ echo $? # 不包含
1
- 搜索某个文件中,空行的数量:
lucky@DESKTOP-VQ8KID4:~/shell$ grep -c ^$ nowcoder.txt
0
awk
命令中的内置函数
字符串函数
-
sub
和gsub
函数:sub
函数在记录中查找能够匹配正则表达式的最长且最靠左的字串,然后用替换子串替换找到的子串。默认是整个记录,可指定目标字符串。
sub(regualr expression,substitution string);
sub(regualr expression,substitution string,target string);
例子:
lucky@DESKTOP-VQ8KID4:~$ awk '{sub(/now/,"nowcoder"); print}' nowcoder.txt
big
nowcodercoder
Betty
basic
nowcoder
gsub
类似于sub
的用法,但是sub
只对目标串中出现的第一个匹配进行替换。gsub
则对字符串中的正则表达式进行全局替换,即替换出现在目标串的每一次匹配成功的子串。
lucky@DESKTOP-VQ8KID4:~$ awk '{gsub(/now/,"nowcoder"); print}' nowcoder.txt
big
nowcodercoder
Betty
basic
nowcoder
-
index
函数:index
函数返回子串在字符串中的第一次出现的位置。下标从1开始算起。语法格式为:
index(string, substring)
例子:
lucky@DESKTOP-VQ8KID4:~$ awk 'BEGIN{print index("hellow", "lo")}'
4
-
length
函数:这个函数很常用,返回字符串的字符个数,支持中文字符。语法格式为:
length(string)
例子:
lucky@DESKTOP-VQ8KID4:~$ awk 'BEGIN{print length("helloworld")}'
10
-
substr
函数:该函数返回从字符串指定位置开始的一个子串(从1开始算起)。如果指定了子串的长度,则返回字符串相应的部分。如果指定长度超出极限,则返回实际内容。语法格式为:
sbustr(string, starting position, length for substring);
例子:
lucky@DESKTOP-VQ8KID4:~$ awk 'BEGIN{print substr("Santa Claus",7,6)}'
Claus
-
match
函数:该函数用于返回正则表达式在字符串中出现的位置,如果没有出现,则返回0。其内置变量RSTART
保存开始位置,RLEGNTH
保存匹配到的长度。语法格式:
match(string,regular expression)
例子:
awk 'BEGIN{start=match("good morning everyone",/n... e/); print start,RSTART,RLENGH}'
-
split
函数:该函数使用有用户自定义的分割符来分割一个字符串,保存到一个数组中。如果没有提供分割符,则使用FS作为分割符。语法格式为:
split(string, array, fieldseparator);
split(string, array);
例子:
lucky@DESKTOP-VQ8KID4:~$ awk 'BEGIN{split("2013/06/16",date,"/"); print date[2],date[3]}'
06 16
-
printf
函数:同C语言的printf
用法。
常用算术函数
-
int(x)
:x
的整数部分,直接去掉小数部分,不四舍五入。
-
cos(x)
:x
的余弦值,x
是弧度
-
sin(x)
:x
的正弦值,x
是弧度
-
exp(x)
:x
和e
的指数函数
-
log(x)
:x
的自然对数,底数为e
-
sqrt(x)
:x
的平方根
-
rand( )
:返回一个0到1之间的随机数
-
srand(x)
:设定rand
的种子,常与rand()
一起使用,以产生不同的随机数。例子:
lucky@DESKTOP-VQ8KID4:~$ awk 'BEGIN{srand(); print rand()}'
0.136167
lucky@DESKTOP-VQ8KID4:~$ awk 'BEGIN{srand(); print rand()}'
0.548888
用户自定与函数
脚本中凡是可以出现模式操作规则的位置都可以放置用户自定义函数。
function function_name(parameter1,parameter2....){
statements
return expression
}
注意事项:
- 变量的传递是传值调用,而且变量只在该函数中局部有效。数组则是地址引用。
- 函数中出现的任何变量,如果不是作为参数列表传进来的,都是全局变量。
- 调用函数时1如果没有指定某个形参的值,则设定为空。
题目解决方案
方法一:sed
命令
sed '/[Bb]/d' nowcoder.txt
或者
sed '/B\|b/d' nowcoder.txt
方法二:grep
命令
grep -v [Bb] nowcoder.txt
方法三:awk
正则
awk '!/[Bb]/{print $0}' nowcoder.txt
方法四:awk
命令配合match
函数
awk '!match($0, /[Bb]/){print $0}' nowcoder.txt
方法五:循环
while read line; do
if [[ $line =~ [bB] ]]; then
continue
else
echo $line
fi
done < nowcoder.txt