shell脚本编写简单入门

初试入门

入门 helloword.sh 程序

使用编辑器vim 创建并添加语句

vim helloworld.sh
#! /bin/bash
echo 'helloworld'

#! /bin/bash 是指此脚本使用/bin/bash来解释执行。
echo 输出语句

chmod ug+x helloworld.sh		# 修改文件执行权限
./helloworld.sh			# 执行脚本

执行结果
在这里插入图片描述

运行shell脚本的四种方式

  1. 绝对路径
    权限:
    1、当前用户对沿途的文件卖都有x权限
    2、当的用户对目标文件应该有rx权限

    用法:绝对路径即可

  2. 相对路径
    权限:

    1. 当前用户对沿途的文件夹都有x权限
    2. 当前用户对目标文件应该有rx权限

    用法:必须加上./作为前缀

  3. 加上解释器作为前缀;
    权限:

    1. 当前用户对沿途的文件夹都有x权限
    2. 当前用户对目标文件应该有r权限

    用法:

    • bash 文件路径〔绝对路径、相对路径)
    • sh 文件路径〔绝对路径、相对路径)
  4. 用source命令执行或者在路径前加.空格作为前缀
    权限:

    1. 当前用户对沿途的文件夹都有x权限
    2. 当前用户对目标文件应该有r权限

    用法:

    • source 文件路径 (绝对路径、相对路径)
    • . 文件珞径 (绝对路径、相对路径)

方式1、2、3都是开启一个子bash进程,然后在子进程里运行的shell程序
方式4是直接在当前bash进程里运行的shell程序
进程之间变量不能相互调用,线程是独立的

注释

# 前面加 # 这就是注释
echo 'helloworld'	# 也可以在语句后面加注释
# name=123		#也可以注释掉语句

调试功能

-x 就是调试功能

sh -x /a/b/1.sh

-vx 一箩筐显示功能,+ 号是可调式代码

sh -x /a/b/1.sh

.sh文件里 使用 set -xset +x 部分调试代码

#XXXX
#XXXX
#XXXX
set -x
echo 111
set +x
echo 222
echo 3333
sh -x /a/b/1.sh

在这里插入图片描述

shell编写规范与建议

一、脚本编写规范

  1. 脚本存放目录需要统一
  2. shell脚本的结尾以.sh
  3. 脚本开头要有解释器如#!/bin/bash 或者#!/usr/bin/env bash
  4. 脚本开头注意加时间、作者、联系邮箱、脚本作用等信息
    # Author egon 2020-8-30version 1 des: xXXXX
    
  5. 关键代码应该用#号加上注释

二、代码编写好习惯

  1. 成对的符号尽量一次性写出来,防止遗漏
    例如大括号{},中括号[ ],小括号(),单引号'',双引号"",反引号``等
    
  2. 括号的保留空格习惯
    中括号 [  ] 两端需要留有空格,不然会报错。书写时即可留出空格然后书写内容。
    如果不知道大括号 { } ,中括号 [ ] ,小括号 ( ) ,到底哪种括号需要两端留空格,可以在书写这些括号的时候两端都保留空格来进行书写,这样可以有效避免因空格导致的各种错误。
    
  3. 流程控制语句一次性书写完再添加内容
    例1:if语句格式一次书写完成
    
    if 条件内容
    then
    	条件成立后执行的代码
    fi
    

变量

变量

一、变量介绍

什么是变量?
变:事物的状态是会发生变化的
量:记录事物的状态

变量是一种存取内存的机制

为何要用变量?
是为了让计算机具备人的某项技能

二、变量的使用

  1. 先定义、后引用
    先定义age=18后引用echo $age
  2. 删除变量
    删除变量unset age

补充:字符连接

money=1000
echo ${money}RMB

三、变量的三大组成部分

