Shell脚本的基础用法介绍

Shell 脚本介绍

  • Shell 一个命令行解释器,它的作用是将输入的命令加以解释并传给系统执行, 是用户与系统沟通的桥梁, 而 Shell 脚本是一种脚本语言, 支持逻辑判断, 循环执行等操作, 可以实现批量处理非常的复杂Shell 命令, 是 Linux 系统高级用法的灵魂
  • Shell 脚本以文件的形式存在,格式为 *.sh ,需要注意文件默认是没有执行权限的, 因此我们需要手动给 shell 脚本执行权限, chmod +x ./test.sh #使脚本具有执行权限./test.sh #执行脚本
    • 我们可以使用三种方式调用 shell 脚本
      • . xx.sh 先按照定义的指定解析器解析,如果找不到使用默认解析
        • 定义指定解析器的格式为在脚本行首添加 #!/bin/bash 或其他解析器
      • bash xxx.sh 指定使用 bash 解析器解析,如果找不到使用默认解析
      • source xx.sh 直接在当前环境中运行的 shell 中运行脚本
        • 如果使用 source *.sh 命令则不需要给脚本文件执行权限
      • 除此之外, 将脚本添加到 /etc/profile~/.bashrc 中会在登录或打开终端时自动调用此脚本

创建一个脚本

  • 脚本的开头为 #!/bin/bash, 如果没有这一行, 会使用默认的解释器,只能写在第一行
    • /bin/sh
    • /bin/bash
    • /usr/bin/sh
    • /usr/bin/bash
  • # 代表单行注释, <<EOF … EOF 或 :<<! … ! 代表多行注释

基础语法

  • Shell 脚本基础写法和概念与常见的编程语言类似, 本文档只解释Shell脚本和其他编程语言语法上的区别, 不解释详细细节,需要一定编程基础才能理解

变量

  • 普通变量: test1=10
  • 只读变量: readonly test2="Hello World" 无法修改变量的值
  • 局部变量: local test3="Hello Linux" 只在函数体内有效
  • 环境变量: export test4="Hello shellScript" 只要不重启电脑就永久有效
    • 想要永久生效, 可以将环境变量编辑进 /etc/profile~/.bashrc
    • 使用 env 命令可以查看当前所有的环境变量
    • 为了规范书写, 建议将定义环境变量时所有字母大写
  • 删除变量: unset test1
  • 使用变量: echo $test2echo {$test2} {}用于区别变量和字符串例如 echo {$test2}NiHao
  • Shell 脚本中存在一些特殊变量
特殊变量列表
变量含义
$0当前脚本的文件名
$n传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$#传递给脚本或函数的参数个数。
$*传递给脚本或函数的所有参数。被双引号 (" ") 包含时,"$\*" 会将所有的参数作为一个整体
$@传递给脚本或函数的所有参数。被双引号 (" ") 包含时,"$@" 会将各个参数分开
$?上个命令的退出状态,或函数的返回值。
$$当前 Shell 进程 ID。对于 Shell 脚本,就是这些脚本所在的进程 ID。
$!后台运行的最后一个进程的 ID

