1. shell的概述

shell 是一种脚本语言

脚本:本质是一个文件,文件里面存放的是特定格式的命令,系统可以使用脚本解析器翻译或解析指令并执行(它不需要编译)

shell 既是应用程序 又是一种脚本语言(应用程序解析脚本语言)

shell 命令解析器:

系统提供shell命令解析器:sh ash bash 查看自己linux系统的默认解析:echo $SHELL

alt

shell脚本是一种脚本语言,我们只需使用任意文本编辑器,按照语法编写相应程序,增加可执行权限,即可在安装shell命令解释器的环境下执行

2. 脚本的调用形式

  • 打开终端时系统自动调用:/etc/profile~/.bashrc
  1. /etc/profile

此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行,系统的公共环境变量在这里设置

开机自启动的程序,一般也在这里设置

  1. ~/.bashrc

用户自己的家目录中的.bashrc

登录时会自动调用,打开任意终端时也会自动调用

这个文件一般设置与个人用户有关的环境变量,如交叉编译器的路径等等

用户手动调用:用户实现的脚本

alt

3. shell语法初识

3.1 定义开头:#!/bin/bash

#!用来声明脚本由什么shell解释,否则使用默认shell

ls /bin | grep sh

echo $SHELL

3.2 单个"#"号代表注释当前行

  • 第一步:编写脚本文件
#!/bin/bash

#注释
echo "hello world!"

alt

  • 第二步:加上可执行权限
chmod +777 01_dzf.sh

alt

  • 第三步:运行
./01_dzf.sh

alt

  • 三种执行方式

./xxx.sh :先按照文件中"#!" 指定的解析器解析

如果"#!" 指定的解析器不存在才会使用系统默认的解析器

bash xxx.sh :指明先用bash解析器解析

如果bash不存在才会使用默认解析器

.xxx.sh :直接使用默认解析器解析(不会执行第一行"#!" 指定的解析器)但是第一行还是要写的

三种执行情况:

打开终端就会有一个解析器,我们称为当前解析器

我们指定解析器的时候(使用 ./xxx.shbash xxx.sh)时会创建一个子shell解析 脚本

alt

注意:Windows下写脚本在Linux下执行注意将Windows文件转换成unix文件

alt

alt

方法一:dos2unix 如果没有该插件 需要安装

sudo apt-get install dos2unix

dos2unix 02_dzf.sh

转换成功就可以执行运行

方法二:需要用vim打开脚本,在最后一行模式下执行:set ff=unix后执行:wq

alt

alt

4. 变量

  • 定义变量 变量名=变量值

如:num=10

  • 引用变量 $变量名

  • 清除变量值 unset 变量名

  • 从键盘获取值 read

在一行上显示和添加提示 需要加上-p

  • 只读变量 readonly
#!/bin/bash

#定义变量
num=100

#打印 引用变量
echo "打印定义变量num:$num"

#清除变量值
unset num

echo "打印清除后的定义变量:$num"

#从键盘获取值
echo "请输入num的值"
read num
echo "num=$num"

read -p "请输入Num的值:" Num
echo "Num=$Num"

#读取多个值
read -p "读取多个值data1 data2:" data1 data2
echo "data1=$data1"
echo "data2=$data2"

#只读变量
readonly onlyNum=10
echo "onlyNum=$onlyNum"
onlyNum=11
echo "onlyNum=$onlyNum"

alt

  • 查看环境变量 env

alt

  • 导出环境变量 作用:(让其他shell脚本识别该变量,设为全局变量)

  • source命令用法: source FileName

作用:在当前bash环境下读取并执行FileName中的命令。

注:该命令通常用命令“.”来替代。

如:source .bash_rc 与 . .bash_rc 是等效的。

注意:source命令与shell scripts的区别是,

source在当前bash环境下执行命令,而scripts是启动一个子shell来执行命令。这样如果把设置环境变量(或alias等等)的命令写进scripts中,就只会影响子shell,无法改变当前的BASH,所以通过文件(命令列)设置环境变量时,要用source 命令。

#!/bin/bash

#设置环境变量
export DATA=250

source命令使文件生效

使用 env可以查看到环境变量中已经有 DATA

alt

可以在终端直接中读取:

echo $DATA

alt

在其他sh脚本读取

#!/bin/bash
echo "环境变量中的DATA=$DATA"

