• Shell 入门(一):变量和流程控制


    刚入职新公司,碰到一个棘手的线上莫名宕机问题。我需要查看应用日志,应用端口等信息,以前,只是知道tail -f service.log | grep '关键词'这样的简单查询日志操作。另外,需要频繁打开应用目录,启动应用,然后切换到日志目录,查看日志,比较繁琐,后面我通过 Shell 的alias功能,让操作得到一定程度的简化。但是,为了写出更好的执行脚本,于是有了这系列文章。

    本系列文章分为两小节
    1,Shell 是什么?
    2,Shell 基础

    • 变量
    • 流程控制
    • 数组
    • 函数

    1,Shell 是什么?

    简单来说,Shell 就是操作系统上运行的一个应用程序。
    普通用户接触最多的操作系统是 Windows,而程序员使用更频繁的是 Linux 的服务端系统。
    Windows 提供图形化界面,让我们可以通过移动光标,鼠标右键等一系列操作完成文件的编辑工作,降低普通用户的使用难度。
    而程序员是以软件开发为职业,操作的是很少提供图形化界面的服务端系统,而 Shell 可以让用户更方便的操作 Linux 系统,
    文本或字符串检索,文件的查找或创建,大规模软件的自动部署,监控服务器性能,压缩文件等。

    Shell 原理图

    1.2,初识 Shell

    • test.sh
    #!/bin/bash
    echo "Hello World!"
    

    1.3 shell 脚本 Debug 调试

    • 基本写法:sh [-nxv] 脚本
      • -n: 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误;
      • -v: 一边执行脚本,一边将执行过的脚本命令打印到标准输出;
      • -x:提供跟踪执行信息,将执行的每一条命令和结果依次打印出来;

    2, Shell 基础

    • 变量
    • 流程控制
    • 数组
    • 函数

    2.1 变量

    • 自定义变量
    • 系统环境变量
    • 预先定义变量

    2.1.1 自定义变量

    • 定义变量:变量名=变量值
    • 引用变量:$变量名${变量名}
    • 查看变量:echo $变量名 或者 set显示所有变量,包括自定义变量和环境变量

    2.1.2 系统环境变量

    • 定义环境变量:export 变量名=变量值
    • 引用环境变量:$变量名
    • 查看环境变量:echo $变量名 或者 env | grep 变量名

    2.1.3 预先定义变量

    • $0: 脚本文件名称
    • $*: 当前脚本传入的所有参数
    • $@: 当前脚本传入的所有参数
    • $#: 当前脚本传入参数的个数
    • $$: 执行当前脚本进程的 PID
    • $!: 上一个后台进程的 PID
    • $?: 上一个命令的返回值,0 表示成功执行

    2.1.4 单引号和双引号的困惑

    [root@roclinux ~]$ echo $PWD
    /root
    [root@roclinux ~]$ alias dirA="echo work directory is $PWD"
    [root@roclinux ~]$ alias dirB='echo work directory is $PWD'
     
    # 正确显示
    [root@roclinux ~]$ dirA            
    work directory is /root
     
    # 正确显示
    [root@roclinux ~]$ dirB
    work directory is /root 
     
    # 显示不正确, 怎么回事?     
    [root@roclinux ~]$ cd /
    [root@roclinux /]$ dirA
    work directory is /root     
     
    # 正确显示 
    [root@roclinux /]$ dirB
    work directory is /    
    
    # 查看 alias 的真实内容
    [roc@roclinux ~]$ alias dirA
    alias dirA="echo work directory is /root"
     
    [roc@roclinux ~]$ alias dirB
    alias dirB='echo work directory is $PWD'
    
    • 示例来源:单引号和双引号的困惑
    • 使用双引号的 dirA,通过 Shell 的变量转换后已经变成了字符串echo work directory is /root,当目录切换后,显示的还是原目录内容;
    • 使用单引号的 dirB,由于不受 Shell 的影响,仍然保留着原来的位置echo work directory is $PWD,当切换目录后再执行,变量$PWD被 Shell 替换掉,因此内容被正确显示。
    • 换句话说,双引号是弱引用,会解析变量,单引号是强引用,不会解析变量。
    # 示例二:
    [xiaoa@linux ~]$ name=zhangsan
    [xiaoa@linux ~]$ echo 'My name is ${name}'
    My name is ${name}
    
    [xiaoa@linux ~]$ echo "My name is ${name}"
    My name is zhangsan
    

    2.1.5 命令替换

    • \``命令等价于 $()
    # 示例一,创建文件
    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}'`
    

    2.1.6 变量数值运算

    # 示例一: 整数运算  expr + - * / %
    [xiaoa@linux ~]$ a=1
    [xiaoa@linux ~]$ b=2
    [xiaoa@linux ~]$ expr $a + $b
    3
    [xiaoa@linux ~]$ expr $a * $b
    
    
    # 示例二:整数运算  let + - * / %
    [xiaoa@linux ~]$ let sum=2+3;
    [xiaoa@linux ~]$ echo $sum
    5
    
    
    # 示例三: 小数运算  bc  + - * / %
    [xiaoa@linux ~]$ echo "1+2" |bc
    [xiaoa@linux ~]$ echo "scale=2;4/8" |bc
    

    2.1.7 默认变量

    • ${变量名-新的变量值}:
      • 变量没有被赋值,会使用“新的变量值”替代;
      • 变量有被赋值(包括空值):不会被替代
    • echo ${var1-test}
    • ${变量名:-新的变量值}:
      • 变量没有被赋值(包括空值),会使用“新的变量值”替代;
      • 变量有被赋值:不会被替代
    • echo ${var1:-test}

    2.2 流程控制

    • 条件测试与逻辑运算表达式
    • 流程控制语句 if
    • 流程控制语句 case
    • 循环语句 for,while

    2.2.1 条件测试 test 条件测试与逻辑运算表达式

    • 基本语法
    • 文件测试
    • 数值比较
    • 字符串比较
    • 正则比较

    2.2.1.1 基本语法

    # 条件表达式
    1,`test 条件表达式`
    2,`[ 条件表达式 ]`
    3,`[[ 条件表达式 ]]`
    
    
    # 逻辑运算表达式
    命令 1 ; 命令 2     无论命令 1 是否执行成功都会执行命令 2
    命令 1 && 命令 2    命令 1 必须执行成功,才会执行命令 2
    命令 1 || 命令 2    命令 1 必须执行失败,才会执行命令 2
    

    2.2.1.1 文件测试

    • man [: 查看[表达式用法;
    • [ -e dir|file ]: 目录或文件是否存在;
    • [ -d dir ]: 目录是否存在;
    • [ -f file ]: 文件是否存在;
    • [ -r file ]: 当前用户对该文件是否具有读权限;
    # 判断目录是否存在,不存在,则创建该目录
    test -d ~/Desktop/test || mkdir -p ~/Desktop/test
    

    2.2.1.2 数值比较

    • [ 整数1 操作符 整数 2 ]
    • 大于:[ 1 -gt 10 ]
    • 小于: [ 1 -lt 10 ]
    • 等于: [ 1 -eq 10 ]
    • 不等于: [ 1 -ne 10 ]
    • 小于等于: [ 1 -le 10 ]
    • 大于等于: [ 1 -ge 10 ]
    # 示例一:磁盘使用率大于 80, 打印输出
    
    #!/bin/bash
    Disk_Free=$(df -h|grep "$"|awk '{print $5)'|awk -F '%' '{print $1}'})
    
    if [ $Disk_Free -ge 80 ];then
        echo "Disk is Use: $Disk_Free%" > /tmp/disk_use.txt
    fi
    
    
    # 示例二:多整数比对
    # -a: and
    [ 1 -lt 2 -a 5 -gt 10 ];echo $?
    
    # -o: or
    [ 1 -lt 2 -o 5 -gt 10 ];echo $?
    
    # &&
    [ 1 -lt 2 && 5 -gt 10 ];echo $?
    
    # ||
    [ 1 -lt 2 || 5 -gt 10 ];echo $?
    

    2.2.1.3 字符串比较

    [xiaoa@linux ~]$ name=zhangsan
    [xiaoa@linux ~]$ [ $name = "zhangsan" ]; echo $?
    0
    [xiaoa@linux ~]$ [ $name == "zhangsan" ]; echo $?
    0
    [xiaoa@linux ~]$ echo ${#name}  # 计算字符长度
    8
    [xiaoa@linux ~]$ BBB=""
    [xiaoa@linux ~]$ echo ${#BBB}
    0
    [xiaoa@linux ~]$ [ -z "$BBB" ]  字符长度为 0
    0
    [xiaoa@linux ~]$ [ -n "$BBB" ]  字符长度不为 0
    1
    
    # 小结:变量为空或未定义,长度都为 0
    

    2.2.1.4 正则比对

    # 判断 name 是不是以 z 开头
    [xiaoa@linux ~]$ [[ $name =~ ^z ]];echo $?
    0
    
    # 判断变量是不是数字
    [xiaoa@linux ~]$ num=123
    [xiaoa@linux ~]$ num2=123abc
    [xiaoa@linux ~]$ [[ $num =~ ^[0-9]+$ ]];echo $?
    0
    [xiaoa@linux ~]$ [[ $num2 =~ ^[0-9]+$ ]];echo $?
    1
    

    2.2.2 流程控制语句 if

    # 单分支结构
    if [ 分数大于 60 ];then
        echo "及格"
    fi
    
    # 双分支结构
    if [ 分数大于 60 ];then
        echo "及格"
    else
        echo "不及格"
    fi
    
    # 多分支结构
    if [ 分数大于 80 ];then
        echo "优秀"
    elif [ 分数大于 60 ];then
        echo "及格"
    else
        echo "不及格"
    fi
    

    2.2.3 流程控制语句 case

    case 变量 in
    模式 1)
        执行命令 1;;
    模式 2)
        执行命令 2;;
    模式 3)
        执行命令 3;;
        *)
        执行默认命令
    esac
    
    # 示例 1
    
    #!/bin/bash
    case $1 in
            Linux)
                    echo "linux ..."
                    ;;
            Shell)
                    echo "shell ..."
                    ;;
            MySQL)
                    echo "mysql ..."
                    ;;
                *)
                    # echo "USAGE $0 [Linux|Shell|MySQL]"
                    echo "USAGE `basename $0` [Linux|Shell|MySQL]"
    esac
    
    # 示例 2
    
    #!/bin/bash
    read -p "Are you sure?[y/n]:" action
    case "$action" in
        y|Y|yes|YES)
            echo "user is deleted!"
            ;;
        *)
            echo "error"
    esac
    
    # 示例 3: 系统工具
    #!/bin/bash
    
    cat <<-EOF
    ===================
    h 显示命令帮助
    f 显示磁盘分区
    d 显示磁盘挂载
    m 查看内存使用
    u 查看系统负载
    q 退出程序
    ===================
    EOF
    
    while true
    do
        read -p "请输入你想查看的系统状态对应码[d/m/u/q] " sys
        case $sys in
            h)
                help|less
                ;;
            f)
                clear
                lsblk
                ;;
            d)
                clear
                df -h
                ;;
            m)
                clear
                free -m
                ;;
            u)
                clear
                uptime
                ;;
            q)
                break
                ;;
            *)
                echo "error"
                exit 1;
        esac
    done
    

    2.2.4 循环语句

    2.2.4.1 for 循环

    # 基本语法
    for 变量名 [ in 取值列表 ]
    do
        循环体
    done
    
    
    # 示例 1: 计算 1 到 100 的和
    #!/bin/bash
    sum=0
    for i in {1..100}
    do
        let sum=sum+$i
    done
    
    echo $sum
    
    
    # 示例 2:计算 1 到 100 之间所有奇数的和
    #!/bin/bash
    sum=0
    for i in `seq 1 2 100`
    do
        let sum=sum+$i
    done
    
    echo $sum
    

    2.2.4.2 while 循环

    # 基本语法
    while 条件测试
    do
        循环体
    done
    
    # 示例 1:计算 1~100 的和
    #!/bin/bash
    i=1
    sum=0
    while [ $i -le 100 ]
    do
        let sum=sum+$i  # 注意:此处没有空格
        let i++
    done
    
    echo $sum
    
    
    # 示例 2: 猜数字游戏
    #!/bin/bash
    
    sum=$((RANDOM%51))
    
    sleep 1
    
    echo "请输入1-50之间的数,开始猜吧!"
    
    count=0
    
    function type_num(){
        read -p "请输入一个数吧:" n
        expr $n + 1 &>/dev/null
        if [ $? -ne 0 ]; then
            echo "请输入一个数字"
            type_num
        fi
    }
    
    function guess(){
        (( count++ ))
        if [ $n -eq $sum ]; then
            echo "你猜中了,你的次数是:"${count}
            if [ $count -lt 3 ]; then
                echo "你太厉害了"
            elif [ $count -ge 3 -a $count -lt 6 ]; then
                echo "还是不错的,加油"
            else
                echo "你有点水啊"
            fi
            exit 0
        elif [ $n -gt $sum ]; then
            echo "猜大了"
            type_num
        else
            echo "猜小了"
            type_num
        fi
    }
    
    function main(){
        type_num
        while true
        do
            guess
        done
    }
    
    main
    

    **参考资料:**
  • 相关阅读:
    linux 统计文件信息 wc
    实现TRACE宏功能(内联函数形式和宏形式),无MFC时打印到Output窗口
    MFC关联控件和WORD类型变量 DDX_TEXT for WORD Walkaround
    winsock2.h头文件重复定义链接错误问题解决
    The Visual Studio Remote Debugger service on the target computer cannot connect back to this computer
    组策略 允许空密码
    使用template扩展已有的DDX_Text函数,使扩展能够同时支持各种数据类型的一个例子
    好文转载—为程序员量身定制的12个目标
    好文转载—六步创建一个安全的密码
    Win8下VS调试提升权限,避免权限造成的程序运行错误
  • 原文地址:https://www.cnblogs.com/linkworld/p/15063580.html
Copyright © 2020-2023  润新知