变量名 赋值号

  • 变量名的命名规范
    • 前提:变量名的命名必须见名知意
    • 规范:
      1. 变量名是由字母数字下划线组成
      2. 不能以数字开头
      3. 不能使用shell的关键字
      4. 不要使用中文的变量名
  • 变量值的三种来源三预定变量
    • 直接赋值
      ip="10.0.0.11"
      age=18
      now_date1=`date "+%F"`
      now_date2=$(date "+%F")
      
    • 从脚本的参数获取,即从命令获取
      $0 是本身
      $1 是第一个参数
      $2 是第二个参数
      ...
      ${10} 10以上就要用{}括起来
      
      创建.sh脚本nginx.sh
      #! /bin/bash
      if [ $1 = "start" ];then
              echo "nginx is starting..."
      elif [ $1 = "stop" ];then
              echo "nginx is stopping"
      elif [ $1 = "restart" ];then
              echo "nginx is restartting"
      else
              echo "Usage: ./nginx.sh start:stop:restart"
      fi
      
      运行
       chmod +x nginx.sh			# 设置权限
       ./nginx.sh stop 				# stop 是传入的变量
      
    • 与用户交互来获取变量值,即通过键盘输入来获取
      read -p "请输入您的账号:" name
      登录案例
      db_name="egon"
      db_pwd="123"
      
      read -p "请输入您的账号:" inp_name
      read -p "请输入您的密码:" inp_pwd
      
      if [ $inp_name = $db_name ] && [ $inp_pwd = $db_pwd ]:then
              echo "登录成功"
      else
              echo "登录失败"
      fi
      
      运行
      chmod +x login.sh
       ./login.sh
      请输入您的账号:egon
      请输入您的密码:123
      登录成功
      

与用户交互

接收用户输入:read命令
把程序的处理结果反馈给用户:
	echo命令格式化输出
		user="egon"
		age=18
		echo "my name is $user my age is $age"
	
	printf命令的格式化输出
		printf "my name is %s, my age is %s\n" "egon" 18			#直接传入
		
		x="egon"
		y=18
		printf "my name is %s, my age is %s\n" $x $y		#变量传入

		%s 字符串、%d整型、%f浮点数
		%f浮点数		" %.2f "显示后两位小数		" %8.2f "显示整数8位,小数2位

预定义变量

$*		# 获取所有的位置参数
$@		# 获取所有的位置参数

$# 		# 获取所有的位置参数个数
$$		# 获取当前shell进程的pid

$?		# 获取上一条命令运行状态,0代表成功  非0代表失败

四、常量

常量:不变的量

readonly m=111		# 使用常量关键字
AGE=18				# 或者使用大写变量名来提示自己这是常量

基本数据类型与值操作

二、基本数据类型
整型:年龄、个数、号码

age=10
level=3

浮点型:薪资、身高、体重

salary=3.3
height=1.81

字符串类型:名字、国籍、名人名言、ip地址等描述性质的内容

msg="hello"
msg='hello'		# 硬引用,包含的字符均无特殊意义

sheil是一门角解释型的、弱类型

x=1
y="100"
expr $x + $y  #结果 101 ,expr 运算

数组类型:

  1. 普通数组
    # 直接赋值
    hobbies=("抽烟" "酒" "烫头")
    echo ${hobbies[0]}		# 结果:抽烟
    echo ${hobbies[1]}		# 结果:酒
    echo ${hobbies[2]}		# 结果:烫头
    
    # 先创建后赋值
    declare -a array1
    array1[0]=12
    array1[1]=34
    array1[2]=56
    echo ${array1[0]}		# 结果:12
    echo ${array1[1]}		# 结果:34
    echo ${array1[2]}		# 结果:56
    
    # 指定下标对应为什么值
    array2=([2]=3.3 [1]=1.1 [3]=4.4)
    echo ${array2[3]}		# 结果:4.4
    echo ${array2[2]}		# 结果:3.3
    echo ${array2[1]}		# 结果:1.1
    echo ${array2[0]}		# 结果:		,因为没有定义
    
  2. 关联数组
    declare -A info=(["name"]="egon" ["age"]=18 ["gender"]="male")
    echo ${info["name"]}		# 结果:egon
    echo ${info["age"]}			# 结果:18
    echo ${info["gender"]}		# 结果:male
    

补充