alt

注意事项:

  1. 变量名只能包含英文字母下划线,不能以数字开头

  2. 等号两边不能有空格符,若变量中本身就包含了空格,则整个字符串都要用双引号、或单引号括起来

  3. 双引号和单引号的区别

双引号:可以解析变量的值

单引号:不能解析变量的值

#!/bin/bash

num=200

#$num当成变量的值处理
echo "num=$num"

#$num当成字符串处理
echo 'num=$num'

alt

如果想在PATH变量中 追加一个路径写法如下:(重要!!!!)

export PATH=$PATH:/需要添加的路径

5. 预设变量

5.1 shell 直接提供无需定义的变量

名称 解释
$# 传给shell脚本参数的数量
$* 传给shell脚本参数的内容
$1、$2...$9 运行脚本时传递给其的参数,用空格隔开
$? 命令执行后返回的状态
"$?" 用于检查上一个命令执行是否正确(在Linux中,命令退出状态为0表示该命令正确执行,任何非0值表示命令出错)
$0 当前执行的进程名
$$ 当前进程的进程号
"$$" 变量最常见的用途是用作临时文件的名字以保证临时文件不会重复
#!/bin/bash
echo "参数的个数=$#"
echo "参数的内容=$*"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "第三个参数:$3"
readonly data=10
data=250
echo "data=250的结果:$?"
echo "进程名:$0"
echo "进程号:$$"

alt

5.2 脚本标量的特殊用法

名称 解释
""(双引号) 包含的变量会被解释
''(单引号) 包含的变量会当作字符串解释
``(数字键1左面的反引号) 反引号的内容作为系统命令,并执行其内容,可以替换输出为一个变量
\(转义字符) 同c语言 \n \t \r \a 等 echo 命令需要加-e转义
(命令序列)(小括号) 由子shell来完成,不影响当前shell中的变量
{命令序列}(大括号) 在当前shell中执行,会影响当前变量
#!/bin/bash

#打印当前系统时间
echo "today is `date`"

#查看当前文件夹文件
ls

#加-e转义 才起换行作用
echo "##\n##"
echo -e "##\n##"

#()由子shell完成
echo "()由子shell完成"
data=10
echo "定义data = $data"
( #子shell完成 不会影响当前shell得值
	data=100
    echo "()里面data = $data"
)
echo "当前data=$data"

#{}由当前的shell执行
echo "{}由当前shell完成"
data=10
echo "定义data = $data"
{ #子shell完成 不会影响当前shell得值
	data=100
    echo "{}里面data = $data"
}
echo "当前data=$data"


alt

6. 变量的扩展

6.1 判断变量是否存在

#!/bin/bash
# ${num:-val} 如果num存在,整个表达式的值为num,否则为val

echo ${num:-100} #100
num=200
echo ${num:-100} #200

alt

#!/bin/bash

#${num:=val} 如果num存在,整个表达式的值为num,否则为val,同时将num的值赋值为val
echo ${num:=100} #100
echo "num=$num" #100

alt

6.2 字符串的操作

#!/bin/bash

str="hehe:haha:xixi:lala"
#测量字符串的长度${#str}
echo "str的长度为:${#str}" 

#从下标3的位置提取${str:3}
echo "从下标3的位置提取:${str:3}"

#从下标3的位置提取长度为6字节${str:3:6}
echo "从下标3的位置提取长度为6字节:${str:3:6}"

#${str/old/new} 用new替换str中出现的第一个old
echo "单个字符串替换:${str/:/#}"

#${str//lod/new} 用new替换str中所有的lod
echo "字符串替换:${str//:/#}"

alt

7. 条件测试

  • test命令

用于测试字符串、文件状态和数字

test命令有两种格式:test condition[condition]

使用方括号时,要注意在条件两边加上空格

7.1 文件测试

名称 解释
-e 如果文件存在则为真
-d 如果文件存在且为目录则为真
-f 如果文件存在且为普通文件则为真
-r 如果文件存在且可读则为真
-w 如果文件存在且可写则为真
-x 如果文件存在且可执行则为真
-L 符号链接
-c 如果文件存在且为字符型特殊文件则为真
-b 如果文件存在且为块特殊文件则为真
-s 如果文件存在且至少有一个字符则为真(文件非空)
#!/bin/bash

