Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。
Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。
Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。
其中Bash 在日常工作中被广泛使用。同时,Bash 也是大多数Linux 系统默认的 Shell。
1. 创建一个文件,扩展名为.sh注意:扩展名并不影响脚本执行,只是为了见名知意。
2. 修改文件的权限为可执行文件
3. 执行 (eq:./test.sh):./表示当前目录下
1. #!是一个约定的标记,用来指定执行脚本的shell
2. #注释
3. 命令和控制结构
实例:
#!/bin/bash
echo "Hello World !"
运行结果:
注意:一定要携程./test.sh,而不是test.sh,运行其他二进制的程序也是一样,直接写test.sh,linux系统会去PATH里寻找有没有叫做test.sh的,而只有/bin,/sbin,/usr/bin,/usr/sbin等在PATH里,你的当前目录通常不在PATH里,所以直接写成test.sh是找不到命令的,要用./test.sh告诉系统,就在当前目录下寻找。
your_name=”xiaoming”
除了显式地直接赋值,还可以使用语句给变量赋值,如
for file in `ls /etc`或for file in $(ls /etc)
注:for循环的使用方法在后面会详细说明
注意:
①变量和等号之间不能有空格。
②习惯用大写字母来命名变量
③命令的执行结果赋值给变量时,使用反单引号如,TIME=`date`此命令可以获得当前系统时间
变量的命名规则:
1. 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
2. 中间不能有空格,可以使用下划线(_)
3. 不能使用标点符号。
4. 不能使用bash里的关键字(可使用help命令查看保留关键字)
使用一个定义变量,只要在变量名前加美元符号($),如
your_name="qinjx"
echo $your_name
echo ${your_name}
注:变量名外面的大括号是可以选择的,加不加都行,加大括号的目的只是想让解析器区分变量的边界
使用readonly,此命令可以将变量定义为只读变量,只读变量的值是不能改变的,如:
#!/bin/bash
name="hello world !"
readonly name
name="hello kitty !"
运行结果:
使用unset,变量删除后不能再次被使用,unset不能删除只读变量,如
#!/bin/sh
name1="HelloWorld"
name2="HelloKitty"
readonly name2
unset name1
unset name2
echo $name1
echo $name2
运行结果:
注:删除的变量,再次输出为空
1)临时变量:也叫做局部变量,在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量
2)永久变量:也叫做环境变量,所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也是可以定义环境变量。
3)shell变量:shell变量是由shell程序设置的特殊变量。shell变量的中的一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行
字符串是shell编程中最常用最有用的数据类型。字符串可以使用单引号,也可以用双引号,也可以不用引号。
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
单引号字串中不能出现单引号(对单引号使用转义符后也不行)。
如:
name=HelloWorld
name2=’HelloWorld${name}’
name3=’Hello’World’’
echo $name
echo $name2
echo $name3
运行结果:
双引号里可以有变量
双引号里可以出现转义字符
如:
name=HelloWorld
name2=”HelloWorld${name}”
name3=”Hello\”World\””
echo $name
echo $name2
echo $name3
运行结果:
两种方式 :
your_name="小明"
#无缝拼接
greeting="hello, "$your_name" !"
#双引号可以直接使用变量
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
运行结果:
语法1:${#name}
语法2:expr length ${name}
string="abcd"
echo ${#string} #输出4
length=`expr length "${name}"`
Echo $length#输出4
语法:
${name:startIndex:length}#索引开始,截取length长度
${name:startIndex}#开始索引到结尾
${name:0-index:lenght}#索引从后开始,截取length长度
${name:0-index}#索引从后开始,截取到结尾
实例:
string="runoob is a great site"
echo ${string:1:4} # 输出 unoo
echo ${string:1}#输出unoob is a great site
echo ${string:0-4:3}#输出sit
echo ${string:0-4}#输出site
注:还可以使用#、##、%、%%进行删除后保留子串
①#、##:从左边开始删除。
#表示从左边删除到第一个指定字符;
##表示从左边删除到最后一个指定的字符
②%、%%:从右边开始删除
%:表示从右边删除到第一个指定字符
%%:表示从右边删除到第一个指定字符
注:删除包括了指定的字符本身
如:
#使用#、##、%、%%进行删除后保留子串
string="http:/www.baidu.com/a.html"
echo "使用#得到字符串:${string#*/}"
echo "使用##得到的字符串:${string##*/}"
echo "使用%得到字符串:${string%/*}"
echo "使用%%的到字符串:${string%%/*}"
运行结果:
使用命令expr
string="runoob is a great company"
echo `expr index "$string" is` # 输出 8
1. Bash仅仅支持一维数组,并且没有限定数组的大小。
2. 数组元素的下标从0开始。
3. 获取数组中的元素要利用下标。
4. 下标使用不当,会报错
语法1:arrayName=(值1 值2 ……值n)
语法2:arrayName=(
值1
值2
……
值n
)
注:元素间使用空格分隔开或者换行符
也可以单独定义数组的各个分量:
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
注:可以不使用连续的下标,而且下标的范围没有限制
格式:${数组名[下标]}
如:
#!/bin/bash
#数组的使用规则
#数组第一种赋值方法
my_array1=(A B "C" 34)
#数组第二种赋值方法
my_array2=(
A
B
"C"
34
)
#数组第三种赋值方法
my_array3[0]=A
my_array3[1]=B
my_array3[2]="C"
my_array3[3]=34
#获取单个元素
echo "获取第一个元素:"${my_array1[0]} "获取第三个元素"${my_array1[2]}
运行结果:
格式:${name[*]}或${name[@]}
#!/bin/bash
#数组的使用规则
#数组第一种赋值方法
my_array1=(A B "C" 34)
#数组第二种赋值方法
my_array2=(
A
B
"C"
34
)
#数组第三种赋值方法
my_array3[0]=A
my_array3[1]=B
my_array3[2]="C"
my_array3[3]=34
#获取所有元素,使用*或@
echo "使用@方式获取my_array:"${my_array1[@]}
echo "使用*方式获取my_array:"${my_array1[*]}
echo "my_array2:"${my_array2[*]}
echo "my_array3:"${my_array3[*]}
运行结果:
格式:${#name[*]}或${#name[@]}
如:
#!/bin/bash
#数组的使用规则
#数组第一种赋值方法
my_array1=(A B "C" 34)
#数组第二种赋值方法
my_array2=(
A
B
"C"
34
)
#数组第三种赋值方法
my_array3[0]=A
my_array3[1]=B
my_array3[2]="C"
my_array3[3]=34
#获取数组长度
echo "my_array1长度:${#my_array1[*]}"
echo "my_array2长度:${#my_array2[*]}"
echo "my_array3长度:${#my_array3[@]}"
运行结果:
Shell解释用户执行的命令时,会将命令行的一串字符进行解析划分成部分。每个部分都有一个固定的变量名$n,不能自定义。
$n: n从0开始,依次往后+1。从$1开始 叫位置参数变量。大于等于10时,必须使用{}将数字括起来
Ex:./test1.sh file1 file2 file3 ….. file10
$0 的值为:ls -la
$1 的值为file1
…….
${10} 的值为file10
如:
#位置变量
echo "第一个参数:$0"
echo "第二个参数:$1"
echo "第三个参数:$2"
echo "第十一个参数:$10"
echo "第十一个参数有括号:${10}"
运行结果:
还有其他参数:
变量名称 | 意义 |
$* | 获取所有参数,合并成一个字符串 |
$# | 参数个数 |
$$ | 当前进程的ID |
$! | 后台运行的最后一个进程的ID |
$? | 显示最后一个命令的退出状态。0表示没有错误 |
从键盘读取数据,赋值给变量
如:
read username
echo "你输入的用户名为:"$username
运行结果:
注1:当输入的词组个数大于需要的参数个数,则多出的词组将被作为整体为最后一个参数接收。
完整版read命令:
read –p “请输入密码:” –n 6 -t 5 password
echo -e “\npassword is $password”
参数说明:
-p:输出提示文字
-n 输入字符串长度,达到此长度,自动结束
-t 输入限制时间
-s 隐藏输入内容
如:
read -p "请输入用户名要求:长度为6输入时间限制为5秒:" -n 6 -t 5 username
read -p "请输入密码:隐藏" -s password
echo "你输入的用户名:${username}和${password}"
运行结果:
用途:对整数型变量进行算术运算
如:
expr 3 + 5
expr 3 \* 5
运行结果:
复杂计算需要使用反单引号“·”,对应键盘上“1”旁边的键
如:
expr ` expr 5 - 1` / 2
num=` expr $var / 2`
运行结果:
简便写法:
格式$[表达式]
如:
#简便方法实现运算
num=$[5 - 1]
echo $num
var=4
num=$[var /2]
echo $num
用途:用于字符串的输出显示
普通输出,直接使用echo “想要输出的内容”
开启转移:-e
如:
echo It is a test file
echo "hello \n world"
echo -e "hello \n world"
echo -e "helloworld \c"
运行结果:
转义字符说明:
\n #换行
\c #不换行
注:不加-e转义字符没有转义效果用途: 与echo功能相同,用来输出,功能更强大,如格式化字符串,指定字符串的宽度,左右对齐方式,默认不换行,可以手动添加\n
语法:
printf format-string [arguments……...]
说明:
format-string: 格式控制字符串
arguments: 参数值列表
%d %s %c %f 格式替代符详解:
d: Decimal 十进制整数 -- 对应位置参数必须是十进制整数,否则报错!
s: String 字符串 -- 对应位置参数必须是字符串或者字符型,否则报错!
c: Char 字符 -- 对应位置参数必须是字符串或者字符型,否则报错!
f: Float 浮点 -- 对应位置参数必须是数字型,否则报错!
也有其他的替代符,读者需自行查找
如:
printf "HelloWorld"
printf "HelloWorld\n"
printf "%-20s %-16s %-8s\n" 姓名 性别 体重kg
printf "%-20s %-16s %-8.2f\n" 张三 男 45.1234
printf "%-20s %-16s %-8.2f\n" 李四 男 76.5678
printf "%-20s %-16s %-8.2f\n" 王五 女 40.6542
运行结果:
转义字符说明:
转义序列 | 意义 |
\b | 退格 |
\f | 换页显示 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | 转义斜杠 |
用途:用于测试变量是否相等、是否为空、文件类型等
格式:
test 测试条件
测试范围:整数测试,字符串测试,文件测试
①整数测试test v1 -eq v2 测试两个数是否相等
test v1 -gt v2 测试是否 v1>v2
test v1 -ge v2 测试是否 v1>=v2
test v1 -lt v2 测试是否 v1
test v1 -le v2 测试是否 v1<=v2
test v1 -ne v2 测试两个数是否不相等
②字符串测试
test str1=str2 测试字符串是否相等
test str1!= str2 测试字符串是否不相等
test str1 测试字符串是否不为空
test -n str1 测试字符串是否不为空
test -z str1 测试字符串是否为空
③文件测试
test -d file 指定文件是否目录
test -f file 指定文件是否是常规文件
test -x file 指定文件是否可执行
test -r file 指定文件是否可读
test -w file 指定文件是否可写
test -a file 指定文件是否存在
test -s file 文件的大小是否非0
另注:通常test命令不单独使用,而是与if语句连用
if语法格式:
if 条件
then
逻辑块(通常都是些命令)
fi
如:
#如果4<5,输入出4<5
if test 4 -lt 5
then
echo "4<5"
fi
运行结果:
写成一行:适用于终端写法
If 条件; then 逻辑 ; fi
If else语法格式:
if 条件
then
逻辑块(通常都是些命令)
else
逻辑块
fi
如:
read -p "请输入一个数字:" num
if test $num -gt 100
then
echo "你中奖了"
else
echo "你没有中奖"
fi
运行结果
test命令的关键字也是可以省略的
如:
read -p "请输入一个数字:" num
if [ $num -gt 100 ]
then
echo "你中奖了(省略了test关键字)"
else
echo "你没中奖(省略了test关键字)"
fi
运行结果:
If else-if else语法格式:
if 条件1
then
逻辑块(通常都是些命令)
elif 条件2
then
逻辑块
else
逻辑块
fi
如:
read -p "请输入学生姓名:" name
read -p "请输入成绩:" score
if [ $score -ge 90 ]
then
echo "${name} is A"
elif [ $score -ge 80 ]
then
echo "${name} is B"
elif [ $score -ge 70 ]
then
echo "${name} is C"
else
echo "${name} is D"
fi
运行结果:
①分支结构也可以进行嵌套
②多个条件的联合
-a 逻辑与 当两个条件都成立时,结果为true
-o 逻辑或,两个条件只要有一个成立,结果为真
Case语句会匹配一个值和一个模式,匹配成功,执行相应逻辑块
语法:
case 值 in
模式1)
逻辑块
;;
模式2)
逻辑块
;;
esac
如:
read -p "输入一个选择:" num
case $num in
1)
echo "1"
;;
a)
echo "a"
;;
aa)
echo "aa"
;;
"bb")
echo "bb"
;;
*)
echo "other"
;;
esac
运行结果:
注:没有的选项就会匹配到*)这个选项
格式:
for 变量 in 名字表
do
逻辑
done
写成一行:
for 变量 in 列表;do 逻辑; done;
如:
for day in Sunday Monday Tuesday WednesDay Thursday Friday SaturDay
do
echo $day
done
运行结果:
语法:
select 变量 in 列表
do
逻辑块
done
写成一行:
Select 变量 in 列表;do 逻辑 ; done;
如:
#输出/home/llg目录下的路径名称:
select pathName in `ls /home/llg`
do
echo "路径名称:$pathName"
done
运行结果:
注:这个要使用Ctrl+c退出
语法:
while 条件
do
逻辑块
done
写成一行:
Select 变量 in 列表;do 逻辑 ; done;
如:
#使用while循环输出1到10
num=0
while [ $num -le 9 ]
do
echo $[++num]
done
运行结果:
语法:
while :
do
逻辑块
done
或
while true
Do
逻辑块
done
如:
while true
do
echo "HelloWorld"
done
运行结果:
until循环执行逻辑直到true时停止
语法:
util 条件
do
逻辑块
done
如:
#输出1到10
#!/bin/bash
num=0
until [ $num -gt 9 ]
do
let “num++”
echo $num
done
运行结果:
①如果想要提前结束循环(即想跳出循环)
使用关键字
break
如:
#循环输出1到10,当输出到五的时候跳出循环
for i in {1..10}
do
echo $i
if [ $i -eq 5 ]
then
break
fi
done
运行结果:
②如果想结束当次循环,进行下一次循环
使用关键字
continue
如:
#输出1到10中的偶数
for i in {1..10}
do
if [ $[i % 2] -eq 1 ]
then
continue
fi
echo "i:${i}"
done
运行结果:
作用:参数左移,每执行一次,参数序列顺次左移一个位置,$#的值少一个,
多数用于分别处理每一个参数,移出去的参数不再可用
如:
#使用位置参数显示1~7
while [ $# -ne 0 ]
do
echo "输入参数:$1"
shift
done
运行结果:
在shell脚本中,我们也可以定义函数,
封装逻辑,进行调用
语法:
[function] functionName()
{
逻辑
}
调用时,不带()
说明:
1、关键字function,可以忽略不写
如:
demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"
运行结果:
(1)加: 返回值只能是0~255的数字
(2)不加: 最后一条命令结果作为返回值
如:
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
运行结果:
注意:
①函数内的变量,都是全局变量(整个脚本都可以使用)
②函数的参数:调用函数时,可以向函数中传递参数,必须通过$n的形式来获取参数的值
如:
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
运行结果:
另外
变量名称 | 意义 |
$* | 获取所有参数,合并成一个字符串 |
$# | 参数个数 |
$$ | 当前进程的ID |
$! | 后台运行的最后一个进程的ID |
$? | 显示最后一个命令的退出状态。0表示没有错误 |
通常情况下:系统命令会从你的终端接收输入数据,再将需要输出的信息发送给你的终端。
有时:我们可能会需要指定 输入的位置,或者指定输出的位置,这就是”重定向”的概念
命令 | 说明 |
command > file | 将输出重定向到file |
command < file | 将输入重定向到file |
command >> file | 将输出以追加的方式重定向到file |
①输出重定向会覆盖文件内容,如:
$ echo "HelloWorld" > users
$ cat users
运行结果:
②如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,如:
$ echo " HelloWorld" >> users
$ cat users
运行结果:
③输入从定向,如:
read name < ./users
echo $name
运行结果:
概念:
Shell脚本内部也可以引用其他外部脚本这样可以很方便的封装一些公用的代码作为一个独立的文件
Shell脚本引用外部脚本语法如下:
. fileName # .与文件之间一定要有空格
或者
source fileName
如:
创建两个 shell 脚本文件。
test1.sh 代码如下:
#!/bin/bash
name=”HelloWorld”
test2.sh 代码如下:
#!/bin/bash
#使用 . 号来引用test1.sh 文件. ./test1.sh
# 或者使用以下包含文件代码# source ./test1.sh
source ./test1.sh
echo "大声说::$name
运行结果:
①sh -x script
这将执行该脚本并显示所有变量的值
如:
sh -x test2.sh
运行结果:
②sh -n script
不执行脚本只是检查语法的模式,将返回所有语法错误
如:source ./test1.sh
echo "大声说::$name
我们少些了双引号的另一边
运行结果:
awk -F 域分隔符 ‘命令’
如:
①检测系统中UID为0的用户
awk –F: ‘$3==0 {print $1}’ /etc/passwd
运行结果:
②检测系统中密码为空的用户
awk –F: ‘length($2)==0 {print $1}’ /etc/shadow
因为我们有密码为空的用户所以输出结果为空