read -p "xxx: " name
read -p "yyy: " password
echo "$name $password" >> db.txt
# >> 符号是没有这个文件就创建写入文件,有这个文件就追加写入文件里

变量值操作

  1. 获取变量值的长度

    x="hello"
    y=123456
    z=3.12
    
    echo ${
          
          #x}		# 结果:5
    echo ${
          
          #y}		# 结果:6
    echo ${
          
          #z}		# 结果:4
    
    # 其它三种
    echo $x | wc -L					# 结果:5
    expr length $x					# 结果:5
    echo $x | awk '{print length}'	# 结果:5
    
  2. 切片

    msg="hello123"
    echo ${msg:1}		# 结果:ello123	,从第一个开始切
    echo ${msg:1:3}		# 结果:ell	,从第一个开始切,切三个
    
  3. 截断

    url="www.sina.com.cn"
    # 从左开始截断
    echo ${url#www.}				# 结果:sina.com.cn
    echo ${url#*.}					# 结果:sina.com.cn		,截断到第一个点
    echo ${url##*.}					# 结果:cn		,截断到最后第一个点
    
    # 从右开始截断
    echo ${url%.cn}					# 结果:www.sina.com
    echo ${url%.*}					# 结果:www.sina.com		,从最后截断第一个点
    echo ${url%%.*}					# 结果:www		,从最后截断到最后一个点
    
  4. 内容的替换

    url="www.sina.com.cn"
    echo ${url/sina/baidu}			# 结果:www.baidu.com.cn
    echo ${url/c/C}					# 结果:www.baidu.Com.cn
    
    # 替换所有
    echo ${url//c/C}				# 结果:www.baidu.Com.Cn
    
  5. let

    age=18
    
    # 没有用let前
    age=`expr $age + 1`
    echo $age
    
    # 使用 let
    let age++
    echo $age				# 结果:19
    
    # 加值并赋值操作
    x=1
    let y=x++
    echo $x					# 结果:2
    echo $y 				# 结果:1
    
  6. 变量的替代

    # 1.${x-word} 没有定义过就显示默认值
    unset x			# 删除x的值,现在x是没有值的
    echo ${x}		# ,没有定义过显示的是空值
    echo ${x-mei}	# mei,没有定义过就显示 - 号后面的默认值
    
    x=
    echo ${x-mei}	# ,定义过就不显示 - 号后面的默认值了
    
    # 2.${x:-word} 没有定义值和没有值就显示默认值
    unset x			# 删除x的值,现在x是没有值的
    echo ${x:-mei}	# mei,没有定义过就显示 :- 号后面的默认值
    
    x=
    echo ${x:-mei}	# mei,定义过没有值也会显示默认值
    
    # 3.${x:=word} 没有定义值时会把默认值赋值给x
    unset x			# 删除x的值,现在x是没有值的
    echo ${x:=mei}	# mei,没有定义过就显示 := 号后面的默认值并赋值
    echo ${x}		# mei,已经赋值了默认值
    
    echo ${x:=111}	# mei,已经有值就不会在次赋值了
    echo ${x}		# mei,已经有值就不会在次赋值了
    
    # 4.${x:+word}没有定义值时不操作,有值会临时覆盖值
    unset x			# 删除x的值,现在x是没有值的
    echo ${x:+mei}	# ,没值时不操作
    echo ${x}		# ,因为没值时不操作,所以x没值
    
    x=10
    echo ${x:+mei}	# mei,有值时会临时覆盖值,不改变x
    echo ${x}		# 10,上面语句只是临时覆盖值,没有赋值给x
    
    # 5.${x:?word}没有定义值时提示错误
    unset x			# 删除x的值,现在x是没有值的
    echo ${x:?该变量没有值}	# 提示信息:该变量没有值
    
    x=10
    echo ${x:?该变量没有值}	# 10,有值时,就显示它自己的值
    
  7. 取命令的结果赋值给变量;

    # ``与$():`` 命令替换等价于$()反引号中的shell命令会被先执行
    touch `date +%F`_file1.txt
    touch $(date +%F)_file2.txt
    
    disk_free3="df -Ph |grep '/$' |awk '{print $4}'" 	#错误
    disk_free4=$(df -Ph |grep '/$' |awk '{print $4}') 	#正确
    disk_free5=`df -Ph |grep '/$' |awk '{print $4}'` 	#正确
    
    # `` 不支持嵌套
    # $() 支持嵌套
    

元字符

算数运算符

运算符

符号 中文词义
+
-
*
/
% 取余

算数运算符需要配合下述操作使用

# 浮点运算
bc

# 整数运算
expr
$(())
$[]
let

浮点运算

yum install bc -y ^C		# 有就不用安装
echo "scale=2;3/10" | bc	# .3
echo "scale=2;10/3" | bc	# 3.3

# scale是保留多少位,;后面的是运算

整数运算

x=1
y=2

expr $x+$y				# 结果:3
expr $x \* $y			# 结果:2,乘法需要加 \ ,因为 * 有特殊意义

echo $((1+2))			# 结果:3
echo $(( $x + $y ))		# 结果:3

echo $[ $x + $y ]		# 结果:3

let z=x+y
echo $z					# 结果:3

补充:清理内存

# 每次写入都会清除内存
echo 3 > /proc/sys/ vm/ drop caches

测试运算符

测试命令: test,详细可用man test查询
测试符号:0,注意只有一层中括号,中括号内左右两侧必须要有空格
test与 [] 效果都一样,参数也都一样

测试文件状态

  • -d 目录
    test -d /etc		
    echo $?				# 0 , 0是真,就是存在,1是假,不存在
    
    [ -d /etc ]
    echo $?				# 0 , 0是真,就是存在,1是假,不存在
    
  • -s文件长度 >0 、非空
    [ -s /etc/password ]
    echo $?				# 0 , 0是真,就是存在,1是假,不存在
    
    touch a.txt
    [ -s a.txt ]
    echo $?				# 1 , 刚创建的文件长度为0
    
  • -f 是否为标准文件
    [ -f a.txt ]
    echo $?				# 0 , 是标准文件
    
  • -w 是否为可写
    [ -w a.txt ]
    echo $?				# 0 , 可写
    
  • -r 是否为可读
    [ -r a.txt ]
    echo $?				# 0 , 可读
    
  • -x 是否为可执行
    [ -x a.txt ]
    echo $?				# 0 , 可执行
    
  • -L 是否为连接文件
    ln -s a.test b.test
    [ -L b.txt ]
    echo $?				# 0 , 是连接文件
    

字符串

  • -n 字符串长度不为零
    x=""
    y="123abc"
    [ -n "$x" ]
    echo $?			# 1,x的长度为0
    
    [ -n "$y" ]
    echo $?			# 0,y的长度不为0
    
  • -z字符串长度为零
    x=""
    y="123abc"
    [ -z "$x" ]
    echo $?			# 0,x的长度为0
    
    [ -z "$y" ]
    echo $?			# 1,y的长度不为0
    
  • = 两个字符串相等
    x=""
    y="123abc"
    [ "$x" = "$y" ]
    echo $?			# 1,x不等于y
    
  • != 两个字符串不相等
    x=""
    y="123abc"
    [ "$x" != "$y" ]
    echo $?			# 0,x不等于y
    

数字

[ 10 -eq 10 ];echo $?				# 0,-eq等于
[ 10 -gt 10 ];echo $?				# 1,-gt大于
[ 10 -lt 10 ];echo $?				# 1,-lt小于
[ 10 -ge 10 ];echo $?				# 0,-ge大于等于
[ 10 -le 10 ];echo $?				# 0,-le小于等于
[ 10 -ne 10 ];echo $?				# 1,-ne不等于

[ 10 -gt 3 ] && [ 3 -le 3 ]			# 下面是效果就是这个
[ 10 -gt 3 -a 3 -le 3 ];echo $?		# 0,-a并且

[ 10 -gt 30 ] || [ 3 -le 3 ];echo $?# 下面是效果就是这个
[ 10 -gt 30 -o 3 -le 3 ];echo $?	# 0,-o或者

[ 10 -gt 30 ];[ 3 -le 3 ];echo $?	# ;就是一定执行后面的语句

[ ! 10 -eq 10 ];echo $?				# 1,非运算,取反运算

补充:符号优先级
!>&&>||
多个符号,先算完,再算完&&,才算||

赋值运算符

  • +=
    age=18
    let age+=10
    echo $age			# 结果:28
    
  • -=
    age=18
    let age-=10
    echo $age			# 结果:8
    
  • *=
    age=18
    let age*=10
    echo $age			# 结果:180
    
  • /=
    age=18
    let age/=10
    echo $age			# 结果:1,小数点被去除了
    
  • %=
    age=18
    let age%=10
    echo $age			# 结果:8,18%10 的值为8
    

补充[[ ]]

[[ ]] 和 [ ]基本一样,不同的是[[ ]]支持正则表达式

#!/bin/bash
[ $# -ne 1 ] && echo "只能传入一个参数" && exit

# 只能是数字
[[ ! $1 =~ ^[0-9]+$ ]] && echo "只能传入数字" && exit

echo "ko ----> $1"

案例-比较两个数字(浮点数也是数字)

第一种方法:使用正则表达式

#! /bin/bash
[ $# -ne 2 ] && echo "要传入两个参数" && exit
[[ ! $1 =~ ^[0-9]+(\.[0-9]+)?$ ]] && echo "第一位参数只能传入数字" && exit
[[ ! $2 =~ ^[0-9]+(\.[0-9]+)?$ ]] && echo "第二位参数只能传入数字" && exit

# 使用 bc 浮点运算工具进行大小判断
[ `echo " $1 > $2" | bc` -eq 1 ] && echo "第一个参数大" 
[ `echo " $1 < $2" | bc` -eq 1 ] && echo "第二个参数大" 
[ `echo " $1 == $2" | bc` -eq 1 ] && echo "两个参数一样大" 

流程控制

  • if语句
    语法:
    # 单分支
    if 条件1;then
    	代码1
    	代码2
    	...
    fi
    # 双分支
    if 条件1;then
    	代码1
    	代码2
    	...
    else
    	代码1
    	代码2
    	...
    fi
    # 多分支
    if 条件1;then
    	代码1
    	代码2
    	...
    elif 条件2;then
    	代码1
    	代码2
    	...
    elif 条件3;then
    	代码1
    	代码2
    	...
    else
    	代码1
    	代码2
    	...
    fi
    
  • case语句
    语法:
    case $x in
    1)
    	代码1
    	代码2
    	代码3
    	;;
    2)
    	代码1
    	代码2
    	代码3
    	;;
    3|4|5)			# 变量为3、4、5都可以进入这个代码块
    	代码1
    	代码2
    	代码3
    	;;
    ...
    *)
    	代码1
    	代码2
    	代码3
    esac
    
    案例:输入名字,输出系统权限身份
    #!/bin/bash
    read -p "please input your name: " name
    if [ -z "$name" ];then
    	echo "必须输入用户名" 
    	exit
    	
    case $name in
    "root")
    	echo "管理员"
    	;;
    "egon")
    	echo "普通用户"
    	;;
    *)
    	echo "访客"
    esac
    
  • while循环
    特点:条件为真则运行循环体代码,直到条件变为假则结束循环
    总结:循环次数取决于条件何时变为假
    语法:
    while 条件	
    do
    	代码1
    	代码1
    	代码1
    done
    
    案例:打印0~7
    #! /bin/bash
    count=0
    while [ $count -le 7 ]	#小于等于7
    do
    	echo $count
    	let count++
    done	
    
  • until循环
    特点:条件为假则运行循环体代码,直到条件变为真则结束循环
    语法:
    until 条件	
    do
    	代码1
    	代码1
    	代码1
    done
    
    案例:打印0~7
    #! /bin/bash
    count=0
    
    until [ $count -gt 7 ]	#大于7
    do
    	echo $count
    	let count++
    done	
    
  • break终止本层循环
    #! /bin/bash
    count=0
    while :	
    do
    	if [ $count -le 7 ];then
    		break
    	fi
    	echo $count
    	let count++
    done
    
  • continue终止本次循环
    #! /bin/bash
    count=0
    while [ $count -le 7 ]	
    do
    	if [ $count -eq 4 ];then
    		continue
    	fi
    	echo $count
    	let count++
    done
    
  • for循环
    语法:
    for x in 参数值1 参数值2 参数值3
    do
    	代码1
    	代码1
    	代码1
    done
    
    案例:
    #! /bin/bash
    for x in `ls /root`
    do
    	echo "hello $x"
    done
    
    shell风格的for,常用n列表方式
    for i in 1 2 3
    for i in {
          
          1,2,3}
    for i in {
          
          1..9}
    for i in {
          
          9..1}
    for i in {
          
          a..z}
    for i in {
          
          z..a}
    for i in {
          
          A..Z}
    for i in {
          
          Z..A}
    for i in ${命令}		# 列如:for i in ${head -}
    for i in ${find...}
    
  • select选择循环
    select choice in {
          
          "退出","取款","转账","查询余额"}
    do
    	echo "选项是:$choice"
    	case $choice in
    	"退出")
    		break
    		;;
    	"取款")
    		echo "正在取款"
    		;;
    	"转账")
    		echo "正在转账"
    		;;
    	"查询余额")
    		echo "正在查询余额"
    		;;
    	*)
    		echo "其他功能还没有开发"
    done
    

