• Day58 Linux shell编程 shell语法 脚本语言


    目录

    1 shell编程

    1.1 概述

    1.2执行脚本 

    2. shell语法 

    2.1shell环境变量

     2.2临时变量

    2.3 文件名代换

    3. 脚本语法

    3.1 条件测试

    3.2分支

    if/then/elif/fi


    shell编程

    1.1 概述

    Shell linux 中一个重要的层次,它是用户与系统交互作用的界面。在介绍 linux 命令时, shell 都作为命 令解释程序出现:它接收用户打入的命令,进行分析,创建子进程实现命令所规定的功能,等子进程终 止工作后,发出提示符。这是shell 最常见的使用方式。
    Shell 除了作为命令解释程序以外,还是一种高级程序设计语言, 它有变量,关键字,有各种控制语句, if case while for 等语句,有自己的语法结构 。利用 shell 程序设计语言可以编写出功能很强、但代码简单的程序,特别是它把相关的linux 命令有机地组合在一起,可大大提高编程的效率,充分利用linux系统的开放性能,设计出适合自己要求的命令。
    用户在命令行输入命令后,一般情况下 Shell fork exec 该命令,但是 Shell 的内建命令例外,执行内建命令相当于调用Shell 进程中的一个函数,并不创建新的进程。以前学过的 cd. alias. umask exit 等命令即是内建命令,凡是用which 命令查不到程序文件所在位置的命令都是内建命令,内建命令没有单独的man手册,要在 man 手册中查看内建命令,应该执行。
    user02$ man bash-builtins

    1.2执行脚本 

    编写一个简单的脚本 test.sh:
    1. #!/bin/bash
    2. #This is to show what a example looks like
    3. date
    4. echo "Our first example."
    5. echo #this inserts an empty line in output .
    6. echo "We are currently in the following diractory."
    7. echo
    8. /bin/pwd
    9. echo
    10. echo "This diractory contains following files"
    11. /bin/ls
    Shell 脚本中用 # 表示注释,相当于 C 语言的 // 注释。但如果 # 位于第一行开头,并且是 #! ( 称为 Shebang) 则例外,它表示该脚本使用后面指定的解释器/bin/sh 解释执行。如果把这个脚本文件加上可执行权限然后执行:
    user02@vir:~ $ chmod a + x test.sh
    user02@vir:~ $ ./test.sh # 方法一
    user02@vir:~ $ . test.sh # 方法二
    user02@vir:~ $ source test.sh # 方法三
    #source 或者 . 命令是 Shell 的内建命令,这种方式也不会创建子 Shell, 而是直接在交互式 Shell 下逐行执行脚本中的命令
    原理: Shell fork 一个子进程并调用 exec 执行 ./test.sh 这个程序 ,exec 系统调用应该把子进程的代码段替换成./test.sh 程序的代码段,并从它的 start 开始执行。然而 test.sh 是个文本文件,根本没有代码段和start 函数,怎么办呢 ?
    特例:用 bash 程序直接执行文件时,不需要脚本文件有可执行权限

     user02$ /bin/bash ./test.sh

    如果将命令行下输入的命令用 () 括号括起来,那么也会 fork 出一个子 Shell 执行小括号中的命令,一行中可以输入由分号;隔开的多个命令,比如:
    user02 $ (cd ..; ls -l ) # 不会改变工作目录

    则有不同的效果,cd ..命令是直接在交互式Shell下执行的,改变交互式ShellPWD

    user02 $ cd ..; ls -l # 会改变工作目录

    2. shell语法 

    Shell 变量名通常是以 字母或下划线打头的字母、数字和下划线字符序列,并且大小写字母意义不同
    有两种类型的 Shell 变量:环境变量和临时变量

    2.1shell环境变量

    环境变量可以从父进程传给子进程,因此 Shell 进程的环境变量可以从当前 Shell 进程传给 fork 出来的子进程。用printenv 命令可以显示当前 Shell 进程的环境变量。

     2.2临时变量

    只存在于当前 Shell 进程,用 set 命令可以显示当前 Shell 进程中定义的所有变量 ( 包括临时变量和环境变量) 和函数。
    环境变量是任何进程都有的概念,而临时变量是 Shell 特有的概念。在 Shell 中,环境变量和临时变量的定义和用法相似。在Shell 中定义或赋值一个变量 :
    1. user02$ VARNAME=HELLO #定义一个临时变量
    2. user02$ export VARNAME=HELLO #定义一个环境变量
    3. user02$ env | grep VARNAME #env能查到就是环境变量,查不到就是临时变量
    4. user02$ unset VARNAME #取消环境变量
    注意
    • 等号两边都不能有空格,否则会被Shell解释成命令和命令行参数
    注意
    • 在定义变量时不用“VAR” 取变量值时要用。和C语言不同的是,Shell 变量不需要明确定义类型,事实上Shell变量的值都是字符串,比如我们定义VAR=45,其实VAR的值是字符串45而非整数。Shell 变量不需要先定义后使用,如果对一个没有定义的变量取值,则值为空字符串

    2.3 文件名代换

    这些用于匹配的字符称为通配符 (wildcard), : * ? [ ] 具体如下 :
    * :匹配 0 个或多个任意字符
    ? :匹配一个任意字符
    [ 若干字符 ]: 匹配方括号中任意一个字符的次出现一次的文件名         
    注意
    • Globbing 所匹配的文件名是由Shell 展开的,也就是说在参数还没传给程序之前已经展开了,比如上述Is ch0[012].doc命令,如果当前目录下有cho00.docch02.doc,则传给Is命令的参数实际上是这两个文件名,而不是一个匹配字符串

    命令代换

    “ ' ” 反引号括起来的也是一条命令, Shell 先执行该命令,然后将输出结果立刻代换到当前命令行中。
    例如 . 定义一个变量存放 date 命令的输出 :
    user02 $ DATE = `date`
    user02 $ echo = $DATE #$ 变量名:变量的值

    命令代换也可以用S()表示:

    user02$ DATE=$(date)  

    算数代换

    使用$(()),用于算术计算,(())中的 Shell变量取值将转换成整数,同样含义的$[ ]等价例如:

    user02$ VAR=45
    user02$ echo $(($VAR+3)) # 等价 $((VAR+3)) $[VAR+3]

     

    S(())) 中只能用 +-*/ () 运算符,并且只能做整数运算
    S[base#n], 其中 base 表示进制, n 按照 base 进制解释,后面再有运算数,按十进制解释

     user02$ echo $[2#10+3] #二进制10,然后+3

    user02$ echo $[8#10+3] # 八进制 10 ,然后 +3

     转移字符

    C 语言类似, \ Shell 中被用作转义字符,用于去除紧跟其后的单个字符的特殊意义 ( 回车除外 ) ,换句话说,紧跟其后的字符取字面值。例如:

     user02$ echo \$PATH

    user02$ \\

    创建文件 --test

    touch --test #报错  

    因为各种 UNIX 命令都把 - 号开头的命令行参数当作命令的选项,而不会当作文件名。如果非要处理以号开头的文件名,可以有两种办法:
    user02$ touch -- --test
    user02$ touch ./--test
    \ 还有一种用法,在 \ 后敲回车表示续行, Shell 并不会立刻执行命令,而是把光标移到下一行,给出一
    个续行提示符 > ,等待用户继续输入,最后把所有的续行接到一起当作 - 一个命令执行。例如 :
    user02$ ls \ # 按回车续行继续输入
    > -l # 按回车代表结束

    单引号

    Shell 脚本中的单引号和双引号一样都是字符串的界定符,而不是字符的界定符。单引号用于保持引号内所有字符的字面值,即使引号内的\ 和回车也不例外,但是字符串中不能出现单引号。如果引号没有配对就输入回车,Shell 会给出续行提示符,要求用户把引号配上对。例如 :
    user02$ echo "abcde"
    user02$ echo 'abcd'

    双引号

    被双引号括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。单引号不能展开变量。

     user02$ VAR=`date`

    user02$ echo "$VAR"
    2022 09 17 日星期六 10:22:52 CST
    user02$ echo "${VAR}"
    2022 09 17 日星期六 10:22:52 CST
    user02$ echo 'VAR'
    user02$ echo '${VAR}'

    3. 脚本语法

    3.1 条件测试

    命令 test [ 可以测试一个条件是否成立,如果测试结果为真,则该命令的 Exit Status 0 ,如果测试结果为假,则命令的Exit Status 1 。例如测试两个数的大小关系 :
    user02$ VAR=2
    user02$ test $var -gt 1
    user02$ echo $?
    虽然看起来很奇怪,但左方括号 [ 确实是一个命令的名字,传给命令的各参数之间应该用空格隔开,比如:$VAR -gt 3 ] [ 命令的四个参数,它们之间必须用空格隔开。命令 test [ 的参数形式是相同的,只不过test 命令不需要 ] 参数。以 [ 命令为例,常见的测试命令如下表所示 :
    [ -d DIR ] 如果 DIR 存在并且是一个目录则为真
    [ -f FILE ] 如果 FILE 存在且是一个普通文件则为真
    [ -z STRING ] 如果 STRING 的长度为零则为真
    [ -n STRING ] 如果 STRING 的长度非零则为真
    [ STRING1 = STRING2 ] 如果两个字符串相同则为真
    [ STRING1 ! = STRING2 ] 如果字符串不相同则为真
    [ ARG1 OP ARG2 ] ARG1 ARG2 应该是整数或者取值为整数的变量, op -eq ( 等于 ) -ne ( 不等于 ) - lt ( 小于 ) -le ( 小于等于 ) -gt ( 大于 ) -ge ( 大于等于 ) 之中的一个。

    C语言类似,测试条件之间还可以做与、或、非逻辑运算:

    [ ! EXPR ] EXPR 可以是上表中的任意一种测试条件, ! 表示 逻辑反 ( )”
    [ EXPR1 -a EXPR2 ] EXPR1 EXPR2 可以是上表中的任意一种测试条件, -a 表示 逻辑与
    [ EXPR1 -o EXPR2 ] EXPR1 EXPR2 可以是上表中的任意一种测试条件, -o 表示 逻辑或

    例程:

    $ VAR=abc
    $ [ -d user02 -a $VAR = 'abc' ]
    $ echo $?
    注意
    如果上例中的 $VAR 变量事先没有定义,则被 Shell 展开为空字符串,会造成测试条件的语法错误
    ( 展开为 [ -d user02 -a = 'abc' ]) 作为一种好的 Shell 编程习惯,应该总是把变量取值放在双引号之
    ( 展开为 [ -d user02 -a " " = 'abc' ]):

     $ VAR=abc

    $ [ -d user02 -a "$VAR" = 'abc' ]
    $ echo $?

    3.2分支

    if/then/elif/fi

    C 语言类似,在 Shell 中用 if then elif else fi 这几条命令实现分支控制。这种流程控制语句本质上也是由若干条Shell 命令组成的
    if [ -f ~/.bashrc ] ; then
            . ~/.bashrc
    fi

     “==:==”是一个特殊的命令,称为空命令,该命令不做任何事,但Exit Status总是真。

    1. echo "Is it morning? Please answer yes or no. "
    2. read YES_OR_NO
    3. if [ "$YES_OR_NO" = "yes" ]; then
    4. echo "Good morning!"
    5. elif [ "$YES_OR_NO" = "no" ]; then
    6. echo "Good afternoon!"
    7. else
    8. echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
    9. return ;
    10. fi
    上例中的 read 命令的作用是等待用户输入一行字符串,将该字符串存到一个 Shell 变中。
    此外, Shell 还提供了 && || 语法,和 C 语言类似,具有 Short-circuit 特性,很多 Shell 脚本喜欢写成这样:

     test "$(whoami)" != 'root' && (echo you are using a non-privileged account;)

    && 相当于 “if...then..." || 相当于 “if not...then..."
    && || 用于连接两个命令,而上面讲的 -a -o 仅用于在测试表达式中连接两个测试条件,要注意它们的区别,例如:

     test "$VAR" -gt 1 -a "$VAR" -lt 3

    例程:

    #! /bin/bash
    echo " 请输入你要判别的文件名 : "
    read file_ name
    if [ -f " $file_ name" ]; then
            echo 'it is a file
    elif [ -d " $file_ nane" ]; then
            echo 'It is a dir'
    elif [ -p $file_ _name ] ; then
            echo 'It is pipe
    else
            echo 'it is not a known file'
            exit 1
    fi

     

     case/esac

    case 命令可类比 C 语言的 switch/case 语句, esac 表示 case 语句块的结束。 C 语言的 case 只能匹配整型或 字符型常量表达式,而Shell 脚本的 case 可以匹配字符串和 Wildcard , 每个匹配分支可以有若干条命令, 末尾必须以 ;; 结束 ,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到 esac 之后,不需要像C 语言一样用 break 跳出
    1. #! /bin/bash
    2. echo "Is it morning? Please answer yes or no."
    3. read YES_OR_NO
    4. case "$YES_OR_NO" in
    5. yes|y|Yes|YES)
    6. echo "Good Morning!";;
    7. [nN][oO])
    8. echo "Good Afternoon!";;
    9. *)
    10. echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
    11. return 1;;
    12. esac
    启动 nfs-kernel-server 服务的命令是:
    $ sudo /etc/init.d/nfs-kernel-server start

    $1是一个特殊变量,在执行脚本时自动取值为第一个命令行参数,也就是start,所以进入start)分支执行相关的命令。同理,命令行参数指定为stopreload restart 可以进入其它分支执行停止服务、重新加载配置文件或重新启动服务的相关命令

     

     

  • 相关阅读:
    如何实现chatGPT批量问答,不用token
    机器学习笔记之高斯过程(一)——基本介绍
    STM32的光敏检测自动智能窗帘控制系统proteus设计
    企业为什么要数字化转型?数字化转型成功的案例有哪些?
    关于两个不同数据库的两张表建立数据库链接,关联查询数据
    SpringBoot集成Apache RocketMQ详解
    QLineEdit 类(行编辑器)
    电脑重装系统后序列号怎么查
    金仓数据库 KingbaseES 异构数据库移植指南 (4. 应用迁移流程)
    《利息理论》指导 TCP 拥塞控制
  • 原文地址:https://blog.csdn.net/qq_59947178/article/details/127656987