1,创建shell脚本
新建文件test.sh,其中sh是拓展名,不影响脚本执行,只是为了见名知意,比如用php写的脚本,拓展名可以用php。test.sh内容如下:
#!/bin/bash
echo "bash shell"
#!是一个约定的标记,告诉系统这个脚本需要用什么解释器来执行,即使用哪一种shell。
2,运行shell脚本
1、 作为可执行程序
chmod +x ./test.sh #使脚本具有执行权限
./test.sh #执行脚本
一定要写成./test.sh,而不是test.sh,运行其他二进制的程序也一样。直接写test.sh,Linux系统会去PATH里寻找有没有叫test.sh的,而只有/bin,/sbin,/usr/bin,/usr,/sbin等在PATH里面,你的当前目录通常不在PATH里面,所以test.sh会找不到命令,要用./test.sh告诉系统,就在当前目录找。
运行结果如下:
2,作为解释器参数
这种方式是直接运行解释器,其参数就是shell脚本的文件名,如:
/bin/sh test.sh
sh test.sh
结果如上。这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用,
3,Shell变量
(1)显示给变量赋值
- 命名只能使用数字,英文字母,下划线
- 中间不能有空格,可以使用下划线
- 不能使用标点符号
- 不能使用bash里的关键字
#!/bin/bash
name="hello"
echo $name #使用一个定义过的变量,只要在变量前面加美元符号即可
echo ${name} #变量的花括号可以选择,方便解释器识别边界
(2)使用语句给变量赋值
(3)只读变量
使用readonly命令可以将变量定义为只读变量,只读变量的值不能被改变
#!/bin/bash
name="hello"
readonly name
name="hi"
echo $name
运行结果如下,name的值依然是hello:
(4)删除变量
unset可以删除变量,但是不能删除只读变量。变量删除以后不能再使用。
#!/bin/bash
name="hello"
unset name
echo ${name}
运行结果如下:
(5)变量类型
- 局部变量:局部变量在脚本或者命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
- 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
- shell变量:shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量。有一部分是局部变量,这些变量保证了shell的正常运行。
(6)shell字符串
1、单引号
- 单引号里的任何字符都会原样输出,单引号字符串的变量是无效的
- 单引号字符串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但是可以成对出现,作为字符串拼接使用。
#!/bin/bash
name='java'
str='hello ${name}'
echo ${str}
输出如下:
2、双引号
- 双引号里可以有变量
- 双引号里可以出现转移字符(对单引号转义’似乎有问题)
#!/bin/bash
name="java"
str="hello \"${name}\""
echo ${str}
运行结果如下:
3、获取字符串长度,截取字串,查找字串
#!/bin/bash
name="hellojava"
echo ${#name} #获取字符串长度
echo ${name:1:2} #获取字串,从第2个字符开始截取2个字符
echo `expr index ${name} lo` #查找子字符串,查找l或者o的位置,哪个先出现就计算哪个
运行结果如下:
(7)shell数组
shell支持一维数组。用括号来表示数组,用空格分开。也可以单独定义数组的各个分量。
读取数组的一般格式是${数组名[索引]},如果索引是@,可以获取数组中的所有元素。
获取数组长度的方式和获取字符串长度的方式相同。
#!/bin/bash
arr1=(hello java hi cpp)
arr2[0]=hello
arr2[1]=java
arr2[2]=hi
arr2[3]=cpp
echo ${arr1[@]}
echo ${arr1[0]}
echo ${arr2[@]}
echo ${arr2[1]}
echo ${#arr1[0]} #获取数组单个元素的长度
echo ${#arr1[@]} #获取数组元素的个数
运行结果如下:
(8)注释
- 单行注释 #
- 多行注释
:<<EOF
注释内容
EOF
其中EOF也可以使用其他符号,例如
:<<!
注释内容
!
4,shell传递参数
可以在执行shell脚本的时候向脚本传递参数,脚本内获取参数的方式是:$n。n表示一个数字,1代表第一个参数,2表示第二个参数。
#!/bin/bash
echo $# #传递到脚本的参数个数
echo "$*" #以一个单字符串显示所有向脚本传递的参数,在引用中将所有参数作为一个参数
for i in "$*";do
echo $i
done
echo "$@" #与S#相同,但是使用的时候加引号,并在引号中返回每个参数
for i in "$@";do
echo $i
done
echo $$ #脚本运行的当前进程ID
echo $! #后台运行的最后一个进程的ID
echo $- #显示shell使用的当前选项,与set命令相同
echo $? #显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误
运行结果如下:
5,Shell基本运算符
(1)算术运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如awk和expr,expr最常用。expr是一款表达式计算工具,使用它能完成表达式的求值操作。
#!/bin/bash
a=10
b=20
echo "a:${a} b:${b}"
echo `expr ${a} + ${b}`
echo `expr ${a} - ${b}`
echo `expr ${a} \* ${b}`
echo `expr ${a} / ${b}`
echo `expr ${a} % ${b}`
if [ ${a} == ${b} ]
then
echo "a等于b"
fi
if [ ${a} != ${b} ]
then
echo "a不等于b"
fi
a=${b}
echo "a:${a} b:${b}"
运行结果如下:
(2)关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值是数字。
#!/bin/bash
a=10
b=20
echo "a:${a} b:${b}"
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
echo "a大于等于b"
else
echo "a并不大于等于b"
fi
if [ ${a} -le ${b} ] #小于等于
then
echo "a小于等于b"
else
echo "a大于b"
fi
运行结果如下:
(3)布尔运算符
- 与:-a
- 或:-o
- 非:!
(4)逻辑运算符
- 逻辑AND:&&
- 逻辑OR:||
(5)字符串运算符
- = 检测两个字符串是否相等,相等为true
- != 检测两个字符串是否不等,不等为true
- -z 检测字符串长度是否为0,为0返回true
- -n 检测字符串长度是否不为0,不为0返回true
- $ 检测字符串是否为空,不为空返回true
#!/bin/bash
a="abc"
b="efg"
if [ ${a} = ${b} ]
then
echo "a等于b"
else
echo "a不等于b"
fi
if [ ${a} != ${b} ]
then
echo "a不等于b"
else
echo "a等于b"
fi
if [ -z ${a} ]
then
echo "a字符串长度为0"
else
echo "a字符串长度不为0"
fi
if [ -n ${a} ]
then
echo "a字符串长度不为0"
else
echo "a字符串长度为0"
fi
if [ ${a} ]
then
echo "a字符串不为空"
else
echo "a字符串为空"
fi
运行结果如下:
(6)文件测试运算符
文件测试运算符用于检测Unix文件的各种属性
#!/bin/bash
#-b file 检测文件是否是块设备文件,如果是,则返回 true。
#-c file 检测文件是否是字符设备文件,如果是,则返回 true。
#-d file 检测文件是否是目录,如果是,则返回 true。
#-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。
#-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。
#-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。
#-p file 检测文件是否是有名管道,如果是,则返回 true。
#-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。
#-r file 检测文件是否可读,如果是,则返回 true。
#-w file 检测文件是否可写,如果是,则返回 true。
#-x file 检测文件是否可执行,如果是,则返回 true。
#-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。
#-e file 检测文件(包括目录)是否存在,如果是,则返回 true。
#-S file 判断某文件是否 socket。
#-L file 检测文件是否存在并且是一个符号链接。
6,echo命令
- 显示普通字符串
#!/bin/bash
echo "hello java" #字符串可以加双引号
echo hello java #字符串不加双引号也行
运行结果如下:
- 显示转义字符
echo "\"hello java\""
echo \"hello java\"
运行结果如下:
- 显示变量
#!/bin/bash
read name
echo "hello ${name}"
运行结果如下:
- 显示换行
#!/bin/bash
echo -e "OK \n" # -e开启转义
echo "java"
运行结果如下:
- 显示不换行
#!/bin/bash
echo -e "OK \c"
echo java
运行结果如下:
- 显示结果定向至文件
#!/bin/bash
echo "hello java" > java.txt
- 原样输出字符串,不进行转义或者取变量(单引号)
#!/bin/bash
name="hello"
echo '${name}\"'
运行结果如下:
- 显示命令的执行结果
#!/bin/bash
echo `date`
运行结果如下:
7,Shell printf 命令
printf模仿C程序库的printf程序,移植性更好。
#!/bin/bash
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
运行结果如下:
%s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实数,以小数形式输出。
%-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
%-4.2f 指格式化为小数,其中 .2 指保留2位小数。
序列 | 说明 |
---|---|
\a | 警告字符,通常为ASCII的BEL字符 |
\b | 后退 |
\c | 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略 |
\f | 换页 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\\ | 一个字面上的反斜杠字符 |
\ddd | 表示1到3位数八进制值的字符。仅在格式字符串中有效 |
\0ddd | 表示1到3位的八进制值字符 |
8,Shell test 命令
shell中的test命令用于检查某个条件是否成立,它可以进行数值,字符和文件三方面的测试。
数值测试
#!/bin/bash
num1=100
num2=100
if test $[num1] -eq $[num2] #代码中的 [] 执行基本的算数运算,例如:$[num1+num2]
then
echo "两个数相等"
else
echo "两个数不相等"
fi
-eq:等于则为真
-ne:不等于则为真
-gt:大于则为真
-ge:大于等于则为真
-lt:小于则为真
-le:小于等于则为真
字符串测试
#!/bin/bash
num1="java"
num2="Cpp"
if test ${num1} = ${num2}
then
echo "两个字符串相等"
else
echo "两个字符串不相等"
fi
=:等于则为真
!=:不等于则为真
-z:字符串的长度为零则为真
-n:字符串的长度不为零则为真
文件测试
#!/bin/bash
cd /bin
if test -e ./bash
then
echo "文件存在"
else
echo "文件不存在"
fi
-e file 如果文件存在则为真
-r file 如果文件存在且可读则为真
-w file 如果文件存在且可写则为真
-x file 如果文件存在且可执行则为真
-s file 如果文件存在且至少有一个字符则为真
-d file 如果文件存在且为目录则为真
-f file 如果文件存在且为普通文件则为真
-c file 如果文件存在且为字符型特殊文件则为真
-b file 如果文件存在且为块特殊文件则为真
8,Shell 流程控制
if else
#!/bin/bash
a=10
b=20
if [ ${a} == ${b} ]
then
echo "a==b"
elif [ ${a} -gt ${b} ]
then
echo "a>b"
elif [ ${a} -lt ${b} ]
then
echo "a<b"
else
echo "不符合条件"
fi
for 循环
#!/bin/bash
for loop in 1 2 3 4 5
do
echo "The value is: ${loop}"
done
while 循环
#!/bin/bash
int=1
while((${int}<=5))
do
echo ${int}
let "int++"
done
case … esac
和其他语言的switch…case语句类似,是一种多分支选择结构,每个case分支用右圆括号开始,用;;表示break。
#!/bin/bash
read num
case ${num} in
1) echo "你选择了1"
;;
2) echo "你选择了2"
;;
3) echo "你选择了3"
;;
4) echo "你选择了4"
;;
*) echo "你没有输入1-4之间的数组"
;;
esac
运行结果如下:
跳出循环
break
#!/bin/bash
while :
do
read num
case $num in
1|2|3|4|5) echo "你输入的数字是:$num"
;;
*) echo "你输入的数字不在1-5之间,游戏结束"
break
;;
esac
done
运行结果如下:
continue
continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。
#!/bin/bash
while :
do
read num
case $num in
2|3|4|5) echo "你输入的数字是:$num"
;;
1) echo "退出游戏"
break
;;
*) echo "你输入的数字不在1-5之间,游戏结束"
continue
echo "游戏结束"
;;
esac
done
运行结果如下:
9,Shell 函数
(1)可以带function fun()定义,也可以直接fun()定义,不带任何参数
(2)参数返回,可以显示加return返回,如果不加,将以最后一条命令运行结果,作为返回值。
#!/bin/bash
demoFun(){
echo "第一个函数"
}
echo "=========函数开始执行============"
demoFun
echo"==========函数执行结束============"
结果如下
#!/bin/bash
fun(){
echo "输入第一个参数"
read num1
echo "输入第二个参数"
read num2
# return $(($num1+$num2))
sum=$[ num1 + num2 ]
return $sum
}
fun
echo "两数之和为: $?"
函数返回值在调用该函数后通过 $? 来获得。
#!/bin/bash
fun(){
echo "The first parameter: $1"
echo "The second parameter: $2"
echo "The third parameter: $3"
echo "The total: $#"
}
fun 1 2 3
运行结果:
参数处理 | 说明 |
---|---|
$# | 传递到脚本或函数的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
10,Shell 输入输出重定向
- 输出重定向。
#!/bin/bash
who > who.txt #输出定向到文件中
who >> who.txt #输出追加到文件中
- 输入重定向
#!/bin/bash
wc -l < who.txt
wc -l < who.txt > res.txt
- 重定向深入讲解
标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。
默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。
如果希望 stderr 重定向到 file,可以这样写:
command 2>file或者command 2>>file
如果希望将stdouthe stderr合并后重定向到file,可以这样写:
command > file 2>&1
- Here Document
Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。它的基本形式如下:
command << delimiter
document
delimiter
它的作用就是将document之间的内容作为输入传递给command。
#!/bin/bash
wc -l << EOF
hello
java
cpp
EOF
- /dev/null
如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:
#!/bin/bash
command > /dev/null 2>&1 #屏蔽stdout和stderr
/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。
11,Shell 文件包含
shell也可以包含外部脚本,这样可以很方便的封装一些公用的代码作为一个独立的整体。. filename或者source filename都可以。
test1.sh内容如下:
#!/bin/bash
name="java"
test.sh内容如下:
#!/bin/bash
. test1.sh
#source test1.sh
echo $name