函数

先定义后使用

基本使用

function f1(){
    
    
	echo "第一种方式"
}
function f2{
    
    
	echo "第二种方式"
}
f1(){
    
    
	echo "第三种方式"
}

函数参数

function f1(){
    
    
	echo "$1"
	echo "$2"
	echo "$3"
	echo "$4"
	echo "$5"
}
f1 111 222 333 444 555 

返回值

使用return返回值:

  • 使用return返回值,只能返回1-255的整数
  • 函数使用return返回值,通常只是用来供其他地方调用获取状态,因此通常仅返回0或1;0表示成功,1表示失败
    #!/bin/bash
    
    this_pid=$$
    # 判断nginx进程是否正在运行
    function is_nginx_running
    {
          
          
    	ps -ef | grep nginx | grep -v grep | grep -v $this_pid &>/tmp/null
    	if [ $? -eq 0 ];then
    		# return 0,也可以省略0直接return,两个是等价
    		return
    	else
    		return 1
    	fi
    }
    # return在函数中的返回值,只是用来做状态判断的,根据状态值做下一步判断
    # 函数的返回值为0时,表示nginx服务运行正常输出 && 后字符串,否则返回 ||后字符串
    is_nginx_running && echo "Nginx is running" || echo "Nginx is stoped"
     
     
    # 运行脚本
    ~ % sh 29.echo_return_nginx.sh
    Nginx is stoped
    ~ % sudo nginx  # Mac 使用,Linux为 systemctl start nginx 
    ~ % sh 29.echo_return_nginx.sh
    Nginx is running
    

使用echo返回值:

  • 使用echo可以返回任何字符串结果
  • 通常用于返回数据,比如一个字符串值或者列表值
    #!/bin/bash
    
    # 获取整个Linux系统上所拥有的所有用户
    function get_users
    {
          
          
    	# users=`cat /etc/passwd | cut -d: -f1` # linux使用
    	# Mac 使用
    	users=`cat /etc/passwd | tail -n+11 | cut -d: -f1 | cut -d_ -f2`
    	echo $users
    }
     
    # 执行该函数,返回值为用户列表
    # get_users
     
    # 遍历用户列表对用户名做处理
    user_list=`get_users`
    index=1
    for user in $user_list
    do
    	echo "This $index user is : $user"
    	index=$(($index+1))
    done
     
     
    # 运行脚本
    ~ % sh 30.echo_sys_user.sh
    This 1 user is : nobody
    This 2 user is : root
    ... ...
    This 109 user is : oahd
    

猜你喜欢

转载自blog.csdn.net/weixin_46051479/article/details/125648230