read -p "请输入一个文件名:" fileName
#第一种命令格式
test -e $fileName
echo "第一种命令格式结果:$?"

#第二种命令格式
[ -e $fileName ]
echo "第二种命令格式结果:$?"

alt

7.2 字符串测试

语法

test str_operator "str"
test "str2" str_operator "str2"
[ str_operator "str" ]
[ "str1" str_operator "str2" ]
str_operator 解释
= 两个字符串相等
!= 两个字符串不相等
-z 空串
-n 非空串
#!/bin/bash
test -z $yn
echo "yn是否为空:$?"

read -p "please input y/n:" yn
[ -z $yn ]
echo "输入的是否为空:$?"

[ $yn = "y" ]
echo "输入的是否为y:$?"

read -p "请输入第一个字符串:" str1
read -p "请输入第二个字符串:" str2
# test $str1 = $str2
[ $str1 = $str2 ]
echo "两次输入是否相同:$?"

alt

7.3 数值测试

test num1 num_operator num2
[ num1 num_operator num2 ]
num_operator 解释
-eq 数值相等
-ne 数值不相等
-gt 数1大于数2
-ge 数1大于等于数2
-le 数1小于等于数2
-lt 数1小于数2
#!/bin/bash

read -p "请输入第一个数值:" data1
read -p "请输入第二个数值:" data2

test $data1 -eq $data2
echo "相等:$?"

test $data1 -ge $data2
echo "大于等于:$?"

[ $data1 -lt $data2 ]
echo "小于:$?"

alt

7.4 符合语句测试

  • &&

command1 && command2

command1执行成功shell才执行command2

  • ||

command1 || command2

command1执行失败shell才执行command2

#!/bin/bash

#根目录下有home文件且是个目录则打印true
test -e /home && test -d /home && echo "true"

#2小于3且5大于3则打印equal
test 2 -lt 3 && test 5 -gt 3 && echo "equal"

#aaa与aaa不相同则打印not equal 相同则打印equal
test "aaa" = "aaa" || echo "not equal" && echo "equal"

alt

  • 多重条件判定
名称 解释
-a (and)test -r file -a -x filefile同时具有r与x权限时才为true
-o (or)test -r file -o -x filefile具有r或x权限时就为true
! test ! -x filefile不具有x权限时为true

8. 控制语句

8.1 if

  • 语法
#格式一
if [条件一]; then
	执行第一段程序
else
	执行第二段程序
fi

#格式二
if [条件一]; then
	执行第一段程序
elif [条件二]; then
	执行第二段程序
else
	执行第三段程序
fi
  • 案例1
#!/bin/bash
read -p "输入y继续:" yn
if [ $yn = "y" ]; then
	echo "继续执行"
else
	echo "停止执行"
fi

alt

  • 案例2
#!/bin/bash

read -p "请输入文件夹的名字:" dirName

#判断文件夹是否存在
if [ -d $dirName ]; then
	echo "$dirName文件夹存在,进入文件夹"
    cd $dirName
    echo "创建test.c文件"
    touch test.c
else #不存在
	echo "$dirName文件夹不存在,创建文件夹"
    mkdir $dirName
    echo "进入$dirName文件夹"
    cd $dirName
    echo "创建test.c文件"
    touch test.c
fi

判断当前路径下有没有文件夹 有就进入创建文件 没有 就创建文件夹 再进入创建文件

alt

  • 案例3
#!/bin/bash

read -p "请输入文件夹的名字:" dirName

#判断文件夹是否存在
if [ -d $dirName ]; then
	echo "$dirName 文件夹存在,进入文件夹"
    cd $dirName
    if [ -e test.c ]; then
    	echo "test.c文件已存在,安装失败"
    else
    	echo "创建test.c文件"
        touch test.c
        if [ $? -eq 0 ]; then
        	echo "安装成功"
        else
        	echo "安装失败"
        fi
    fi
else #不存在
	echo "$dirName文件夹不存在,创建文件夹"
    mkdir $dirName
    echo "进入$dirName文件夹"
    cd $dirName 
    if [ -e test.c ]; then
    	echo "test.c文件已存在,安装失败"
    else
    	echo "创建test.c文件"
        touch test.c
        if [ $? -eq 0 ]; then
        	echo "安装成功"
        else
        	echo "安装失败"
        fi
    fi