字符串

  • 使用单引号''创建的字符串只能原样输出, 字符串中的变量是无效的, 且不支持转义字符
  • 双引号""里可以有变量, 也可以使用转义字符, 非特殊情况建议使用双引号
    • echo -e 使用转义字符, 否则原样输出
  • 字符串是可以拼接到一起的
    • str01="1""2"
    • str3="hello, "$test2""
    • str03=${test01}${test02}
  • 获取字符串长度
    • echo ${#name}
    • echo $test1 | wc -L
  • 提取字符串
    • MYVAR=foodforthought.jpg
      • ${varible##*string} 从左向右截取最后一个 string 后的字符串
        • echo ${MYVAR##*fo}
          • 结果为 rthought.jpg
      • ${varible#*string} 从左向右截取第一个 string 后的字符串
      • ${varible%%string*} 从右向左截取最后一个 string 后的字符串
      • ${varible%string*} 从右向左截取第一个 string 后的字符串
      • ${varible:n1:n2} 从n1个位置到底n2个位置的字符串
        • $echo ${MYVAR:0:4}
          • 结果为 food
  • 读取用户输入
    • read 命令会将提示符后输入的所有数据分配给单个变量,要么就指定多个变量。输入的每个数据值都会分配给变量列表中的下一个变量。如果变量数量不够,剩下的数据就全部分配给最后一个变量
      • -p 设置提示信息
      • -d 定义结束标志
      • -e 允许使用命令补全功能
      • -a 定义一个数组
      • -n 定义文本长度, 超过长度自动结束
      • -t 定义时间, 超过时间自动结束
      • 示例
        • read -p "Enter your name:" test01
        • echo "$test01"

数组

  • 在 Shell 脚本中, 数组的下标不需要连续, 且没有范围限制
  • 数组名=(元素1 元素2 元素3) 定义数组
  • 数组名[0]=值 给该数组第一个元素赋值
  • 数组名=([0]=值1 [1]=值2 [2]=值3) 给该数组多个元素赋值
  • ${数组名[0]} 引用数组
  • ${数组名[*]} 引用数组中的全部元素, 用 @ 也行
  • ${#数组名[@]} 获取数组长度, 和获取字符串长度的方法一样
  • c=($a{*} $a{*}) 将 a 和 b 的数组元素合并为新的数组 c
  • unset aunset a[3] 删除数组或删除数组中的指定元素

运算符

  • bash 中的运算符依靠 expr 命令实现, 以下表格中的 ab 都是变量

需注意:

条件表达式要放在方括号之间, 表达式和运算符之间要有空格
使用 expr 进行计算时需要使用 ` ` 将表达式包含起来

算数运算符

运算 写法
加法 expr $a + $b
减法 expr $a - $b
乘法 expr $a \* $b
除法 expr $b / $a
取余 expr $b % $a
赋值 a=$b
相等 [ $a == $b ]
不相等 [ $a != $b ]
#!/bin/bash
a=10
b=20

val=`expr $a + $b`
echo "a + b : $val"

关系运算符

  • 只支持数字,不支持字符串,除非字符串的值是数字,字符串有单独的运算符
运算 写法 主要符号
检测两个数是否相等 [ $a -eq $b ] -eq
检测两个数是否不相等 [ $a -ne $b ] -ne
检测左边的数是否大于右边的 [ $a -gt $b ] -gt
检测左边的数是否小于右边的 [ $a -lt $b ] -lt
检测左边的数是否大于等于右边的 [ $a -ge $b ] -ge
检测左边的数是否小于等于右边的 [ $a -le $b ] -le
#!/bin/bash
a=1
b=2

if [ $a != $b ]
then
   echo "$a != $b : a 不等于 b"
else
   echo "$a == $b: a 等于 b"

布尔运算符

  • 返回 false 或 true
运算 写法 主要符号
非运算 [ ! false ] !
或运算 [ $a -lt 20 -o $b -gt 100 ] -o
与运算 [ $a -lt 20 -a $b -gt 100 ] -a

逻辑运算符

  • 逻辑运算符需要双中括号括起来, 布尔运算只需要单括号
  • 此外逻辑运算符具有短路功能, 在 AND 运算中第一个表达式为 false 时则不执行第二个表达式,在 OR 运算中第一个表达式为 true 时不执行第二个表达式。
运算 写法 主要符号
逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] &&
逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] ||

字符串运算符

  • 字符串需要使用专用的运算符, 不要和算数运算符和关系运算符弄混
运算 写法 主要符号
检测两个字符串是否相等 [ $a = $b ] =
检测两个字符串是否不相等 [ $a != $b ] !=
检测字符串长度是否为 0 [ -z $a ] -z
检测字符串长度是否不为 0 [ -n "$a" ] -n
检测字符串是否为空 [ $a ] Dollar符号

文件测试运算符

  • 主要用于检测 Unix 文件的各种属性, 同样返回 true 或 false
运算 写法 主要符号
检测文件是否是块设备文件 [ -b $file ] -b file
检测文件是否是字符设备文件 [ -c $file ] -c file
检测文件是否是目录 [ -d $file ] -d file
检测文件是否是普通文件(既不是目录,也不是设备文件) [ -f $file ] 返回 true -f file
检测文件是否设置了 SGID 位 [ -g $file ] -g file
检测文件是否设置了粘着位 (Sticky Bit) [ -k $file ] -k file
检测文件是否是有名管道 [ -p $file ] -p file
检测文件是否设置了 SUID 位 [ -u $file ] -u file
检测文件是否可读 [ -r $file ] -r file
检测文件是否可写 [ -w $file ] -w file
检测文件是否可执行 [ -x $file ] -x file
检测文件是否为空(文件大小是否大于 0) [ -s $file ] -s file
检测文件(包括目录)是否存在 [ -e $file ] -e file
#!/bin/bash
file="/home/textcpp/test.sh"
if [ -r $file ]
then
   echo "文件可读"
else
   echo "文件不可读"
fi

运算指令

  1. (( ))
    • 直接使用双圆括弧计算其中的内容,如 ((var=a+b)),经常在 if/while 等条件判断中需要计算时使用。
    • ((a==b)) 等价于 [ $a == $b ] 等价于 [ $a -eq $b ]
    • ((a>b)) 等价于 [ $a -gt $b]
  2. let
    • 在计算表达式的时候可以直接使用 let,如 let var=a+b。
  • 3.expr
    • 最常用的计算指令,使用时一定记得在外部增反引号
var=`expr a+b`
  1. bc 计算器
    • bc 计算器支持 shell 中的小数进行运算,并且可以交互式或者非交互式的使用。基本使用方式为 var=$(echo "(1.1+2.1)"|bc)
    • bc 的浮点运算由 scale 变量控制, 仅对除法生效,需要显示浮点数时, 请直接修改 scale 的值 scale=3
  2. $[]
    • 可以直接使用这种方式计算中括弧中的内容,如 echo $[1+2]

选择结构

if 语句

  • if ... fi
if [ $a == $b ]
then
    command1 
    command2
fi
#或者以下写法
if [ $a == $b ]; then
    command1 
    command2
fi
#test命令用于检查条件是否成立,与[]类似,当test后面没有命令时默认为false
if test $a -eq $b 
  • if ... else ... fi
if [ $a == $b ]
then
    command1 
else
	command2
fi
  • if ... elif ... else fi
if [ $a == $b ]
then
    command1 
elif [ $a -gt $b ]
	command2
else
	command3
fi
  • 在 Shell 脚本中,if 需要以 fi 结尾表示结束
  • 如果返回值为 true, 则执行 then 后面的语句, 返回值为 false 时执行 else 后面的语句

case 语句

  • case ... esac
read a
case $a in:
1)
	command1
	command2
	;;
2)
	command3
	command4
	;;
*)
	command4
	;;

