shell脚本
shell脚本的一些注意事项
Shell 编程跟 JavaScript、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。
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)
- ……
在一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh
,它同样也可以改为 #!/bin/bash
。`#!`` 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序。
一、变量
1.1 变量名和等号之间不能有空格
your_name="google"
1.2 变量名的命名规则
- 只包含字母、数字和下划线: 变量名可以包含字母(大小写敏感)、数字和下划线 _,不能包含其他特殊字符。
- 不能以数字开头: 变量名不能以数字开头,但可以包含数字。
- 避免使用 Shell 关键字: 不要使用Shell的关键字(例如 if、then、else、fi、for、while 等)作为变量名,以免引起混淆。
- 使用大写字母表示常量: 习惯上,常量的变量名通常使用大写字母,例如 PI=3.14。
- 避免使用特殊符号: 尽量避免在变量名中使用特殊符号,因为它们可能与 Shell 的语法产生冲突。
- 避免使用空格: 变量名中不应该包含空格,因为空格通常用于分隔命令和参数。
1.3 使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可,变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,推荐给所有变量加上花括号
for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done
1.4 特殊变量
1.4.1 元字符
=
: 赋值变量
$
: 变量值替换,${变量名}替换为shell变量名的值
>
: prog > file 将标准输出重定向到文件
>>
:prog >> file 将标准输出追加到文件
>&
:file1 >& file2 将输出文件file1和file2合并
<&
:file1 <& file2 将输入文件file2和file1合并
<
:prog < file 从文件中获取标准输入
|
: 管道命令,p1 | p2 将p1的标准输出作为p2的标准输入
&
: 后台运行命令,无需等待命令执行结束就可以在同一命令行下继续输入命令
()
:在子shell中执行命令
{}
:在当前shell中执行命令,或者用在变量的界定范围({变量名})
;
: 命令结束符,p1;p2表示先执行p1再执行p2
&&
:前一个命令执行成功后才能继续执行下一个命令,p1&&p2若p1执行成功后才能执行p2,反之不执行p2
||
:前一个命令执行失败后才能继续执行下一个命令,p1||p2若p1执行失败后才能执行p2,反之不执行p2
!
: 执行历史记录中的命令
~
: home目录
1.4.2 正则匹配字符
+
: 重复一个或者一个以上的前一个字符
?
: 重复零个或者一个的前一个字符
|
: 使用或者(or)方式找出多个字符
()
:查找“组”字符串
()+
:辨别多个重复的组
1.4.3 转义字符$
$#
: 传给脚本的参数个数
$0
: 脚本本身的名字
$1
: 传递给该shell脚本的第一个参数
$2
: 传递给该shell脚本的第二个参数
… :
$@
: 传给脚本的所有参数列表
$*
: 以一个单字符串显示所有向脚本传递的参数
$$
: 脚本运行当前进程ID号
$!
: 后台运行的最后一个进程的ID
$?
: 显示最后命令的退出状态,0表示没有错误,其他表示有错误
$*
与 $@
区别:
#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done
echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done
:"
$ chmod +x test.sh
$ ./test.sh 1 2 3
-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3
"
1.5 字符串操作
字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。
- 单引号字符串的限制:
- 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
- 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。
- 双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
1.5.1 拼接字符串
your_name="google"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1 # hello, google ! hello, google !
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2 $greeting_3 # hello, google ! hello, google !
1.5.2 ${#string}
获取字符串长度
string="abcd"
echo ${#string} # 输出 4
# 变量为字符串时,${#string} 等价于 ${#string[0]}:
string="abcd"
echo ${#string[0]} # 输出 4
1.5.3 提取子字符
string="google is a great site"
echo ${string:1:4} # 输出 oogl
1.5.4 查找子字符
# 查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):
string="google is a great site"
echo `expr index "$string" io` # 输出 1
二、数组
数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小,数组元素的下标由 0 开始,Shell 数组用括号来表示,元素用"空格"符号分割开。
2.1 创建数组
array_name=(value1 value2 ... valuen)
# 也可以用数字下标定义数组
array_name[0]=value0
array_name[1]=value1
array_name[2]=value2
2.2 读取数组
${array_name[index]}
#!/bin/bash
my_array=(A B "C" D)
echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"
:"
$ chmod +x test.sh
$ ./test.sh
第一个元素为: A
第二个元素为: B
第三个元素为: C
第四个元素为: D
"
2.3 关联数组
Bash 支持关联数组,可以使用任意的字符串、或者整数作为下标来访问数组元素。关联数组使用 declare 命令来声明,语法格式如下:
declare -A array_name
-A 选项就是用于声明一个关联数组,关联数组的键是唯一的。
访问关联数组元素可以使用指定的键,格式如下:
array_name["index"]
#!/bin/bash
# 创建一个关联数组 site,并创建不同的键值
declare -A site=(["google"]="www.google.com" ["taobao"]="www.taobao.com")
# 也可以先声明一个关联数组,然后再设置键和值
declare -A site
site["google"]="www.google.com"
site["taobao"]="www.taobao.com"
echo ${site["google"]} # www.google.com
2.4 获取数组中的所有的元素
使用 @
或 *
可以获取数组中的所有元素
使用 !@
或 !*
可以获取数组所有的键
#!/bin/bash
declare -A site
site["google"]="www.google.com"
site["taobao"]="www.taobao.com"
echo "数组的元素为: ${site[*]}"
echo "数组的元素为: ${site[@]}"
:"
$ chmod +x test.sh
$ ./test.sh
数组的元素为: www.google.com www.taobao.com
数组的元素为: www.google.com www.taobao.com
"
# 在数组前加一个感叹号 ! 可以获取数组的所有键
echo "数组的键为: ${!site[*]}"
echo "数组的键为: ${!site[@]}"
:"
数组的键为: google taobao
数组的键为: google taobao
"
2.5 获取数组的长度
使用${#数组名[*]}
或${#数组名[@]}
获取数组长度
#!/bin/bash
my_array[0]=A
my_array[1]=B
my_array[2]=C
my_array[3]=D
echo "数组元素个数为: ${#my_array[*]}"
echo "数组元素个数为: ${#my_array[@]}"
:"
$ chmod +x test.sh
$ ./test.sh
数组元素个数为: 4
数组元素个数为: 4
"
三、echo
与 printf
Shell 的 echo 指令用于字符串的输出。命令格式:
echo [Options] [String]
选项:
- -n 不换行输出
- -e 启用反斜线转义解释
- -E 禁用反斜线转义解释(默认)
-e
若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
\a
发出警告声;\b
删除前一个字符;\c
最后不加上换行符号;\f
换行但光标仍旧停留在原来的位置;\n
换行且光标移至行首;\r
光标移至行首,但不换行;\t
插入tab;\v
与\f相同;\\
插入\字符;\nnn
插入nnn(八进制)所代表的ASCII字符;–help
显示帮助–version
显示版本信息
echo -e "OK! \n" # -e 开启转义
echo "It is a test"
:" 输出结果
OK!
It is a test
"
printf 命令模仿 C 程序库(library)里的 printf() 程序。
printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。
printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认的 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n
。
printf命令格式:
- format-string: 为格式控制字符串
- arguments: 为参数列表。
printf format-string [arguments...]
#!/bin/bash
# format-string为双引号
printf "%d %s\n" 1 "abc"
# 单引号与双引号效果一样
printf '%d %s\n' 1 "abc"
# 没有引号也可以输出
printf %s abcdef
# 格式只指定了一个参数,但多出的参数仍然会按照该格式输出,format-string 被重用
printf %s abc def
printf "%s\n" abc def
printf "%s %s %s\n" a b c d e f g h i j
# 如果没有 arguments,那么 %s 用NULL代替,%d 用 0 代替
printf "%s and %d \n"
:" 输出
1 abc
1 abc
abcdefabcdefabc
def
a b c
d e f
g h i
j
and 0
"
printf的转义序列
\a
警告字符,通常为ASCII的BEL字符\b
后退\c
抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参`数- 以及任何留在格式字符串中的字符,都被忽略\f
换页(formfeed)\n
换行\r
回车(Carriage return)\t
水平制表符\v
垂直制表符\\
一个字面上的反斜杠字符\ddd
表示1到3位数八进制值的字符。仅在格式字符串中有效\0ddd
表示1到3位的八进制值字符
printf "a string, no processing:<%s>\n" "A\nB"
# a string, no processing:<A\nB>
printf "a string, no processing:<%b>\n" "A\nB"
# a string, no processing:<A
# B>
printf "a string, no processing \a"
# a string, no processing #不换行
四、test
命令
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。
4.1 数值测试
-eq
等于则为真-ne
不等于则为真-gt
大于则为真-ge
大于等于则为真-lt
小于则为真-le
小于等于则为真
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi
# 两个数相等!
:"代码中的 [] 执行基本的算数运算,如:"
a=5
b=6
result=$[a+b] # 注意等号两边不能有空格
echo "result 为: $result"
# result 为: 11
4.2 字符串测试
=
等于则为真!=
不相等则为真-z 字符串
字符串的长度为零则为真-n 字符串
字符串的长度不为零则为真
num1="glogle"
num2="google"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi
# 两个字符串不相等!
4.3 文件测试
-e 文件名
如果文件存在则为真-r 文件名
如果文件存在且可读则为真-w 文件名
如果文件存在且可写则为真-x 文件名
如果文件存在且可执行则为真-s 文件名
如果文件存在且至少有一个字符则为真-d 文件名
如果文件存在且为目录则为真-f 文件名
如果文件存在且为普通文件则为真-c 文件名
如果文件存在且为字符型特殊文件则为真-b 文件名
如果文件存在且为块特殊文件则为真
cd /bin
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi
# 文件已存在!
:"
Shell 还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,
其优先级为: ! 最高, -a 次之, -o 最低。"
cd /bin
if test -e ./notFile -o -e ./bash
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi
# 至少有一个文件存在!
五、流程控制
# 1. if 语句
if condition
then
command1
command2
...
commandN
fi
# 写成一行
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi
# 2. if else 语句
if condition
then
command1
command2
...
commandN
else
command
fi
# 3. if else-if else 语句
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
:"
if else 的 [...] 判断语句中大于使用 -gt,小于使用 -lt。
如果使用 ((...)) 作为判断语句,大于和小于可以直接使用 > 和 <。
"
if [ "$a" -gt "$b" ]; then
...
fi
if (( a > b )); then
...
fi
# 4. for 语句
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
# 或者
for((i=1;i<=10;i++));
do
echo $(expr $i \* 3 + 1);
done
# 写成一行
for var in item1 item2 ... itemN; do command1; command2… done;
# 5. while 语句
while condition
do
command
done
# 6. 无限循环
while :
do
command
done
# 或者
while true
do
command
done
# 或者
for (( ; ; ))
do
command
done
# 7. until 语句
until condition
do
command
done
# 8. case ... esac
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
# 9. break 跳出所有循环
# 10. continue 跳出当前循环
六、函数
[ function ] funname [()]
{
action;
[return int;]
}
#!/bin/bash
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !" # 函数返回值在调用该函数后通过 $? 来获得。
:"
这个函数会对输入的两个数字进行相加运算...
输入第一个数字:
1
输入第二个数字:
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !
"
函数参数
在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数…
#!/bin/bash
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !" # 注意,$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73
:"
第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !
"
七、shell文件包含外部脚本
Shell 也可以包含外部脚本,这样可以很方便的封装一些公用的代码作为一个独立的文件。
Shell 文件包含的语法格式如下:
. filename # 注意点号(.)和文件名中间有一空格
或
source filename
:"test1.sh"
#!/bin/bash
a="hjhdfhkolfk"
:"test2.sh"
#!/bin/bash
#使用 . 号来引用test1.sh 文件
. ./test1.sh
# 或者使用以下包含文件代码
# source ./test1.sh
echo "$a"
:"
$ chmod +x test2.sh
$ ./test2.sh
hjhdfhkolfk
"