一、什么是shell?
shell是处于用户和Linux系统之间的命令解释器。
二、shell基础操作
1.创建shell脚本
vi first_shell.sh
2.shell脚本的格式
#!/bin/bash
#filename:first_shell
#created day:2019.9.28
echo "Hello World!"
第一行:#!/bin/bash
#!是一种约定标记,他告诉系统这个脚本需要用/bin/bash解释器来执行,如果不写这一行脚本也可以执行,只是系统会根据自身的shell解释器来解释脚本。
第二和第三行
shell脚本使用#来标识注释。
第四行:echo “Hello World!”
shell将没有#标识的语句解释为命令。
shell脚本的命令语句可以在行末使用;
表示结束,也可以不适用,但是一行有多条语句必须使用;
分隔。
3.执行脚本
/bin/bash first_shell.sh
输出结果:
Hello World!
三、变量
1.局部变量
shell中没有特别标注的变量都属于局部变量,如:
#!/bin/bash
#by goulandis 2019.9.28
A="asd"
echo $A
A=123
echo $A
局部变量只能在脚本内部才能使用。
shell是非类型的解释型语言,变量的类型在运行事确定。
字符串也可以不要使用""
,如:A=asd
,输出结果也是一样的,但是不使用""
赋值时,字符串之间不能出现空格,如:A=a s d
这样赋值会报错。
输出结果:
asd
123
变量的使用
shell中直接使用$加变量名的组合即可使用变量,如:echo $A
需要注意的是:在给变量赋值时不能出现空格,如:A = 123,这样的语法shell无法识别,必须为:A=123,才可以。
2.环境变量
环境变量可以在脚本内也可以在其他的脚本中使用,即在系统环境中都可以使用。
变量名 | 作用 |
---|---|
$0 | 存储当前脚本的名字 |
$n | n是一个数字,存储传递给脚本的迪n个变量 |
$# | 存储传递给脚本的参数的数量 |
$* | 存储传递给脚本的所有的参数 |
$@ | 用法和$*一样// |
$? | 存储上一个命令的退出状态,命令执行成功返回0,否则返回错误码 |
$$ | 存储当前shell脚本所在进程的ID |
$UID | 存储当前用户ID |
$PWD | 存储当前所在路径 |
示例:
注:
echo -e "\033[32m"
可以修改其后的输出字体的颜色。
echo -e "\033[0m"
结束字体颜色修改。
字体设置详情请前往:https://blog.csdn.net/andylauren/article/details/60873400
输出时可以使用\来进行字符转义
3.变量的命名规则
shell
的变量的命名规则基本和C是一致的,并且shell大小写敏感。
四、语句结构
1.if条件语句
普通结构
if((num1>num2))
then
echo "$num1>$num2"
else
echo "$num1<$num2"
fi
和其他语言不同的是,shell中条件判断分支语句不能为空,即不能出现如下情况:
if((num1>num2))
then
echo "$num1>$num2"
else
fi
如此编译将无法通过。
这里有几点须要注意:
- if后面更的必须是双括号(()),使用单括号会报错,使用(())变量可以直接使用变量名来使用,而不需要通过 来 引 用 , 除 此 之 外 , i f 后 面 也 可 以 跟 [ ] , 但 是 [ ] 与 变 量 之 间 必 须 使 用 空 格 隔 开 , [ ] 与 i f 也 必 须 使 用 空 格 分 隔 , 且 使 用 [ ] 需 要 使 用 来引用,除此之外,if后面也可以跟[],但是[]与变量之间必须使用空格隔开,[]与if也必须使用空格分隔,且使用[]需要使用 来引用,除此之外,if后面也可以跟[],但是[]与变量之间必须使用空格隔开,[]与if也必须使用空格分隔,且使用[]需要使用来引用变量,否则也会报错;
使用双括号:**
a=10
b=20
if((a>b))
then
echo "$a > $b"
else
echo "$a < $b"
fi
使用中括号(不使用空格分隔):
这里有一个错误,即if[a>b]应该改为if[
a
>
a>
a>b]。
使用中括号(使用空格分隔):
-
一行中的多条语句须要使用;进行分隔;如:
if((num1>num2));then echo "$num1>$num2" fi
-
if语句必须使用fi进行结尾,告知解释器if语句的结束;
嵌套结构
#!/bin/bash
#by goulandis 2019.9.29
if [[ $1 -gt 90 ]];then
echo "every good"
elif [[ $1 -gt 80 ]];then
echo "good"
elif [[ $1 -gt 60 ]];then
echo "pass"
else
echo "no pass"
fi
这里须要注意,每一个if和elif语句后面都必须跟一个then。
逻辑运算符
shell中除了可以使用“+,-,*,/,%,^,>,>=,==,<,<=,!”等编程常用的运算符外,还可以使用字母代替部分运算符。
运算符 | 作用 |
---|---|
-f | 判断文件是否存在 eg: if [ -f filename ] |
-d | 判断目录是否存在 eg: if [ -d dir ] |
-eq | 等于,整型比较 |
-ne | 不等于,整型比较 |
-lt | 小于,整型比较 |
-gt | 大于,整型比较 |
-le | 小于或等于,整型比较 |
-ge | 大于或等于,整型比较 |
-a | 逻辑与 eg: 逻辑表达式 -a 逻辑表达式 |
-o | 逻辑或 eg: 逻辑表达式 -a 逻辑表达式 |
-z | 空字符串 |
如向文件中写入内容:
第一次执行/bin/bash var.sh asd:创建文件text.txt并向文件中写入传入的第一个参数内容;
第二次执行/bin/bash var.sh asd:文件已存在,所以直接cat文件内容。
(())、[]、[[]]的区别
在写shell脚本时,常碰到(())、[]、[[]],这里来看看三者之间的区别:
符号 | 区别 |
---|---|
(()) | (())的作用基本和let一致,(())主要用于算术运算,比较适合进行整数比较,可以直接通过变量名来引用变量,而不需要通过$,只可以使用数学运算符“>,<,==”等进行比较; |
[] | []的作用与test一致,[]不支持数学运算符,只能使用字符运算符“-ge,-lt”等,[]支持“和!=”,但是“”,“!=”在[]中只能用于字符串的比较; |
[[]] | [[]]的功能比[]要强大,[[]]的用法和[]一样,但是[[]]支持逻辑组合“&&,||” |
小知识:
- 用反引号包含的语句可以当作命令解释。如:
shell解释器会将反引号内的字符串解释为一个命令(如果命令有效则执行,如果无效则报错),而不是当作字符串。
组合与&&不止可以组合逻辑表达式,还可以组合命令语句,并且组合命令语句时,当前面的语句执行失败,后面的语句将不执行。而组合或||则是无论前面的语句是否执行成功都会执行后面的语句。
2.for循环
普通结构
j=0
for((i=1;i<=100;i++))
do
j=`expr $i + $j`
done
echo $j
输出结果:
goulandis@ubuntu:~/shell$ /bin/bash var.sh
5050
小知识:
- 这里的循环语句使用的是j=`expr $i + $j`,因为必须告知解释器要将
expr $i + $j
解释为命令,否则解释器会将expr $i + $j
当作是字符串。 - expr命令的作用是求两个数的和。
- 在执行脚本时,添加参数
-x
可以查看脚本运行的过程,如:/bin/bash -x var.sh
for in
结构
cd /home/goulandis/shell/tmp
dir=/home/goulandis/shell
for file in $(ls *)
do
mv $file $dir
done
file
的值从$(ls *)
中依次获取,$file
即获取file
的值; 循环语句放在do和done之间。
for in
结构的好处在于,可以从一个命令的输出集合里自动读取集合里的数据。、
‘’、""的区别
两者都是为了解决字符串中间有空格的情况,''
剥夺了字符串的所有含义,在''
内的任何字符都只被解释为字符,而""
则保留了一些参数如:$、``、\。
小知识:
-
$(ls *)
可以获取当前目录下的所有文件 -
$(ls split*)
可以获取当前目录下的所有子目录
3.while循环
while read line
do
echo $line
done < /home/goulandis/shell/var.sh
i=0
while ((i<5))
do
echo $i
((i++))
done
输出语句:
#!/bin/bash
#by goulandis 2019.9.28
while read line
do
echo $line
done < /home/goulandis/shell/var.sh
i=0
while ((i<5))
do
echo $i
((i++))
done
0
1
2
3
4
-
while
之后可以跟命令语句(如:reas line
),也可以更逻辑表达式(如:i<5
),但是逻辑表达式必须使用(())
括起来,并且while
的递增变量也必须使用(())
括起来。 - 和
for
一样,while
的循=循环体必须放在do done
之间。
小知识:
-
read
命令配合< 文件名
可以读取文件里的内容。如:读取host.txt文件的第二列的内容文件内容:
1 Alian man 176
2 Goulandis faleman 168
3 Dekes man 188
操作代码:
while read line
do
name=`echo $line | awk '{print $2}'`
echo -e "\033[32mName:$name\033[0m"
done <host.txt
读取结果:
goulandis@ubuntu:~/shell$ /bin/bash var.sh
Name:Alian
Name:Goulandis
Name:Dekes
4.until循环
i=5
until [[ i -lt 0 ]]
do
echo $i
((i--))
done
输出结果:
goulandis@ubuntu:~/shell$ /bin/bash var.sh
5
4
3
2
1
0
与while
当满足条件时执行不同的是,until
是当满足条件时退出;
和所有循环体一样,until
的循环体也放在do done
中。
5.case条件语句
case $1 in
apache)
echo "Install Apache"
;;
mysql)
echo "Install mysql"
;;
php)
echo "Install php"
;;
*)
echo "usage:{$0 apache|mysql|php|help}"
;;
esac
-
case
语句必须使用esac
标识语句结束 - 每一个分支的匹配值后面是必须跟一个
)
的 - 每一个分支也必须使用
;;
进行分隔 - 不像C++中的
switch
语句没有break
不跳出而继续向下执行,shell
中的case
语句执行完分支后会自动跳出 -
*
表示当分支中没有能比配的值时,就匹配*
这个分支
6.select语句
select
语句的主要功能就是自动生成一个选择菜单
PS3="select application number:"
select i in "apache" "mysql" "php"
do
case $i in
apache)
echo "install apache"
;;
mysql)
echo "install mysql"
;;
php)
echo "install php"
;;
*)
echo "usage:{input 1|2|3|help}"
esac
done
输出结果:
goulandis@ubuntu:~/shell$ /bin/bash var.sh
1) apache
2) mysql
3) php
select application number:1
install apache
-
in
后面可以跟一系列的键值集合,每个键值之间使用空格分隔 -
shell
预定义了变量PS3
(大写的PS3)用于输入描述,如:输出结果中的select application number:
-
select
和case
搭配能形成十分友好的选择界面 -
select
的语句体也必须放在do done
中间
五、数组
1.一维数组
数组定义
A=(1 2 3)
数组使用
echo ${A[0]}
shell的数组也是从0号索引开始
几种数组的常用方法
方法 | 作用 |
---|---|
${A[@]}或${A[*]} | 显示所有元素 |
${#A[@]} | 统计数组的参数个数 |
${A[@]/1/a} | 替换数组元素 |
unset A[n] | 删除数组中第n个元素 |
数组的赋值
直接初始化时赋值
A=(
apache
mysql
php
)
一个元素一个元素的赋值
#A=()可写也可不写
A=()
A[0]=0
A[1]=1
六、函数
函数定义
function command()
{
#函数体
}
函数调用
command 参数列表
参数列表按顺序依次对应$1,$2,$3等,如:
function command()
{
if [[ (! -z $1) && (! -z $2) ]]
then
echo $1 $2
fi
}
command param1 param2
输出结果:
goulandis@ubuntu:~/shell$ /bin/bash var.sh
param1 param2
这里要注意使用&&
时一个逻辑表达式需要用()
括起来,否则&&
会将$1
与!
进行&&
运算。
函数的返回值
函数用return
来返回值,但是return
只能用于返回整型,返回字符串需要用echo
。如:
#返回数字-方法1
function command()
{
if(($1==1));then
return $1
else
echo $1
fi
}
command 1
echo $?
#返回数字-方法2
echo $command 1
#返回字符串-方法1
echo $(command asd)
#返回字符串-方法2
echo `command sdf`
输出结果:
goulandis@ubuntu:~/shell$ /bin/bash var.sh
1
1
asd
sdf
$?
接收上一句命令的返回值。
小知识:
read -p "输入描述" cmd
命令可以记录命令命令行的输入到cmd变量中,如:
goulandis@ubuntu:~/shell$ read -p "Input parameter:" cmd
Input parameter:this a command
goulandis@ubuntu:~/shell$ echo $cmd
this a command
七、shell命令
1.sed命令
示例:对如下sed.txt文件进行修改
#by auther 2019.9.30 txt
ip 192.168.9.120
ip 192.168.5.5
ip 168.1.2.111
those my ip
替换字符串
sed -i 's/auther/goulandis/g' sed.txt
输出结果:
#by goulandis 2019.9.30 txt
ip 192.168.9.120
ip 192.168.5.5
ip 168.1.2.111
those my i
将sed.txt
中的所有auther
字符串替换成字符串goulandis
,其中开头的s
和结尾的g
是必须写的,这是固定格式,并且参数-i
也是必须写的,如果不使用参数-i
则sed
只会修改缓冲区中的数据,而不会修改sed.txt
源文件中的内容。
在行首/行尾插入
sed -i 's/^/&line /g' sed.txt
输出结果:
line #by goulandis 2019.9.30 txt
line
line ip 192.168.9.120
line ip 192.168.5.5
line ip 168.1.2.111
line
line those my ip
在sed.txt
的每行行首插入字符串line
,行首匹配规则使用的是正则表达式
,若需要在行尾插入,命令为sed -i 's/$/&字符串/g' sed.txt
。
在前一行或后一行插入
sed -i '/192.168.9.120/a line ip 192.168.9.9' sed.txt
输出结果:
line #by goulandis 2019.9.30 txt
line
line ip 192.168.9.120
line ip 192.168.9.9
line ip 192.168.5.5
line ip 168.1.2.111
line
line those my ip
在sed.txt
的192.168.9.120
所在行的后面一行插入字符串line ip 192.168.9.9
,这里要注意,这个命令不再写前面的s
和后面的g
,如果要在前面一行插入,则将a
替换成i
。
打印固定行数的内容
sed -n '1,3p' sed.txt
输出结果:
line #by goulandis 2019.9.30 txt
line
line ip 192.168.9.120
打印sed.txt
中第一行到第三行的内容,其中p
是必须写的 ,打印首行和尾行sed -n '1p;$p' sed.txt
。
这里要注意,'1,$p'
表示从首行到尾行,有多行,'1p;$p'
表示首行和尾行,只有两行。
2.find命令
寻找文件
find . -name "sed.txt"
在当前目录下寻找文件sed.txt
。
find . -maxdepth 1 -name "sed.txt"
在当前目录一级目录下搜索sed.txt
文件,即不向子目录搜索。
find . -maxdepth 1 -type f -name "*.txt" -mtime -1
在当前目录一级目录下搜索类型为文件,修改时间为1天前的txt
文件。
find . -maxdepth 1 -type f -name "*.txt" -mtime -1 -exec rm -rf {} \;
在当前目录一级目录下搜索类型为文件,修改时间为1天前的txt
文件并删除这些文件。-exec
表示将前面搜索到的内容放到{}
中。其中\;
是必须的固定格式。-xargs
的作用和-exec
基本一样,只是-xargs
不能配合cp
和mv
命令使用。
3.grep命令
搜索固定内容的行
grep "ip" sed.txt
输出结果:
line ip 192.168.9.120
line ip 192.168.9.9
line ip 192.168.5.5
line ip 168.1.2.111
line those my ip
使用参数-v
表示反义,如:grep -v "ip" sed.txt
,表示搜索不包含ip
的行。-n
可以将行号一并打印出来。
grep "120$" sed.txt
搜索以120
结尾的行,开头使用"^line"
,""
中写入正则表达式,即可按正则表达式来搜索内容。
4.awk命令
打印列
awk '{print "注解:" $1}' sed.txt
打印第一列的内容,列之间需要用空格分隔,否则将识别成一列,$n
表示第n列。$NF
表示最后一列,""
中可以在输出内容前添加任何注解。