fi

alt

8.2 case

  • 语法
case $变量名称 in
	"第一个变量内容")
    	程序段一
        ;; #break
    "第二个变量内容")
    	程序段一
        ;;
    *) #default
    	其他程序段
        exit 1
esac
  • 案例
#!/bin/bash

read -p "请输入yes/no:" choice
case $choice in
	yes | y* |Y*)
    	echo "输入了yes"
        ;; #break
    no | n* | N*)
    	echo "输入了no"
        ;;
    *) #default
    	echo "输入了其他"
        exit 1 #或;;
esac

alt

8.3 for

  • 语法
#形式一
for (( 初始值; 限制值; 执行步阶 ))
	do
    	程序段
    done

初始值:变量在循环中的起始值

限制值:当变量值在这个限制范围内时,就能继续进行循环

执行步阶:每一次循环时,变量的变化量



#形式二
for var in con1 con2 con3 ...
	do
    	程序段
    done

第一次循环时,$var 的内容为con1

第二次循环时,$var 的内容为con2

第三次循环时,$var 的内容为con3

....

declare 是bash的一个内建命令,可以用来声明shell变量、设置变量的属性。declare也可以写作typeset

declare -i s 代表强制把s变量当做int型参数运算。

  • 案例1
#!/bin/bash
#使用declare定义int类型
declare -i sum=0
declare -i i=0
for (( i=0; i<=100; i++ ))
do
	sum=$sum+$i;
done
echo "sum=$sum"

alt

  • 案例2
#!/bin/bash
#使用declare定义int类型
declare -i sum=0
declare -i i=0
for i in 1 2 3 4 5 6 7 8 9 10
do
	sum=$sum+$i;
done
echo "sum=$sum"

alt

  • 案例3:扫描当前文件
#!/bin/bash
for fileName in `ls`
do
	if [ -d $fileName ]; then
    	echo "$fileName是文件夹"
    elif [ -f $fileName ]; then
    	echo "$fileName是普通文件"
    fi
done

alt

8.4 while

  • 语法
while [ condition ]
do
	程序段
done

当 condition 成立的时候进入 while 循环,直到condition不成立时才退出循环。

  • 案例
#!/bin/bash

declare -i i
declare -i s
while [ "$i" != "101" ]
do
	s+=i;
    i=i+1;
done
echo "The count is $s"

alt

8.5 until

  • 语法
until [ condition ]
do
	程序段
done

这种方式与while恰恰相反,当condition成立的时候退出循环,否则继续循环。

  • 案例
#!/bin/bash
declare -i i
declare -i s
until [ "$i" = "101" ]
do
	s+=i;
    i=i+1;
done
echo "The count is $s"

alt

8.6 break continue

  • break

break 命令允许跳出循环。

break 通常在进行一些处理后退出循环或case语句

  • continue

continue 命令类似于break命令

只有一点重要差别,它不会跳出循环,只是跳过这个循环步

9. 函数

  • 语法

定义函数的两种格式:

#格式一:
函数名 () {
	命令 ...
}

#格式二:
function 函数名 () {
	命令 ...
}

所有函数在使用前必须定义,必须将函数放在脚本开始部分,直至shell解析器首次发现它时,才可以使用

调用函数的格式为:函数名 param1 param2 ...

使用参数同在一般脚本中使用特殊变量$1,$2,$3...$9一样

函数可以使用return提前结束并带回返回值

return 从函数中返回,用最后状态命令决定返回值。

return 0 无错误返回

return 1 有错误返回

  • 案例:求最值
#!/bin/bash
#函数定义
function my_max () {
	if [ $1 -gt $2 ]; then
    	return $1
    else
    	return $2
    fi
}

read -p "请输入数值1:" data1
read -p "请输入数值2:" data2
#函数调用
my_max $data1 $data2
echo "$data1和$data2的最大值为:$?"

alt

  • 案例:函数分文件

alt

fun.sh

#!/bin/bash
function my_max () {
	if [ $1 -gt $2 ]; then
    	return $1;
    else
    	return $2;
    fi
}

15_dzf.sh

#!/bin/bash

#导入函数
source fun.sh

read -p "请输入数值1:" data1
read -p "请输入数值2:" data2
#函数调用
my_max $data1 $data2
echo "$data1和$data2的最大值为:$?"

alt