zoukankan      html  css  js  c++  java
  • Shell 编程入门

    Shell 编程入门

    Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。它是操作系统最外层的接口,
    负责直接面向用户交互并提供内核服务。

    一、变量

    1、 定义

    Shell 定义变量时,变量名不加美元符号,如:

    content="hello world!"
    

    变量名的命名须遵循如下规则:

    • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
    • 中间不能有空格,可以使用下划线 _。
    • 不能使用标点符号。
    • 不能使用bash里的关键字(可用help命令查看保留关键字)。

    2、 使用

    使用一个定义过的变量,只要在变量名前面加美元符号即可,如:

    content="hello world!"
    echo $content
    echo ${content}
    

    变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界。

    content="hello world!"
    echo "name:${content}!!!"
    

    推荐给所有变量加上花括号,这是个好的编程习惯。

    已定义的变量,可以被重新定义,如:

    content="hello world!"
    echo $content
    content="hello shell!"
    echo $content
    

    3、 只读变量

    使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

    content="hello world!"
    readonly content
    content="hello shell!"
    

    运行脚本,结果如下:

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

    4、 局部变量

    Shell 中默认定义的变量是全局变量,可以使用 global 进行显式声明,其作用域从被定义的地方开始,一直到脚本结束或者被删除的地方。

    local 可以定义局部变量,在函数内部使用。

    #!/bin/bash
    
    name="global variable"
    function func(){
        local name="local variable"
        echo $name
    }
    
    func
    echo $name
    
    # local variable
    # global variable
    

    5、 变量类型

    shell 中会同时存在三种变量:

    • 局部变量;
    • 环境变量;
    • shell 变量。

    二、字符串

    字符串是最常用最有用的数据类型,字符串可以用单引号,也可以用双引号,也可以不用引号

    1、单引号

    str='this is a string'
    echo '$str'
    
    # $str
    

    单引号字符串的限制:

    • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
    • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。

    2、 双引号

    name="shell"
    str="Hello, I know you are "$name"! 
    "
    echo $str
    
    # Hello, I know you are "shell"!
    

    双引号的优点:

    • 双引号里可以有变量;
    • 双引号里可以出现转义字符。

    3、 字符串长度

    string="abcd"
    echo ${#string} 
    
    # 4
    

    4、 提取子字符串

    以下实例从字符串第 2 个字符开始截取 4 个字符:

    string="huawei is a great compan"
    echo ${string:1:4} 
    
    # uawe
    

    5、 查找子字符串

    查找字符 i 或 o 的位置(哪个字母先出现就计算哪个):

    string="huawei is a great compan"
    echo `expr index "$string" io`  
    
    # 6
    

    注意: 以上脚本中 ` 是反引号,而不是单引号 ',不要看错了哦。

    三、数组

    Shell 只支持一维数组(不支持多维数组),并且没有限定数组的大小。类似于 C 语言,数组元素的下标由 0 开始编号。

    1、 定义数组

    shell 中,用括号来表示数组,数组元素用"空格"符号分割开。

    array=("value0" "value1" "value2" "value3")
    

    还可以单独定义数组的各个元素:

    array[0]="value0"
    array[1]="value1"
    array[n]="valuen"
    

    2、 读取数组

    读取数组元素值的一般格式是:

    value=${array_name[n]}
    

    使用 @ 符号可以获取数组中的所有元素,例如:

    echo ${array_name[@]}
    
    # value0 value1 value2 value3
    

    3、 获取长度

    获取数组长度的方法与获取字符串长度的方法相同,例如:

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

    注意:数组不可以进行切割,错误用法 ${array[1:2]}

    四、流程控制

    1、 if 判断

    语法示例判断两个变量是否相等。

    #!/bin/bash
    
    a=10
    b=20
    if [ $a == $b ]; then
        echo "a 等于 b"
    elif [ $a -gt $b ]; then
        echo "a 大于 b"
    elif [ $a -lt $b ]; then
        echo "a 小于 b"
    else
        echo "没有符合的条件"
    fi
    
    # a 小于 b
    

    不能使用 if [ $a > $b ],正确的方式是 if (( $a > $b ))

    2、 for 循环

    for 循环即执行一次所有命令,空格进行元素分割,使用变量名获取列表中的当前取值。

    示例,顺序输出当前列表中的数字:

    #!/bin/bash
    
    for loop in 1 2 3; do
        echo "The value is: $loop"
    done
    
    #The value is: 1
    #The value is: 2
    #The value is: 3
    

    循环字符串内容:

    #!/bin/bash
    
    for str in This is a string; do
        echo $str
    done
    
    # This
    # is
    # a
    # string
    

    循环数组中元素:

    #!/bin/bash
    
    array=("value0" "value1" "value2" "value3")
    # array[*]与array[@]两者皆可
    for loop in ${array[*]}; do    
        echo ${loop}
    done
    
    # value0
    # value1
    # value2
    # value3
    

    3、 while 循环

    while 循环用于不断执行一系列命令,也用于从输入文件中读取数据。

    以下是一个基本的 while 循环,测试条件是:如果 int 小于等于 5,那么条件返回真。int 从 1 开始,每次循环处理时,int 加 1。运行上述脚本,返回数字 1 到 5,然后终止。

    int=1
    while [ $int -le 5 ]; do
        echo $int
        let "int++"
    done
    

    无限循环

    # 方式一
    while :
    do
        command
    done
    
    # 方式二
    while true
    do
        command
    done
    

    4、 break 终止

    在循环语句中,可以使用 break 命令,允许跳出所有循环(终止执行后面的所有循环)。

    #!/bin/bash
    
    while :; do
        echo -n "输入 1 到 5 之间的数字:"
        read aNum
        case $aNum in
        1 | 2 | 3 | 4 | 5)
            echo "你输入的数字为 $aNum!"
            ;;
        *)
            echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
            break
            ;;
        esac
    done
    

    5、 continue 继续

    continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

    #!/bin/bash
    
    while :; do
        echo -n "输入 1 到 5 之间的数字: "
        read aNum
        case $aNum in
        1 | 2 | 3 | 4 | 5)
            echo "你输入的数字为 $aNum!"
            ;;
        *)
            echo "你输入的数字不是 1 到 5 之间的!"
            continue
            echo "游戏结束"
            ;;
        esac
    done
    

    运行代码发现,当输入大于5的数字时,该例中的循环不会结束,语句 echo "游戏结束" 永远不会被执行。

    五、函数

    1、 函数定义

    Shell 中可以用户定义函数,然后在 shell 脚本中可以随便调用。

    下面的例子定义了一个函数并进行调用:

    #!/bin/bash
    
    function demo(){
         echo "这是我的第一个 shell 函数!"
    }
    echo "-----函数开始执行-----"
    demo
    echo "-----函数执行完毕-----"
    

    可以带 function fun() 定义,也可以直接 fun() 定义,不带任何参数。

    参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return 后跟数值n(0-255)。

    函数脚本执行结果:

    -----函数开始执行-----
    这是我的第一个 shell 函数!
    -----函数执行完毕-----
    

    2、 函数参数

    shell 中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1 表示第一个参数,$2 表示第二个参数...

    带参数的函数示例:

    #!/bin/bash
    
    function funWithParam(){
         echo "第一个参数为 $1 !"
         echo "第十个参数为 $10 !"
         echo "第十个参数为 ${10} !"
         echo "第十一个参数为 ${11} !"
         echo "参数总数有 $# 个!"
         echo "作为一个字符串输出所有参数 $* !"
    }
    funWithParam 11 22 3 4 5 6 7 8 9 34 73
    

    输出结果:

    第一个参数为 11 !
    第十个参数为 110 !
    第十个参数为 34 !
    第十一个参数为 73 !
    参数总数有 11 个!
    作为一个字符串输出所有参数 11 22 3 4 5 6 7 8 9 34 73 !
    

    参数获取时 $n${n} 还是有区别的,特别是第二行的打印。

    $10 不能获取第十个参数,获取第十个参数需要 ${10}。当n>=10时,需要使用 ${n} 来获取参数。

    另外,还有几个特殊字符用来处理参数:

    $#	传递到脚本或函数的参数个数
    $*	以一个单字符串显示所有向脚本传递的参数
    $$	脚本运行的当前进程ID号
    $!	后台运行的最后一个进程的ID号
    $@	与$*相同,但是使用时加引号,并在引号中返回每个参数。
    $-	显示Shell使用的当前选项,与set命令功能相同。
    $?	显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
    

    6、运算符

    1、算术运算符

    下表列出了常用的算术运算符。

    +	加法	
    -	减法	
    *	乘法	
    /	除法
    %	取余	
    =	赋值	
    ==	相等
    !=	不相等
    

    注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]

    使用示例如下:

    #!/bin/bash
    
    a=10
    b=20
    
    val=$(expr $a + $b)
    echo "a + b : $val"
    
    val=$(expr $a - $b)
    echo "a - b : $val"
    
    val=$(expr $a * $b)
    echo "a * b : $val"
    
    val=$(expr $b / $a)
    echo "b / a : $val"
    
    val=$(expr $b % $a)
    echo "b % a : $val"
    
    if [ $a == $b ]; then
        echo "a 等于 b"
    fi
    if [ $a != $b ]; then
        echo "a 不等于 b"
    fi
    

    还可以使用下面的运算符替换,结果都一致:

    #!/bin/bash
    
    a=10
    b=20
    
    val=$(expr $a + $b)
    echo "a + b : $val"
    
    var=$(($a + $b))
    echo "a + b : $val"
    
    var=$[$a + $b]
    echo "a + b : $val"
    

    注意:

    • 乘号(*)前边必须加反斜杠()才能实现乘法运算;
    • $((表达式)) 此处表达式中的 "*" 不需要转义符号 ""。

    2、关系运算符

    关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

    下表列出了常用的关系运算符。

    -eq	检测两个数是否相等,相等返回 true。
    -ne	检测两个数是否不相等,不相等返回 true。	
    -gt	检测左边的数是否大于右边的,如果是,则返回 true。	
    -lt	检测左边的数是否小于右边的,如果是,则返回 true。	
    -ge	检测左边的数是否大于等于右边的,如果是,则返回 true。	
    -le	检测左边的数是否小于等于右边的,如果是,则返回 true。
    

    使用示例如下:

    #!/bin/bash
    
    a=10
    b=20
    
    if [ $a -eq $b ]; then
        echo "$a -eq $b : a 等于 b"
    else
        echo "$a -eq $b: a 不等于 b"
    fi
    if [ $a -gt $b ]; then
        echo "$a -gt $b: a 大于 b"
    else
        echo "$a -gt $b: a 不大于 b"
    fi
    

    运算符可以使用"=="、"! ="、">"替换:

    #!/bin/bash
    
    a=10
    b=20
    
    if [ $a == $b ]; then
        echo "$a -eq $b : a 等于 b"
    else
        echo "$a -eq $b: a 不等于 b"
    fi
    if (($a > $b)); then
        echo "$a -gt $b: a 大于 b"
    else
        echo "$a -gt $b: a 不大于 b"
    fi
    

    注意:">"、"> =" 、"<" 、"< =" 不能使用"[]"。

    3、逻辑运算符

    常用的逻辑运算符。

    &&	逻辑的 AND	[[ $a -lt 100 && $b -gt 100 ]] 返回 false
    ||	逻辑的 OR	[[ $a -lt 100 || $b -gt 100 ]] 返回 true
    

    使用示例如下:

    #!/bin/bash
    
    a=10
    b=20
    
    if [[ $a -lt 100 && $b -gt 100 ]]; then
        echo "返回 true"
    else
        echo "返回 false"
    fi
    
    if [[ $a -lt 100 || $b -gt 100 ]]; then
        echo "返回 true"
    else
        echo "返回 false"
    fi
    

    执行脚本,输出结果如下所示:

    返回 false
    返回 true
    

    4、字符串运算符

    下表列出了常用的字符串运算符。

    =	检测两个字符串是否相等,相等返回 true。	
    !=	检测两个字符串是否不相等,不相等返回 true。	
    -z	检测字符串长度是否为0,为0返回 true。	
    -n	检测字符串长度是否不为 0,不为 0 返回 true。
    $	检测字符串是否为空,不为空返回 true。	
    

    字符串运算符实例如下:

    #!/bin/bash
    
    if [ -z $a ]
    then
       echo "-z $a : 字符串长度为 0"
    else
       echo "-z $a : 字符串长度不为 0"
    fi
    if [ -n "$a" ]
    then
       echo "-n $a : 字符串长度不为 0"
    else
       echo "-n $a : 字符串长度为 0"
    fi
    if [ $a ]
    then
       echo "$a : 字符串不为空"
    else
       echo "$a : 字符串为空"
    fi
    

    5、文件测试运算符

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

    -b file	检测文件是否是块设备文件。	
    -c file	检测文件是否是字符设备文件。	
    -d file	检测文件是否是目录。
    -f file	检测文件是否是普通文件。
    -g file	检测文件是否设置了 SGID 位。
    -k file	检测文件是否设置了粘着位(Sticky Bit)。
    -p file	检测文件是否是有名管道。
    -u file	检测文件是否设置了 SUID 位。
    -r file	检测文件是否可读。
    -w file	检测文件是否可写。
    -x file	检测文件是否可执行。
    -s file	检测文件是否为空。
    -e file	检测文件。
    

    七、输入/输出重定向

    1、 输出重定向

    将命令的完整的输出重定向在用户文件中。

    # 覆盖
    $ echo "hello world" >./test.file
    
    # 追加
    $ echo "hello world" >>./test.file
    

    2、 输入重定向

    从用户文件中的内容输出到命令行。

    $ wc -l  < ./test.file
    1
    

    可以与 while 语句结合,遍历文件内容,按行打印:

    while read line; do
        echo $line
    done < ./test.file
    

    3、 标准输入输出

    一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

    • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。
    • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。
    • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

    默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

    如果希望 stderr 重定向到 file,可以这样写:

    $ command 2>file
    

    如果希望 stderr 追加到 file 文件末尾,可以这样写:

    $ command 2>>file
    

    2 表示标准错误文件(stderr)。

    如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:

    $ command > file 2>&1
    
    或者
    
    $ command >> file 2>&1
    

    如果希望对 stdin 和 stdout 都重定向,可以这样写:

    $ command < file1 >file2
    

    command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。

    八、eval 函数

    当我们在命令行前加上 eval 时,shell 就会在执行命令之前扫描它两次。eval 命令将首先会先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量。该命令对变量进行两次扫描。

    常见的使用场景如下:

    1、普通情况

    $ var=100
    $ echo $var
    100
    $ eval echo $var
    

    这样和普通的没有加 eval 关键字的命令的作用一样。

    2、字符串转换命令

    $ cat file
    helle shell
    it is a test of eval
    
    $ tfile="cat file"
    $ eval $tfile
    helle shell
    it is a test of eval
    

    从上面可以看出 eval 经历了两次扫描,第一次扫描替换了变量为字符串,第二次扫描执行了字符串内容。

    3、获取参数

    $ cat t.sh
    #!/bin/bash
    
    eval echo $$#
    
    $ ./t.sh a b c
    c
    $ ./t.sh 1 2 3
    3
    

    通过转义符 “|” 与 $# 结合,可以动态的获取最后一个参数。

    4、 修改指针

    $ var=100
    $ ptr=var
    $ eval echo $$ptr
    100
    $ eval $ptr=50
    $ echo $val
    50
    

    推荐阅读:

    《Linux命令行与shell脚本编程大全》

    《谷歌shell编码规范》

  • 相关阅读:
    element-ui 中Message 消息提示
    MYSQL 常用语法格式
    python 爬取必应每日图片
    linux:文件权限管理
    如何建立自我学习的"触发机制"?
    总有那么一些人,把自己的思想强加给别人
    linux 控制history命令历史记录
    linux apt-get 安装与卸载命令
    ubuntu16.04下 安装java8
    历史与过去是一面镜子,它照到的是你现在的自己
  • 原文地址:https://www.cnblogs.com/lianzhilei/p/15316945.html
Copyright © 2011-2022 走看看