esac
  • 基本规则与 C 语言的 switch-case 类似, 匹配到相同值时, 一直运行到结束或遇到 ;; 为止, ;; 等价于 C 语言中的 break, 但 Shell 中的 break 命令并不适用于 case, 只适用于循环
  • 当所有值都不匹配时, 运行*内的命令
  • 同样以倒过来的 esac 结尾表示结束

循环结构

for 语句

  • for 变量 in 列表
for i in 1 2 3 4 5
do
	echo $i
	echo $i
done
#支持C语言风格的写法,需要两个括号  
for((i=1;i<=10;i++))
do
	command1
done
  • 每循环一次, 列表里的值就会依次赋给变量
  • 使用 for 命令遍历目录与文件时, 最好将变量用双引号包围, 例如 '$file', 因为 Linux 允许包含空格的文件名和目录名, 但 shell 脚本会将空格, 换行符,TAB 键视为分隔符
    • 此外,IFS 变量可以用定义分隔符, 例如 IFS=":", 会将冒号视为分隔符

while 语句

  • while 判断条件
a=0
while [ $a -gt 10 ]
do
	a=`expr $a+1`
	if [ $a -eq 6 ]
	then
		continue
	elif [ $a -eq 8 ]
	then
		break
	fi
	echo $a
done
#可以循环读入,按Crtl+D结束输入
while read b
do
	echo "You input $b"
