Shell初识:
Shell 是一个用 C 语言编写的程序,既是一种命令语言,又是一种程序设计语言。Shell 脚本(shell script),是一种为 shell 编写的脚本程序。只要有一个能编写代码的文本编辑器和一个能解释执行脚本的解释器就可以了,扩展名为 sh。本文针对的是Bourne Again Shell,通常脚本里第一行为 #!/bin/bash ,(#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序)。
执行脚本文件时,通常为 ./脚本名.sh ,因为你的当前目录通常不在 PATH 里,所以写成 脚本名.sh 是会找不到命令的,要用 ./脚本名.sh 告诉系统就在当前目录找。
Shell 脚本示例如下(向屏幕输出Hello World):
#!/bin/bash
echo "Hello World"
Linux 的 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)。
1.Shell 注释
单行注释:以 # 开头的行就是注释,会被解释器忽略。
多行注释:格式如下:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
多行注释格式 例如:
:<<!
注释内容...
注释内容...
注释内容...
!
2.Shell 变量
2.1 变量规则
变量名定义规则如下:
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
- 等号两侧不能有空格,不能使用标点符号,可以使用下划线 _。
- 不能使用bash里的关键字(可用help命令查看保留关键字)。
定义变量时,变量名不加美元符号($,PHP语言中变量需要);使用定义过的变量,只要在变量名前面加美元符号即可,变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界。
例如:
#定义变量
var_name = "qurry"
#使用变量
echo ${var_name}
echo $var_name
使用 readonly 命令可以将变量定义为只读变量
#!/bin/bash
var_name="hello world"
readonly var_name#此时如果修改var_name值,执行时会报错。NAME: This variable is read only.
使用 unset 命令可以删除变量(readonly声明的静态变量不能unset)
#!/bin/bash
var_name="hello world"unset var_name
echo var_name
#现在屏幕无任何输出
2.2 变量类型
- 局部变量 :在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
- 环境变量 :所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
- shell变量 :shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行。
2.2.1 字符串
字符串可以用单引号,也可以用双引号,也可以不用引号。
my_str= 'hello world'
使用单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的,就是说它无法在字符串中引用其它变量值;
不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
#!/bin/bash
my_str='I am qurry.'
my_str1='I am qurry1.$my_str'
echo $my_str
echo $my_str1
#运行结果如下
# I am qurry.
# I am qurry1.$my_str
字符串使用双引号:双引号里可以有变量;可以出现转义字符。
#!/bin/bash
my_str="I am qurry."
echo $my_str
echo "I 30. \$my_str"
echo "I 30. $my_str"
#运行结果如下
# I am qurry.
# I 30. $my_str
# I 30. I am qurry.
字符串的拼接(注意单引号和双引号的拼接):
#!/bin/bash
my_name="qurry"
# 使用双引号拼接
add_str="hello, "$my_name" !"
add_str1="hello, ${my_name} !"
echo $add_str $add_str1
# 使用单引号拼接
add_str2='hello, '$my_name' !'
add_str3='hello, ${my_name} !'
echo $add_str2 $add_str3
#运行结果如下:
# hello, qurry ! hello, qurry !
# hello, qurry ! hello, ${my_name} !
获取字符串长度:
格式为:${#字符串名}
#!/bin/bash
my_name="qurry"
echo ${#my_name}
#运行结果:输出 5
提取子字符串:
格式为 : ${字符串名:提取位置:提取长度}
#!/bin/bash
my_name="My name is qurry"
echo ${my_name:3:4}
:<<!
提取my_name中第4个字符起的4个字符(第一个字符索引值为0).
运行结果为:
name
!
2.2.2 数组
bash支持一维数组(不支持多维数组),并且没有限定数组的大小,数组元素的下标由 0 开始编号。
Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:
数组名=(值1 值2 ... 值n)
使用 @ 符号可以获取数组中的所有元素
使用 @ 或者 * 符号可以获取数组长度
#!/bin/bash
arr_num=(1 3 5 7 2 4 6 8)
echo ${arr_num[@]}
echo ${#arr_num[*]}
#运行结果如下:
# 1 3 5 7 2 4 6 8
# 8
关联数组:
格式为:declare -A array_name (需要自定义键和值,关联数组的键是唯一的)
也可以先声明一个关联数组,然后再分别设置键和值。
#!/bin/bash
declare -A Arr_num=(["index1"]="index_one" ["index2"]="index_two" ["index3"]="index_three" )
echo ${Arr_num["index1"]}
echo ${Arr_num["index2"]}
echo ${Arr_num["index3"]}
:<<!
结果如下:
index_one
index_two
index_three
!
2.3 变量赋值
${file-my.file.txt} :假如$file没有设定,则使用my.file.txt作传回值。(空值及非空值时不作处理)
${file:-my.file.txt} :假如$file没有设定或为空值,则使用my.file.txt作传回值。(非空值时不作处理)
${file+my.file.txt} :假如$fil设为空值或非空值,均使用my.file.txt作传回值。(没设定时不作处理)
${file:+my.file.txt} :若$file为非空值,则使用my.file.txt作传回值。(没设定及空值时不作处理)
${file=my.file.txt} :若$file没设定,则使用my.file.txt作传回值,同时将$file赋值为my.file.txt 。(空值及非空值时不作处理)
${file:=my.file.txt} :若$file没设定或为空值,则使用my.file.txt作传回值,同时将$file赋值为my.file.txt 。(非空值时不作处理)
${file?my.file.txt} :若$file没设定,则将my.file.txt输出出至STDERR。(空值及非空值时不作处理)
${file:?my.file.txt} :若$file没设定或为空值,则将my.file.txt输出至STDERR。(非空值时不作处理)
3 Shell 传参数
在执行 Shell 脚本时,可以向脚本传递参数。
脚本内获取参数的格式为:
$n。n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……
$0 为执行的文件名(包含文件路径)。当n>=10时,需要使用${n}来获取参数。
特殊字符处理参数:
$# | 传递到脚本的参数个数 |
$* | 以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。 |
$$ | 脚本运行的当前进程ID号 |
$! | 后台运行的最后一个进程的ID号 |
$@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。 |
$- | 显示Shell使用的当前选项,与set命令功能相同。 |
$? | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
# 首先确保该脚本文件可被执行 ,不可执行就使用chmod命令赋予权限。
# ./脚本文件名 参数1 参数2 参数n
./test.sh 1 2 3
#结果如下:
第1个参数为:1
第2个参数为:2
第3个参数为:3
传递参数个数为:3
传递的参数作为一个字符串显示:1 2 3
退出状态:0
#!/bin/bash
echo "第1个参数为:$1"
echo "第2个参数为:$2"
echo "第3个参数为:$3"
echo "传递参数个数为:$#"
echo "传递的参数作为一个字符串显示:$*"
echo "退出状态:$?"
4 Shell 运算符操作
4.1 基本语法
$((运算符))
或者 $[运算式]
或者 expr m + n
expr
运算符间要有空格,如果希望将expr的结果赋给某个变量,使用反引号;
乘号(*)前边必须加反斜杠(\)才能实现乘法运算,其他运算符不用加反斜杠;
条件表达式要放在方括号[ ]之间,并且要有空格。
#!/bin/bash
add_num=`expr 1 + 2`
add_num1=$[1+2]
add_num2=$((1+2))
add_num3=`expr 1 \* 2`
echo $add_num
echo $add_num1
echo $add_num2
echo $add_num3
if [ $add_num1 == $add_num2 ]
then
echo true
else
echo false
fi
if [ $add_num2 != $add_num3 ]
then
echo true
else
echo false
fi
:<<!
运行结果如下:
3
3
3
2
true
true
!
4.2 关系运算符
运算符 | 说明 | 举例 |
---|---|---|
-eq | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] |
-ne | 检测两个数是否不相等,不相等返回 true。 | [ $a -ne $b ] |
-gt | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] |
-lt | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] |
-le | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] |
4.3 布尔运算符
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o | 或运算,有一个表达式为 true 则返回 true。 | [ $a -lt 10 -o $b -gt 20 ] |
-a | 与运算,两个表达式都为 true 才返回 true。 | [ $a -lt 10 -a $b -gt 20 ] |
4.4 逻辑运算符
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑的 AND | [[ $a -lt 10 && $b -gt 20 ]] |
|| | 逻辑的 OR | [[ $a -lt 10 || $b -gt 20 ]] |
4.5 字符串运算符
运算符 | 说明 | 举例 |
---|---|---|
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] |
!= | 检测两个字符串是否不相等,不相等返回 true。 | [ $a != $b ] |
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] |
-n | 检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n "$a" ] |
$ | 检测字符串是否不为空,不为空返回 true。 | [ $a ] |
4.6 文件测试运算符
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ] |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] |
-s file | 检测文件是否为空(文件大小是否大于0),不为空返回 true。 | [ -s $file ] |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] |
#!/bin/bash
file="/home/workspace/test/test.sh"
if [ -s $file ]
then
echo true
else
echo false
fi
# 结果如下:
# true
5 Shell 输出指令
5.1 echo输出
echo 自动添加换行符;
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量;
-e 表示开启转义,\c 表示不换行, \n表示换行;
使用反引号显示命令结果。
#!/bin/bash
read input_str
echo -e "input over\n"
echo -e "$input_str \c"
echo `date`
echo "qurry" > test.txt
cat test.txt
:<<!
hello
input over
hello Wed Sep 7 10:50:04 CST 2022
qurry
!
5.2 printf输出
printf 使用引用文本或空格分隔的参数,可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。
格式为:
printf format-string [arguments...]
format-string: 为格式控制字符串;arguments: 为参数列表。
示例:
printf "%-6s %d %5.2f" 测试 30 123.456
# -6s:指一个宽度为 6 个字符(- 表示左对齐,否则表示右对齐),任何字符都会被显示在 6 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
# 5.2f:该小数总共占5个字符,小数点后保留2位。
6 Shell 流程控制
6.1 if...else...fi
如果 else 分支没有语句执行,就省略else。
如果使用方括号[ ]大于使用 -gt,小于使用 -lt;
使用 ((...)) 作为判断语句,大于和小于可以直接使用 > 和 <。
示例:
if [ $a == $b ]
if (( $a == $b ))
6.1.1 if---else语法格式
if condition then command1 command2 ... commandN else command fi
6.1.2 if---elif---else语法格式
if condition1 then command1 elif condition2 then command2 else commandN fi
6.2 for循环语句
语法格式:
for var in item1 item2 ... itemN do command1 command2 ... commandN done
示例:
for var1 in My name is qurry do echo $var1 done结果如下
My
name
is
qurry
6.3 while循环语句
语法格式:
while condition do command done
示例:
num=0
while(( $num<=5 ))
do
echo $num
let "num++"
done# let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。
死循环可以用:while true 或者 while : 或者 for (( ; ; )) 。
使用break可以跳出循环,使用continue命令跳出当前循环。
6.4 case---esac
一种多分支选择结构,每个 case 分支用右圆括号开始,用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句,esac(就是 case 反过来)作为结束标记。
可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
语法格式:
case 值 in 模式1) command1 command2 ... commandN ;; 模式2) command1 command2 ... commandN ;; esac
示例:
case $mode1 in
1) echo '模式 1'
;;
2) echo '模式 2'
;;
*) echo '其它模式 '
;;
esac
7 Shell 函数
函数在Shell脚本中仅在当前Shell环境中有效
Shell脚本中变量默认全局有效
将变量限定在函数内部使用local命令
7.1 函数的定义
格式:
[ function ] funname [()]
{
action;
[return int;]
}
① function是shell用于定义函数的关键字,funname 是函数名,action是需要执行的动作,
return int 是函数返回值(0-255之间)。方括号[ ]表示这个可以不写。
② 定义函数时可以不写关键字function(示例:funname(){ });
如果写关键字function,则可以省略函数名后的(),示例:function funname{ }.
③ 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。
7.2 函数的调用
函数调用时,如果不需要传递参数,则直接写函数名;
如果需要传递参数,则函数名与多个参数之间要用空格隔开;
两种方式函数名后面都不需要加括号()。
传参:funname 参数1 参数2 参数3 ...
不传参:funname
#!/bin/bash
function fun1 {
echo "this is fun1. no parm"
}
fun2() {
echo "this is fun2"
echo "\$1:$1"
echo "\$2:$2"
echo "$#"
}
echo "\$0:$0"
fun1
fun2 hello qurry
:<< !
运行结果如下:
$0:./test.sh
this is fun1. no parm
this is fun2
$1:hello
$2:qurry
2
!
8 Shell 重定向
标准输入(stdin),标准输出(stdout)和标准错误输出(stderr),分别用文件标识符0,1,2来标识。
命令 | 说明 |
---|---|
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command >> file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出文件 m 和 n 合并。 |
n <& m | 将输入文件 m 和 n 合并。 |
<< tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
8.1 输出重定向
格式:command > file
输出重定向会覆盖文件内容,如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾。
命令:ls
输出:file test.sh
命令:ls > file
命令:cat file
输出:file
输出:test.sh
8.2 输入重定向
格式:command1 < file1
通常是从键盘获取输入值的,输入重定向即是从指定文件中获取值。