一.Shell说明
Shell是一种用C语言编写的程序,提供了用户与内核进行交互操作的一种接口,它接受用户输入的命令,并把它送入内核去执行。内核是Linux系统的心脏,从开机自检时就驻留在计算机的内存中,直到计算机关闭为止。Shell独立于内核,它是连接内核与应用的桥梁。 同时Shell是一种命令语言,也是一种程序设计语言。
通常所说的Shell都是指Shell脚本,但是shell和shell script是两个不同的概念。
Shell编程只需要一个文本编辑器和一个能解释执行的脚本解析器就可以了。
二.常见的的Shell
- Bourne Shell (/usr/bin/sh 或 /bin/sh)
- Bourne Again Shell (/bin/bash)
- C Shell (/usr/bin/csh)
- K Shell (/usr/bin/ksh)
- Shell for Root (/sbin/sh)
三.基本使用方法
- 可以通过文本编辑器创建文本并保存为.sh后缀文件。(sh代表shell)。 这里我们通过命令的方法来创建一个shell文件。在桌面新建一个名字为Shell的文件夹
#注释:执行cd 命令到文件夹目录
cd Desktop/Shell
#注释:通过touch命令创建shell文件
touch hello.sh
#注释:设置脚本解析器,使用#! 标记,#!标记告诉系统用什么脚本解析器来执行,也就是说用哪种shell
#! /bin/bash
这样一个后缀为sh的shell空文件就创建好了。下面我们可以通过文本编辑器打开我们创建的hello.sh文件,也可以通过vim命令打开。
- 添加脚本,脚本如下:
#注释:echo 用于向窗口输出文本
echo "hello world"
保存并退出。然后通过如下命令来查看文件权限
#注释:ls -l,命令以长格式的形式查看当前目录下所有可见文件的详细属性
ls -l hello.sh
使用上述命令后输出如下:
-rw-r--r-- 1 root staff 0 4 28 15:03 hello.sh
看下图
通过上述输出结果可以看出我们要执行
hello.sh
文件,但是并没有执行的权限,所以通过下面的命令添加执行权限并执行脚本:
#注释 chmod +x 添加文件的执行权限
chmod +x ./hello.sh
#注释 执行文件
./hello.sh
正确操作就可以看到hello world
的输出结果。
PS:操作文件时,一定要写成./hello.sh
,因为直接写成hello.sh
,系统会去指定目录寻找hello.sh文件,而我们并没有设置环境变量指定文件路径,使用./
就是在当前目录寻找,因为我们已经cd到了当前目录。
四.注释
shell脚本中只有单行注释,即用#符号表示注释,如需要多行注释,只需要在每行前加#即可。
# 这是一段注释
# 这里用来说明输出hello world
echo "hello world"
五.变量
- 变量定义格式如下
#注释 变量名=值
var_name="定义的变量"
PS:定义时,等号前后都不能有空格,变量名定义规则如下:
- 命名只能使用英文字母、数字和下划线组成,且首字符不能以数字开头
- 中间不能有空格
- 不能使用标点符号
不能使用bash关键字
- 只读变量
name="readonly"
readonly only
#注释 此时如果修改name的值会报错
#name="write"
- 使用变量
在变量名前加$符号即可。也可以在变量名外加上花括号,也可不加。加上花括号是为了帮助编译器识别变量的边界。
var_name="这是一个变量"
echo $var_name
echo ${var_name}
- 删除变量
使用unset
删除变量,变量删除后不可再使用(属于未初始化状态),无法使用unset
删除只读变量。
var_name="var"
unset var_name
echo $varname
- 变量类型
-
本地变量
本地变量仅可以在用户当前Shell生命期的脚本中使用的变量,随着Shell的进程的消亡而无效。类似于C、C++、Java等语言中的局部变量。 -
环境变量
适用于所有由登录进程所产生的子进程,环境变量在用户登录后到注销之前的所有地方都有效。 -
特殊变量(位置变量)
用于向Shell脚本传递参数,是只读的。
- 字符串
字符串可以使用单引号,也可以使用双引号,也可以不同引号。
- 单引号
单引号内的内容都会原样输出,不做任何处理,单引号中使用变量是无效的。而且单引号中不能出现单引号,使用转义字符也是无效的。
#注释:如果使用如下会报错
#注释:string='a string ' ' 或 string='a string \' '
#注释:如下使用不会做任何处理,直接原样输出,name为定义好的变量,例如:name='zhangsan'
#注释:string='a string $name'
string='a string'
echo $string
- 双引号
双引号中可以使用变量,并且会做处理,也可以出现转义字符。
newName="new name"
newString="a new string ' \n $newName"
echo $newString
- 字符串拼接
userName="张三"
userAge="20"
userSex="男"
#userInfo="姓名:$userName 年龄:$userAge 性别:$userSex"
#注释:也可以给变量加上双引号,输出结果是一样的
userInfo="姓名:"$userName" 年龄:"$userAge" 性别:"$userSex""
echo $userInfo
- 获取字符串长度
#注释:格式:${#变量}
lengthString="a length string"
echo ${#lengthString}
#注释:也可以使用下面的代码
lengthString="a length string"
length=${#lengthString}
echo $length
PS:此处花括号不可省略。
- 提取子字符串
#注释:格式:${字符串:起始位置:长度},起始位置下标从0开始
extractString="this is a long string"
extractNewString=${extractString:1:10}
echo $extractNewString
echo ${extractString:0:4}
也可以向下面这样操作:
extractString="this is a long string"
sourceStringLength=${#extractString}
#注释:sourceStringLength=${#extractString}-1
sourceStringLength=$sourceStringLength-1
resultString=${extractString:0:sourceStringLength}
#注释:resultString=${extractString:0:sourceStringLength-1}
echo $resultString
也可是使用一个参数
#注释:格式:${字符串:位置参数},标示从位置参数开始截取到最后
extractString="this is a long string"
echo ${extractString:5}
- 查找子字符串
- 删除字符串
字符串删除指定子字符串,是从左到右或从右到左的匹配规则,如果一开始匹配不到则删除失败。#
标示从左到右的匹配,##
,从右到左的匹配;%
从右到左的匹配,%%
从左到右的匹配。
#注释:格式:${字符串#要删除的子字符串}
removeString="this is a string"
resultRemoveString=${removeString#this}
echo $resultRemoveString
但是如果删除的子字符串不是从左第一位就开始能匹配的上则删除失败
removeString="this is a string"
resultRemoveString=${removeString#a}
echo $resultRemoveString
删除指定字符(或字符串)前所有的字符(包括指定字符),依然遵从从左到右的匹配规则,并且字符串中存在多个同样的字符时,是删除匹配到的第一个字符或字符串前的所有字符。
#注释:格式:${字符串#*要删除的子字符串} 也可以把a换成string,就是删除string本身和以前的所有字符
removeString="this is a string and new string"
resultRemoveString=${removeString#*a}
echo $resultRemoveString
从右侧匹配的例子,匹配成功后依然是删除左侧所有的字符
#注释:格式:${字符串##*要删除的子字符串}
removeString="this is a string and new string"
resultRemoveString=${removeString##*a}
echo $resultRemoveString
以上无论匹配规则如何,匹配成功后都是删除左侧的字符串,那么要删除右侧的字符串如何处理呢?看如下例子:
#注释:格式:${字符串%要删除的子字符串}
removeString="this is a string and new string a"
resultRemoveString=${removeString%a}
echo $resultRemoveString
这样会删除最右侧的a,输出this is a string and new string
删除指定范围的字符串都是一样的处理,如下代码:
#注释:格式:${字符串%要删除的子字符串*}
removeString="this is a string and new string"
#删除and以及右侧的全部字符串
resultRemoveString=${removeString%and*}
echo $resultRemoveString
下面是从左侧开始匹配,匹配到第一次匹配成功的以后删除右侧所有字符
#注释:格式:${字符串%%要删除的子字符串*}
removeString="this is a string and new string"
resultRemoveString=${removeString%%a*}
echo $resultRemoveString
- 字符串替换
替换
六.echo 指令
echo 用来做字符串的输出。
- 输出普通字符串
echo "this is a string"
#此处的双引号可以省略
echo this is a string
- 使用转义字符
echo "\"this is a string\""
- 输出变量
name="变量"
echo $name
echo ${name}
- 显示换行与不换行
换行 \n
,使用换行转义字符的时候在有些shell环境下是可以直接执行的,但是在bash环境下不能直接直接,首先可以使用```echo $SHELL命令查看当前用户正在使用的shell,如下:
ehco $SHELL
#注释:输入如下:
/bin/bash
这个时候如果定义如下输出可以看出效果:
newString="this is a new string and \n other"
echo $newString
#注释:输出结果如下:
this is a new string and \n other
我们发现换行符并没有起到作用,此时我们要使用-e
来开启转义字符的功能,用如下格式:
newString="this is a new string and \n other"
#注释:这样就可以正常换行输出了
echo -e $newString
不换行输出:
newString="this is a new string"
addString="and string"
echo $newString
echo $addString
当我们按照如上格式输出结果时,会分两行输出,但是当我们想让他们在一行输出时需要如下格式:
newString="this is a new string \c"
addString="and string"
echo -e $newString
echo $addString
#注释:/c 不换行
- 显示执行命令的结果
#打印当前时间,符号为反引号`
echo `date`
- 结果重定向至文件
newString="this is a new string"
echo $newString > newfile
#注释:这样就将newString的内容写入了newfile文件
七.数组
数组中可以存放多个值,Bash Shell只支持一维数组,各元素之间用“空格” 分隔,初始化时也不需要定义数组大小,下标从0开始,如下:
- 数组定义
#注释:格式:变量名=("参数1" "参数2" "参数3" ...)
names=("张三" "李四" "王二麻子")
#注释:这样会输出数组元素的第0个,即参数1
echo $names
也可以通过如下格式为数组元素赋值:
names=()
names[0]="张三"
names[1]="李四"
names[2]="王二麻子"
echo ${names[1]} ${names[2]}
#注释:此时的花括号不可以省略
- 获取数组所有元素
使用@
或*
可以获取数组的所有元素,如下:
names=()
names[0]="张三"
names[1]="李四"
names[2]="王二麻子"
echo ${names[1]} ${names[2]}
#输出所有数组元素
echo ${names[@]}
echo ${names[*]}
PS:使用@
或*
获取的区别在于,使用@
获取的结果是以单个元素的形式输出,使用*
获取的结果是将结果组合成字符串的形式一并输出。
- 获取数组或单个元素长度
names=()
names[0]="张三"
names[1]="李四"
names[2]="王二麻子"
echo ${names[1]} ${names[2]}
#数组元素个数
echo ${#names[@]}
echo ${#names[*]}
#单个元素的字符长度
echo ${#names[2]}
八.运算符
原生bash不支持简单的数学运算,但是可以通过其他命令实现,awk或expr,expr常用。
expr是一款表达式计算工具。
运算操作使用反引号`
- 算数运算符
以下所有运算中,运算符与表达式之间都要存在空格,没有空格是不对的。
正确:a + b
错误:a+b
#加法运算
c=`expr $a + $b`
echo $c
#减法运算
c=`expr $a - $b`
echo $c
#乘法运算,乘法运算时,需要加转义字符,使用\*
c=`expr $a \* $b`
echo $c
#除法运算
c=`expr $a / $b`
echo $c
#模运算
c=`expr $a % $b`
echo $c
#赋值运算
c=$a
echo $c
#是否相等,格式如下,必须将关键字then、else单独放在一行,并且[]与表达式之间也需要有空格,if...then...else...fi 后面会有说明。
if [ $a == $b ]
then
echo "a等于b"
else
echo "a不等于b"
fi
#是否不相等
if [ $a != $b ]
then
echo "a不等于b"
else
echo "a等于b"
fi
- 关系运算符
只支持数字或值为数字的字符串
a=1
b=2
#检测两个数是否相等
if [ $a -eq $b ]
then
echo "a等于b"
else
echo "a不等于b"
fi
#检测两个数是否相等
if [ $a -ne $b ]
then
echo "a不等于b"
else
echo "a等于b"
fi
#检测左边的数是否大于右边的数
if [ $a -gt $b ]
then
echo "a大于b"
else
echo "a不大于b"
fi
#检测左边的数是否小于右边的数
if [ $a -lt $b ]
then
echo "a小于b"
else
echo "a不小于b"
fi
#检测左边的数是否大于等于右边的数
if [ $a -ge $b ]
then
ehco "a大于等于b"
else
echo "a不大于等于b"
fi
#检测左边的数是否小于等于右边的数
if [ $a -le $b ]
then
echo "a小于等于b"
else
echo "a不小于等于b"
fi
- 布尔运算符
a=1
b=2
#布尔 非运算
if [ $a != $b ]
then
echo "a不等于b"
else
echo "a等于b"
fi
#布尔 或运算
if [ $a == $b -o $a -le $b ]
then
echo "成立"
else
echo "不成立"
fi
#布尔 与运算
if [ $a != $b -a $a -le $b ]
then
echo "成立"
else
echo "不成立"
fi
- 逻辑运算符
格式:[[ 表达式1 && 表达式2 ]]
或 [[ 表达式1 || 表达式2 ]]
a=1
b=2
#逻辑AND,&&两侧表达式同时成立为true,否则false
if [[ $a -le $b && $b -gt 1 ]]
then
echo "成立"
else
echo "不成立"
fi
#逻辑OR,||两侧表达式只要有一侧成立为true,否则为false
if [[ $a -eq $b || $b -gt 1 ]]
then
echo "成立"
else
echo "不成立"
fi
- 字符串运算符
PS:在做字符串操作的时候,在获取字符串值得过程中最好都加上双引号,如:"$string"
,避免出现一些问题。
string1="string ba"
string2="string ba"
#两个字符串是否相等
if [ "$string1" = "$string2" ]
then
echo "两个字符串一样"
else
echo "两个字符串不一样"
fi
#两个字符串是否相等
if [ "$string1" != "$string2" ]
then
echo "两个字符串不一样"
else
echo "两个字符串一样"
fi
#字符串长度是否为0
if [ -z "$string1" ]
then
echo "字符串长度为0"
else
echo "字符串长度不为0"
fi
#字符串长度是否为0
if [ -n "$string1" ]
then
echo "字符串长度不为0"
else
echo "字符串长度为0"
fi
#检测字符串是否为空
if [ "$string1" ]
then
echo "字符串不为空"
else
echo "字符串为空"
fi
- 文件测试运算符
用于检测文件的各种属性。
filePath="/Users/helmsmanmac/Desktop/Shell.sh"
#注释:检测是否是块设备文件 -b
if [ -b $filePath ]
then
echo "是块设备文件"
else
echo "不是块设备文件"
fi
#注释:检测是否是字符设备文件
if [ -c $filePath ]
then
echo "是字符设备文件"
else
echo "不是字符设备文件"
fi
#注释:检测是否为目录
if [ -d $filePath ]
then
echo "是目录"
else
echo "不是目录"
fi
#注释:检测是否为普通文件
if [ -f $filePath ]
then
echo "是普通文件"
else
echo "不是普通文件"
fi
检测二个是都是一样的。
九.参数传递
在执行shell脚本时,可以向脚本内传递参数。在脚本内获取参数的格式为:${0}
或$0
,${1}
或$1
,${2}
或$2
,以此类推...,$0
是获取当前执行的文件命令的名称。如果我们执行./hello.sh
,则获取的$0
就是./hello.sh
,也就是说命令本身就是第一个参数。
我们在脚本内添加以下脚本
echo ${0} ${1} ${2}
#echo $0 $1 $2
在终端输入如下命令即可:
#注释:注意参数之间的空格
./hello.sh hello1 hello2
以下是一些获取特殊字符处理参数:
#打印参数个数
echo ${#}
#打印参数列表
echo ${*}
echo ${@}
#打印脚本运行的当前进程ID
echo ${$}
#打印后台运行的最后一个进程的ID
echo ${!}
#打印Shell使用的当前选项
echo ${-}
#打印最后命令的退出状态,0表示没有错误,其他则为错误编号
echo ${?}
十.流程控制
- if
if [ $a -gt 1 ]
then
echo "a的值大于1"
fi
#注释:如果写成一行需要加分号分隔
if [ $a -gt 1 ]; then echo 1111; fi
- if...else
a=10
if [ $a -gt 1 ]
then
echo "a的值大于1"
else
echo "a的值不大于1"
fi
- if...else-if...else
string1=""
string2="string"
if [ $string1 = $string2 ]
then
echo "string1等于string2"
elif [ $string1 ]
then
echo "string的值不为空"
else
echo "string1为空且string1不等于string2"
fi
- for
基本格式:
for value in {list}
do
#执行的代码块(循环体)
done
#注释:依次打印1 2 3 4 5
for value in 1 2 3 4 5
do
echo $value
done
如果列表数据比较多,可以使用略写方式,使用范围:
#注释:{}内不能有空格
for value in {1..20}
do
echo $value
done
输出字符串也是一样的操作:
#注释:"one" "two" "thr" "fou" 也可以用引号包裹,也可不用,如果一个字符串中存在空格,则需要将整个字符串用引号包起来,否则会默认为由空格分割的多个字符串
for value in one two thr fou
do
echo $value
done
打印指定路径下的所有文件:
#注释:打印当前目录下的所有文件,使用ls命令,
for value in $(ls)
do
echo $value
done
也可以指定目录
#注释:指定目录,最后的*通配符,其他为省略的路径
filePath="/Users/****/Desktop/*"
for value in $filePath
do
echo $value
done
输出参数
#注释:在终端输入 ./hello.sh 1 2 3 4,会打印出输入的参数
for value in $*
do
echo $value
done
#注释:也可不带参数列表
for value
do
echo $value
done
也可以写成类C风格的for循环
#注释:c语言格式,两个口号((之间不能有空格,))也不能有
for(( i = 0; i < 10; i++))
do
echo $i
done
类C风格的写法可以省略参数,如下:
for((;;))
do
echo "hello"
done
这样就形成了一个死循环,可以按control+c退出。
当然也可以同时对两个变量进行操作:
for((i = 0, j = 10; i <= 10; i++, j--))
do
echo $i $j
done
- while
a=0
while(($a<10))
do
echo $a
# a=`expr $a + 1`
let a++
done
注释:let命令用于执行一个或多个表达式,变量计算中不需要加$。
while执行无限循环:
while :
do
echo "hello"
done
#注释:或
while true
do
echo "hello"
done
- until
a=0
until [ $a -gt 5 ]
do
echo $a
let a++
done
执行以上我们会发现,当until后的条件为假时才执行,所以until与while的执行条件正好是相反的。
- case
number=16
case $number in
1) echo "1" ;;
2) echo "2" ;;
3) echo "3" ;;
4) echo "4" ;;
5) echo "5" ;;
6) echo "6" ;;
7) echo "7" ;;
*) echo "没有对应的选项值" ;;
esac
*
为通配符,当没有对应的选项时,则执行通配符选项。类似于其它语言的default。 esac 结束标记。
- break
跳出当前循环
for value in 1 2 3 4 5
do
if [ $value -eq 3 ]
then
break
fi
echo $value
done
当value的值为3时,跳出循环,所以只能输出1 2
- continue
跳出当次循环
for value in 1 2 3 4 5
do
if [ $value -eq 3 ]
then
continue
fi
echo $value
done
跳出值为3的当次循环。 输出 1 2 4 5
网友评论