done
	
  • 循环同样是支持 break 和 continue 的, break 3 可以直接跳出三层循环
  • shell 中, 实现无限循环的方法为 for (( ; ; )while :while true

until 语句

  • until 判断条件
a=0
until ((a>10))
do
echo $a
a=`expr $a + 1`
done
  • until 的判断方法与 while 相反,until 只有在返回值为 false 时执行, 当返回值为 true 时停止循环

select-in 语句

  • select 变量 in 列表
echo "What is your favorite OS?"
select var in "Windows" "Linux" "MacOS"
do
case $var in
"Windows")
        echo "by Microsoft"
        break;;
"Linux")
        echo "Open Source System by Linus"
        break;;
"MacOS")
        echo "by Apple"
		break;;
*)
        echo "Error input"
        break;;
esac
done
echo "You have selected : $var"
  • select-in 是 shell 脚本中特有的循环语句, 将列表打印成菜单, 用户可以选择菜单并执行不同的功能
  • PS3 变量是 select-in 的菜单提示符, 默认值为 #?, 可以自行修改

函数

  • function_name(){}function function_name{}
function sub()
{
        res=`expr $1 - $2` # $1 代表传递过来的第一个参数,从10开始需要写成 ${10}  
        echo "减法结果为$res"
}
sum() # 创建函数时的 function 关键字写不写都行 
{  
        res=`expr $1 + $2`
        echo "加法的结果为"
        return $res # 不写return的话,默认将最后一条命令的直接结果作为返回值,默认返回值为0,非0表示执行失败
}
sum 3 3  # 调用函数执行的方式为:    函数名 参数1 参数2 参数3
echo $?  # 调用返回值的方法为 $? ,需要注意的是该命令只能调用上一次执行的命令的返回值  
sub 10 7
echo $?
  • 函数内变量的作用域为全局变量, 在 shell 内的任意位置都有效, 在函数内对外部变量做的修改也会生效, 但传递进去的变量传递的只是该变量的参数, 修改传进去的变量的值不影响参数的值, 因此不要沿用常见编程语言的编写习惯
  • 想让函数内的变量只在函数内生效, 可以加上 local 使其变为局部变量
  • shell 脚本的函数同样是支持嵌套和递归调用的
  • 删除函数: unset .f functionName

重定向

  • 除了标准的输入输出重定向之外,Shell 脚本还支持一些特殊的重定向方法, 例如 Here Document, 它的作用是将两个 delimiter (分隔符) 之间的内容 (document) 作为输入传递给 command, 基本格式是 command << delimiter    内容delimiter
#!/bin/bash
cat << COMMAND
Hello
Nihao
COMMAND

例题讲解

在/usr/bin 目录下创建一个 repwis 脚本, 查找/usr 目录下小于 10M 且组 ID 不为 root 的文件, 把查到的文件结果拷贝到/root/cyfiles 文件内

vim /usr/bin/repwis
#进入vim后编辑以下内容
#!/bin/bash
mkdir /root/cyfiles 
for i in `awk -F":" '{print $3}' /etc/group`  #将group文件的gid信息过滤出来作为列表 
do
	if [ 0 != $i ] #如果gid不是0的话,即gid不为root
	then
		find /usr -size -10M -gid $i -type f -exec cp -a {} /root/cyfiles \; 
		#cp的-a参数一定不能忘记,否则文件的源数据不会保留
	fi
done
#保存退出
chmod +x /usr/bin/repwis
bash /usr/bin/repwis
posted @ 2022-11-18 17:56  RilyLC  阅读(165)  评论(0编辑  收藏  举报