shell脚本语言基础

shell基础理论

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。

Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

shell环境

  • Bourne shell(/user/nbin/sh或/bin/sh)
  • Bourne Again Shell (/bin/bash)
  • C Shell (/usr/bin/csh)
  • K Shell (/usr/bin/ksh)
  • Shell for Root (/sbin/sh)

脚本运行方法

作为可执行程序

  • 编写脚本,使用.sh后缀保存(#!/bin/bash 写在第一行标记解释器形式)

  • 1
    2
    chmod +x ./test.sh  #使脚本具有运行权限
    ./test.sh #运行脚本

使用./说明在当前目录下寻找脚本文件,如果没有./那么会在系统路径中寻找

作为解释器参数

运行解释器,参数为shell脚本名,如

1
/bin/sh test.sh

Shell基础语法汇总

变量

定义变量

变量名不加美元符号(PHP中需要)

1
name="shell"
  • 变量名和等号之间不能有空格
  • 可以用循环语句赋值

使用变量

  • 输出

    1
    2
    echo $name
    echo ${name} #{}界定范围
  • 只读

    1
    2
    name="shell"
    readonly name

    此时name变量不可被修改,运行脚本报错

    1
    /bin/sh: NAME: This variable is read only.

删除变量

1
unset name

变量被删除后不能再次使用,unset命令不能删除只读变量

变量类型

  • 局部变量:当前shell中有效
  • 环境变量:所有程序都可访问
  • shell变量:shell程序设置的变量,部分局部部分环境

字符串

定义

  • 单引号:

    • 字符原样输出,单引号内变量无效;
    • 单引号字符串中不能出现单独的单引号,转义也不行,但是可以在字符串拼接的时候成对使用
  • 双引号

    • 双引号内可以有变量

    • 可以有转义字符

    • 1
      2
      name='shell'
      str="Hello, I know you are \"$name\"! \n"

拼接

  • 双引号

    1
    2
    3
    greeting="hello, "$your_name" !"
    greeting_1="hello, ${your_name} !"
    #两种表示均可以正确输出
  • 单引号

    1
    2
    3
    greeting_2='hello, '$your_name' !'
    greeting_3='hello, ${your_name} !'
    #单引号内变量无效,后一种表示原样输出字符串

长度

1
2
name="shell"
echo ${#name} #输出5

子字符串

  • 截取

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 指定位置截取
    string="shell"
    echo ${string:1:3} # 输出 hel

    #截取子字符串后字符串
    ${varible##*string} #从左向右截取最后一个string后的字符串
    ${varible#*string} #从左向右截取第一个string后的字符串
    ${varible%%string*} #从右向左截取最后一个string后的字符串
    ${varible%string*} #从右向左截取第一个string后的字符串
    #*只是一个通配符可以不要
  • 查找

    1
    2
    string="shell"
    echo `expr index "$string" sa` # 查找字符s或a第一个出现的位置,输出 1,使用反引号

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
array_name=(a b c d)

# ${数组名[下标]}
valuen=${array_name[n]}

# 获取所有元素
echo "数组的元素为: ${array_name[*]}"
echo "数组的元素为: ${array_name[@]}"

# 取得数组元素的个数
length=${#array_name[@]}
length=${#array_name[*]}

# 取得数组单个元素的长度
lengthn=${#array_name[n]}

注释

1
2
3
4
5
6
7
8
9
10
11
#--------------------------------------------
# 这是一个注释
# author:作者名
#--------------------------------------------
##### 用户配置区 开始 #####
#
#
# 这里可以添加脚本描述信息
#
#
##### 用户配置区 结束 #####

传递参数

基础示例

在运行脚本时即向脚本中传递某些参数,这些参数以$n的格式获取,n=0时为文件名,n=1……时为参数

例:向test.sh中传递三个参数1 2 3

脚本如下

1
2
3
4
5
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

命令如下

1
2
$ chmod +x test.sh 
$ ./test.sh 1 2 3

输出如下

1
2
3
4
5
Shell 传递参数实例!
执行的文件名:./test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3

其他特殊字符处理

字符 说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。
如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。相当于一个字符串
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。
如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。相当于n个字符串
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
$$ 脚本运行的当前进程ID号

基本运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2,这与我们熟悉的大多数编程语言不一样。
  • 完整的表达式要被 ` 包含,注意这个字符不是常用的单引号,在 Esc 键下边。

算术运算符

运算符 说明 举例
+ expr $a + $b
- expr $a - $b
* expr $a \* $b
/ expr $a / $b
% 取余 expr $a % $b
= 赋值 a=$b
== 数字相等 [$a == $b]
!= 数字不相等 [$a != $b]

关系运算符

只支持数字,除非字符串中也是数字

运算符 说明 举例
-eq == [ $a -eq $b ]
-ne != [ $a -ne $b ]
-gt > [ $a -gt $b ]
-lt < [ $a -lt $b ]
-ge >= [ $a -ge $b ]
-le <= [ $a -le $b ]

布尔运算符

运算符 说明 举例
! 非,取反 [ ! false ] 返回 true
-o [ $a -lt 20 -o $b -gt 100 ] 返回 true
-a [ $a -lt 20 -a $b -gt 100 ] 返回 false

逻辑运算符

运算符 说明 举例
&& [[ $a -lt 100 && $b -gt 100 ]] 返回 false
\ \ [[ $a -lt 100 \ \ $b -gt 100 ]] 返回 true

字符串运算符

运算符 说明 举例
= 相等 [ $a = $b ]
!= 不相等 [ $a != $b ]
-z 检测长度为0,为0则true [ -z $a ]
-n 检测长度为0,不为0则true [ -n “$a” ]
$ 判断为空 [ $a ]

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性

运算符 说明
-b file [-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 检测文件是否存在并且是一个符号链接

echo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 普通字符串
echo "It is a test"

# 转义字符
echo "\"It is a test\""

# 显示变量
read name # 从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
echo "$name It is a test"

# 显示换行
echo -e "OK! \n" # -e 开启转义
echo "It is a test"

# 显示不换行
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"

# 文件定向
echo "It is a test" > myfile

# 命令执行结果
echo `date`

printf

printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好

1
2
3
4
5
6
7
# printf  format-string  [arguments...]
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
# %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐) 如果不足则自动以空格填充,超过也会将内容全部显示出来
# %-4.2f 指格式化为小数,其中.2指保留2位小数

转义序列

序列 说明
\a 警告字符,通常为ASCII的BEL字符
\b 后退
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f 换页(formfeed)
\n 换行
\r 回车(Carriage return)
\t 水平制表符
\v 垂直制表符
\ 一个字面上的反斜杠字符
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符

test

数值测试

1
2
3
4
5
6
7
8
num1=100
num2=100
if test $[num1] -eq $[num2] # -eq -ne -gt -ge -lt -le
then
echo '两个数相等!'
else
echo '两个数不相等!'
f

字符串测试

1
2
3
4
5
6
7
8
num1="shell1"
num2="shell"
if test $num1 = $num2 # = != -z -n
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi

文件测试

1
2
3
4
5
6
7
cd /bin
if test -e ./bash # -e(exist存在) -r(read可读) -w(write可写) -x(execute可执行) -s(至少一个字符) -d(目录) -f(file) -c(字符型特殊文件) -b(block块特殊文件)
then
echo '文件已存在!'
else
echo '文件不存在!'
fi

条件连接

1
2
3
4
5
6
7
cd /bin
if test -e ./notFile -o -e ./bash # -a -o !
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi

流程控制

if else

1
2
3
4
5
6
7
8
9
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi

for

1
2
3
4
5
6
7
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done

while

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done

# 键盘读入
while read FILM
do
echo "$FILM"
done

for (( ; ; )) # 无限循环

until

1
2
3
4
5
until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done

case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac

#
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;; # 表示break
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 定义
[ function ] funname [()] # [function]可以不写
{
action;
[return int;]
}

# 返回参数用$?获取
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"

# 函数参数
funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !" # $10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

文件包含

. filename 或 source filename

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# test1.sh
#!/bin/bash
language="shell"

# test2.sh
#!/bin/bash
#使用 . 号来引用test1.sh 文件
. ./test1.sh
# 或者使用以下包含文件代码
# source ./test1.sh

echo "当前语言:$language"

# 执行结果
# 当前语言:shell
0%