1、Shell编程概述
Shell是一个命令行解释器,它接收应用程序或用户的命令,然后调用操作系统内核。
Shell还是一个功能强大的编程语言,易编写、易调试、灵活性强。
①Linux系统提供的Shell解析器
[root@hadoop101 ~]$ cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
其中最常用的是/bin/sh和/bin/bash
②bash和sh的关系
[root@apple w]# ll /usr/bin | grep bash$
-rwxr-xr-x. 1 root root 964544 4月 11 2018 bash
lrwxrwxrwx. 1 root root 4 7月 9 16:42 sh -> bash
sh是bash的软链接
③Linux默认的解析器是bash
[root@apple w]# echo $SHELL
/bin/bash
④Shell语法总述
2、Shell编程HelloWorld
①创建脚本文件
通常以.sh作为扩展名。
②脚本内容
第一行指定当前脚本的解析器:
#!/bin/bash
实现具体功能:
echo "hello world"
③Shell脚本的运行方式
命令名 | 在当前进程运行 | 新建子进程运行 |
---|---|---|
source | √ | |
. | √ | |
sh | √ | |
bash | √ | |
chmod +x后直接运行 | √ |
其中“.”是source的另一种写法。在当前进程中发布的全局变量可以在当前进程的其他脚本中继续沿用,也可以在子进程中使用;但是子进程export发布的变量仅限于子进程内部使用。
从上面进程树中能够看到,sh、bash、直接运行这三种方式确实是开辟新的子进程运行脚本中的代码。
3、变量
①系统预定义变量
常用系统变量包括: U S E R 、 USER、 USER、HOME、 P W D 、 PWD、 PWD、SHELL等,可以使用echo命令输出它们的值。
②使用set命令查看所有变量
ABRT_DEBUG_LOG=/dev/null
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extglob:extquote:force_fignore:histappend:interactive_comments:login_shell:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
……
③自定义变量
[1]基本语法
(1)定义变量
USER_NAME=tom
(2)撤销变量
[root@apple ~]# USER_NAME=tom
[root@apple ~]# echo $USER_NAME
tom
[root@apple ~]# unset USER_NAME
[root@apple ~]# echo $USER_NAME
(3)声明静态变量
[root@apple ~]# readonly COMPANY_NAME=atguigu
[root@apple ~]# echo $COMPANY_NAME
atguigu
[root@apple ~]# COMPANY_NAME=uuu
-bash: COMPANY_NAME: 只读变量
[root@apple ~]# unset COMPANY_NAME
-bash: unset: COMPANY_NAME: 无法反设定: 只读 variable
不能修改,不能撤销
[2]语法规则
- 变量名称可以由字母、数字和下划线组成,但是不能以数字开头,环境变量名建议大写。
- 等号两侧不能有空格
- 在bash中,变量默认类型都是字符串类型,无法直接进行数值运算。
- 变量的值如果有空格,需要使用双引号或单引号括起来。
④特殊变量
[1]$n
n是数字,$0代表当前脚本名称。从 1 开 始 代 表 对 应 的 脚 本 参 数 。 从 1开始代表对应的脚本参数。从 1开始代表对应的脚本参数。从{10}开始数字需要使用{}括起来。
[root@hadoop101 datas]$ touch parameter.sh
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0 $1 $2"
[root@hadoop101 datas]$ chmod 777 parameter.sh
[root@hadoop101 datas]$ ./parameter.sh cls xz
./parameter.sh cls xz
[2]$#
返回输入参数的个数
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0 $1 $2"
echo $#
[root@hadoop101 datas]$ chmod 777 parameter.sh
[root@hadoop101 datas]$ ./parameter.sh cls xz
parameter.sh cls xz
2
[3] ∗ 和 *和 ∗和@
都能够返回全部参数,但是只有在循环中且放在引号中能够体现出它们的区别。
[root@hadoop101 datas]$ vim parameter.sh
#!/bin/bash
echo "$0 $1 $2"
echo $#
echo $*
echo $@
[root@hadoop101 datas]$ bash parameter.sh 1 2 3
parameter.sh 1 2
3
1 2 3
1 2 3
[4]$?
返回上一条命令的执行结果。
- 条件判断语句
- 返回0表示true
- 返回1表示false
- 普通语句
- 返回0表示成功
- 返回非0数表示失败
4、运算符
( ( 表 达 式 ) ) 或 ((表达式))或 ((表达式))或[表达式]
[root@apple ~]# echo $((20+30))
50
[root@apple ~]# echo $(((15+15)*2))
60
[root@apple ~]# echo $[(15+15)*2]
60
5、条件判断
①基本语法
- 写法1:test condition
- 写法2:[ condition ]
- 注意condition前后有空格
- 空字符串视为false,非空字符串视为true
②常用判断条件
数据类型 | 写法 | 含义 |
---|---|---|
数值 | -lt | less than小于 |
数值 | -le | less equal小于等于 |
数值 | -eq | equal等于 |
数值 | -gt | greater than大于 |
数值 | -ge | greater equal大于等于 |
数值 | -ne | not equal不等于 |
文件 | -r | read有读权限 |
文件 | -w | write有写权限 |
文件 | -x | execute有执行权限 |
文件 | -f | file文件存在并且是一个常规的文件 |
文件 | -e | existence文件存在 |
文件 | -d | directory文件存在并是一个目录 |
6、流程控制
①三目运算
[root@apple w]# [ 10 -gt 5 ] && echo "10大于5" || echo "10小于5"
10大于5
②if判断
注意:if后面有空格
[1]单if
if [ 10 -gt 5 ]
then
echo "10大于5"
fi
[2]if…else
if [ 10 -gt 5 ]
then
echo "10大于5"
else
echo "10小于5"
fi
[3]if…elif
if [ 10 -gt 5 ]
then
echo "10大于5"
elif [ 10 -lt 5 ]
then
echo "10小于5"
else
echo "10等于5"
fi
③case判断
AGE=10
case $AGE in
"10")
echo 10
;;
"20")
echo 20
;;
*)
echo other
;;
esac
④for循环
for (( i=1;i<=10;i++ ))
do
echo $i
done
⑤for in循环
for i in $*
do
echo $i
done
没有引号的时候 ∗ 和 *和 ∗和@一样,加了引号就有区别
for i in "$*"
do
echo $i
done
上面代码输出的结果是:
[root@apple w]# sh shell42.sh a b c d e
a b c d e
⑥while循环
s=0
i=1
while [ $i -le 100 ]
do
s=$[$s+$i]
i=$[$i+1]
done
echo $s
7、函数
Shell编程中的函数和我们以前熟悉的函数最大的区别是:Shell编程中要求函数的返回值只能是整数。并且只能通过$?方式获得。可以显示加:return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0-255)。
function sum() {
echo $[$1+$2]
}
sum 10 20
function sum() {
return $[$1+$2]
}
sum 33 55
echo $?
8、获取脚本外部数据
①获取参数
这个前面说过了,通过$1、 2 、 … … 方 式 获 取 , 从 2、……方式获取,从 2、……方式获取,从{10}开始需要使用大括号。
②使用read读取用户输入
read命令有两个常用参数
- -t用于指定输入等待时间,单位是秒
- -p用于指定提示文字
read -t 10 -p "please enter:" NAME
echo $NAME
9、Shell工具
①basename
返回路径字符串中的资源(文件或目录本身)部分
[root@apple w]# basename /aa/bb/cc/dd
dd
如果指定了后缀,basename会帮我们把后缀部分也去掉
[root@apple workspace]# basename /aa/bb/cc/dd.txt .txt
dd
②dirname
返回路径字符串中的目录部分
[root@apple w]# dirname /aa/bb/cc/dd
/aa/bb/cc
③cut
根据指定符号拆分字符串并提取。默认根据\t拆分。
- -f参数:指定要提取的列
- -d参数:指定拆分依据的字符
准备测试数据:
[root@hadoop101 datas]$ touch cut.txt
[root@hadoop101 datas]$ vim cut.txt
dong shen
guan zhen
wo wo
lai lai
le le
切割提取第一列:
[root@hadoop101 datas]$ cut -d " " -f 1 cut.txt
dong
guan
wo
lai
le
切割提取第二、第三列:
[root@apple w]# cut -d " " -f 2,3 cut.txt
shen
zhen
wo
lai
la
在cut.txt中切出guan
[root@hadoop101 datas]$ cat cut.txt | grep "guan" | cut -d " " -f 1
guan
选取系统PATH变量值,第2个“:”开始后的所有路径:
[root@hadoop101 datas]$ echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/atguigu/bin
[root@hadoop101 datas]$ echo $PATH | cut -d : -f 2-
/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/atguigu/bin
切割ifconfig 后打印的IP地址:
[root@apple w]# ifconfig | grep "netmask" | cut -d "i" -f 2 | cut -d " " -f 2
192.168.41.100
127.0.0.1
192.168.122.1
另一种做法:
[root@apple workspace]# ifconfig | grep netmask | cut -d " " -f 10
192.168.41.100
127.0.0.1
192.168.122.1
④sed(了解)
sed是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”,接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。
准备测试数据:
[root@hadoop101 datas]$ touch sed.txt
[root@hadoop101 datas]$ vim sed.txt
dong shen
guan zhen
wo wo
lai lai
le le
在第二行下面添加字符串:
[root@hadoop101 datas]$ sed '2a mei nv' sed.txt
dong shen
guan zhen
mei nv
wo wo
lai lai
le le
[root@hadoop101 datas]$ cat sed.txt
dong shen
guan zhen
wo wo
lai lai
le le
删除带有“wo”的行:
[root@hadoop101 datas]$ sed '/wo/d' sed.txt
dong shen
guan zhen
lai lai
le le
将“wo”替换为”ni“
[root@hadoop101 datas]$ sed 's/wo/ni/g' sed.txt
dong shen
guan zhen
ni ni
lai lai
le le
先删除第二行,再执行替换
[root@hadoop101 datas]$ sed -e '2d' -e 's/wo/ni/g' sed.txt
dong shen
ni ni
lai lai
le le
⑤awk
一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
[1]基本用法
awk [选项参数] ‘pattern1{action1} pattern2{action2}…’ filename
pattern:表示AWK在数据中查找的内容,就是匹配模式
action:在找到匹配内容时所执行的一系列命令
使用-F参数指定分隔符。
awk命令的内置变量包括:
变量名 | 说明 |
---|---|
FILENAME | 文件名 |
NR | 已读取的记录 |
NF | 浏览记录的域的个数(切割后,列的个数) |
[2]测试
# 准备数据
[root@hadoop101 datas]$ sudo cp /etc/passwd ./
# 搜索passwd文件以root关键字开头的所有行,并输出该行的第7列。
[root@hadoop101 datas]$ awk -F: '/^root/{print $7}' passwd
/bin/bash
# 搜索passwd文件以root关键字开头的所有行,并输出该行的第1列和第7列,中间以“,”号分割。
[root@hadoop101 datas]$ awk -F: '/^root/{print $1","$7}' passwd
root,/bin/bash
# 只显示/etc/passwd的第一列和第七列,以逗号分割,且在所有行前面添加列名user,shell在最后一行添加"dahaige,/bin/zuishuai"。
[root@hadoop101 datas]$ awk -F : 'BEGIN{print "user, shell"} {print $1","$7} END{print "dahaige,/bin/zuishuai"}' passwd
user, shell
root,/bin/bash
bin,/sbin/nologin
。。。
atguigu,/bin/bash
dahaige,/bin/zuishuai
# 将passwd文件中的用户id增加数值1并输出
[root@hadoop101 datas]$ awk -v i=1 -F: '{print $3+i}' passwd
1
2
3
4
# 统计passwd文件名,每行的行号,每行的列数
[root@hadoop101 datas]$ awk -F: '{print "filename:" FILENAME ", linenumber:" NR ",columns:" NF}' passwd
filename:passwd, linenumber:1,columns:7
filename:passwd, linenumber:2,columns:7
filename:passwd, linenumber:3,columns:7
# 切割IP
[root@apple workspace]# ifconfig | awk -F " " '/netmask/{print $2}'
192.168.41.100
127.0.0.1
192.168.122.1
# 查询sed.txt中空行所在的行号
[root@hadoop101 datas]$ awk '/^$/{print NR}' sed.txt
5
PS:如果命令很长,可以使用反斜杠换行输入
[root@apple workspace]# awk -F : \
> 'BEGIN{print "user\t\tshell"} {print $1"\t\t"$7} END{print "dahaige\t\t/bin/zuishuai"}' \
> passwd
⑥sort
sort命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出。
参数名 | 作用 |
---|---|
-n | 依照数值大小排序 |
-r | 相反顺序排序 |
-t | 设置排序时使用的分隔字符 |
-k | 指定需要排序的列 |
# 准备数据
[root@hadoop101 datas]$ touch sort.sh
[root@hadoop101 datas]$ vim sort.sh
bb:40:5.4
bd:20:4.2
xz:50:2.3
cls:10:3.5
ss:30:1.6
# 测试
[root@hadoop101 datas]$ sort -t : -nrk 3 sort.sh
bb:40:5.4
bd:20:4.2
cls:10:3.5
xz:50:2.3
ss:30:1.6
[root@apple workspace]# sort -nrt : -k 3 sort.txt
bb:40:5.4
bd:20:4.2
cls:10:3.5
xz:50:2.3
ss:30:1.6
⑦wc
wc命令用来计算数字。利用wc指令我们可以计算文件的Byte数、字数或是列数。
参数 | 作用 |
---|---|
-l | 统计行数 |
-w | 统计单词数 |
-m | 统计字符数 |
-c | 统计字节数 |
10、正则表达式入门
符号 | 含义 |
---|---|
^ | 匹配字符串开始位置的字符 |
$ | 匹配字符串结束位置的字符 |
. | 匹配任何一个字符 |
* | 匹配前面的字符出现0~n次 |
[a,m,u] | 匹配字符a或m或u |
[a-z] | 匹配所有小写字母 |
[A-Z] | 匹配所有大写字母 |
[a-zA-Z] | 匹配所有字母 |
[0-9] | 匹配所有数字 |
\ | 特殊符号转义 |
11、企业面试真题
①京东
[1]问题1:
使用Linux命令查询file1中空行所在的行号
[root@hadoop101 datas]$ awk '/^$/{print NR}' sed.txt
5
[2]问题2:
有文件chengji.txt内容如下:
张三 40
李四 50
王五 60
使用Linux命令计算第二列的和并输出
[root@hadoop101 datas]$ cat chengji.txt | awk -F " " '{sum+=$2} END{print sum}'
150
[root@apple workspace]# awk '{sum+=$2} {print $1","$2} END{print "总分:"sum}' chengji.txt
张三,50
小红,60
小刚,100
总分:210
②搜狐&和讯网
Shell脚本里如何检查一个文件是否存在?如果不存在该如何处理?
#!/bin/bash
if [ -f file.txt ]
then
echo "文件存在!"
else
echo "文件不存在!"
fi
③新浪
用shell写一个脚本,对文本中无序的一列数字排序
[root@CentOS6-2 ~]# cat test.txt
9
8
7
6
5
4
3
2
10
1
[root@CentOS6-2 ~]# sort -n test.txt|awk '{a+=$1;print $1}END{print "SUM="a}'
1
2
3
4
5
6
7
8
9
10
SUM=55
④金和网络
请用shell脚本写出查找当前文件夹(/home)下所有的文本文件内容中包含有字符”shen”的文件名称
[root@hadoop101 datas]$ grep -r "shen" /home | cut -d ":" -f 1
/home/atguigu/datas/sed.txt
/home/atguigu/datas/cut.txt
[root@apple workspace]# grep -r "shen" /root/workspace/
/root/workspace/sed.txt:dong shen
/root/workspace/cut.txt:dong shen
[root@apple workspace]# grep -r "shen" /root/workspace/ | awk -F : '{print $1}'
/root/workspace/sed.txt
/root/workspace/cut.txt
[root@apple workspace]# grep -r "shen" /root/workspace/ | awk -F : '{print $1}' | awk -F '/' '{print $4}'
sed.txt
cut.txt
l写一个脚本,对文本中无序的一列数字排序
[root@CentOS6-2 ~]# cat test.txt
9
8
7
6
5
4
3
2
10
1
[root@CentOS6-2 ~]# sort -n test.txt|awk '{a+=$1;print $1}END{print "SUM="a}'
1
2
3
4
5
6
7
8
9
10
SUM=55
④金和网络
请用shell脚本写出查找当前文件夹(/home)下所有的文本文件内容中包含有字符”shen”的文件名称
[root@hadoop101 datas]$ grep -r "shen" /home | cut -d ":" -f 1
/home/atguigu/datas/sed.txt
/home/atguigu/datas/cut.txt
[root@apple workspace]# grep -r "shen" /root/workspace/
/root/workspace/sed.txt:dong shen
/root/workspace/cut.txt:dong shen
[root@apple workspace]# grep -r "shen" /root/workspace/ | awk -F : '{print $1}'
/root/workspace/sed.txt
/root/workspace/cut.txt
[root@apple workspace]# grep -r "shen" /root/workspace/ | awk -F : '{print $1}' | awk -F '/' '{print $4}'
sed.txt
cut.txt