• shell脚本学习笔记


    不是所有的空格都没有意义

    1.1 在终端显示输出

    命令都是在终端会话中输入并执行的,打开终端的时候会出现一个命令提示符,一般形式如下:

    username@hostname$ / username@hostname#

    $ 表示的是普通用户,# 表示的是 root 用户,root 是 Linux 中权限最高的用户。上述命令也可以简单的配置为 $ 或者 #

    一般情况下不建议直接使用 root 用户执行命令,因为很容易对系统造成严重的破坏,更好的方式是使用 sudo 等命令获取临时的 root 权限


    shell 脚本通常以 shebang(sharp-bang) 开始:#!/bin/bash

    shebang 是一个文本行,其中 #!位于解释器路径之前。/bin/bash 是 Bash 的解释器命令路径。bash 将以 # 符号开头的行视为注释。脚本中只有第一行可以使用 shebang 来定义解释该脚本所使用的解释器。

    脚本的两种执行方式:

    • 将脚本名作为命令行参数:(这种方式实际上不需要指定 shebang 了,因为已经指定好了解释器)

      • bash script.sh
    • 授予脚本执行权限,将其变为可执行文件: (使用 shebang 之后的解释器路径来解释脚本)

      • chmod 755 script.sh

      • ./script.sh


    echo 是最基本的终端打印命令,类似的还有 printf 命令(类似于 c 中的 printf() 函数)

    1.2 登录式 shell 和非登录式 shell

    登录shell:是需要用户名、密码登录后才能进入的shell(或者通过–login”选项生成的shell)。例如:

    • 用户通过输入用户名/密码(或证书认证)后启动的shell;

    • 通过带有 -l 或者 --login 参数的bash命令启动的shell。

      例如,系统启动、远程登录、使用su -切换用户、通过bash --login命令启动bash等。

    非登录shell:不需要输入用户名和密码即可打开的Shell。 例如:

    • 默认情况下,直接命令“bash”就是打开一个新的非登录shell

    • 使用su 切换用,注意,这里没有中划线

    • 在Gnome或KDE(桌面环境)中打开一个“终端”(terminal)窗口程序也是一个非登录shell。

    两种常见的进入登录式 shell 的方式:

    • exec -l bash:这种方式会用 login shell 直接替换掉当前的 shell

    • bash -l:进入 login shell,不是替换,使用 exit 可以退出 login 状态

    判断登录式 shell 和非登录式 shell 的方式

    • echo $0:如果第一个字符是 - 说明是登录 shell

    • shopt login_shell:如果是 login_shell on 说明是登录 shell

    以上两种方式只需满足一个便可

    登录式 shell 和非登录式 shell 的区别

    最主要的区别是启动 shell 时所执行的 startup 文件不同

    主要可以参考一下两篇文章:

    最重要的一个点是:当需要对当前的 shell 进行一些设置的时候(e.g.对 bash 进行一些设置、设置一些环境变量、设置一些全局变量),可以根据当前 shell 的状态,知道应该去修改哪个文件

    1.3 使用变量与环境变量

    大多数脚本语言不要求在创建变量之前声明其类型。用到什么类型就是什么类型。在变量名前面加上一个美元符号就可以访问到变量的值。shell定义了一些变量,用于保存用到的配置信息,比如可用的打印机、搜索路径等。这些变量叫作环境变量

    变量名由一系列字母、数字和下划线组成,其中不包含空白字符。常用的惯例是在脚本中使用大写字母命名环境变量,使用驼峰命名法或小写字母命名其他变量。

    1.3.1 环境变量相关命令

    env # 显示所有环境变量
    printenv # 同上
    
    cat /proc/$PID/environ # 查看指定进程的环境变量
    cat /proc/$PID/environ | tr '\0' '\n' # 以一行一条环境变量显示
    
    # 以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
    # -o:仅显示找到的最小(起始)进程号;
    # -n:仅显示找到的最大(结束)进程号;
    # -l:显示进程名称;
    # -P:指定父进程号;
    # -g:指定进程组;
    # -t:指定开启进程的终端;
    # -u:指定进程的有效用户ID。
    pgrep(选项)(参数)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    1.3.2 实践操作

    可以使用等号操作符为变量赋值:varName=value

    • 如果 value 不包含任何空白字符(例如空格),那么就不需要将其放入引号中,否则必须使用单引号或双引号。
    • var = value 不同于 var=value 。把 var=value 写成 var = value 是一个常见的错误。两边没有空格的等号是赋值操作符加上空格的等号表示的是等量关系测试

    # 输出变量的值
    echo $var
    echo ${var}
    
    # 输出和添加环境变量
    echo $PATH
    export PATH="$PATH:/home/user/bin" 
    
    # 输出字符串长度
    var=123456
    echo ${#var}
    
    # 获取当前环境使用的 shell
    echo $SHELL
    echo $0
    
    # 更改 Bash 的提示字符串
    cat ~/.bashrc | grep PS1 # 查看定义当前提示字符串的环境变量
    slynux@localhost: ~$ PS1="zjl>" # 提示字符串已经改变
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • PATH 变量列出了一系列可供shell搜索特定应用程序的目录。$PATH 通常定义在/etc/environment、/etc/profile或~/.bashrc中
    • 使用单引号时,变量不会被扩展(expand),仍依照原样显示。这意味着 echo ‘$var’ 会显示 $var 。但如果变量 $var 已经定义过,那么 echo “$var” 会显示出该变量的值;如果没有定义过,则什么都不显示。
    • 可以利用 PS1 环境变量来定义主提示字符串。默认的提示字符串是在文件~/.bashrc中的某一行设置的。 【①可以使用特定的转义序列定义特殊颜色的提示字符串(\e[1;31);②某些特殊的字符可以扩展为系统参数(\u: 用户名;\h:主机名;\w:当前的工作目录)】

    1.4 使用函数添加环境变量

    环境变量通常保存了可用于搜索可执行文件、库文件等的路径列表。

    将指定路径添加到指定环境变量开头:

    # 最基本的方式
    export PATH=/opt/myapp/bin:$PATH 
    
    # 通过使用函数实现【可以将如下函数定义在 .bashrc 文件中】
    prepend() { [ -d "$2" ] && eval $1=\"$2':'\$$1\" && export $1; } 
    prepend PATH /opt/myapp/bin
    
    # 改进的函数添加方式
    prepend() { [ -d "$2" ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ; } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 第一个函数工作原理:函数 prepend() 首先确认该函数第二个参数所指定的目录是否存在。如果存在, eval 表达式将第一个参数所指定的变量值设置成第二个参数的值加上 : (路径分隔符),随后再跟上第一个参数的原始值。
    • 第一个函数的添加方式如果第一个参数为空的时候,则会在路径的末尾多添加一个路径分隔符 :,解决方法是使用 ${parameter:+expression} 表达式,当且仅当 parameter 有值且不为空的时候才会使用 expression 表达式

    1.5 使用 shell 进行数学运算

    Bash shell 使用 let 、 (( )) 和 [] 执行基本的算术操作。工具 expr 和 bc 可以用来执行高级操作。

    基本操作:只用于整数

    #!/bin/bash
    # 赋值
    a=1;
    b=2;
    
    # let:只用于整数
    let c=a+b; # 等号和加号前后都不能有空格
    let a++;
    let a+=6; # += 前后也不能有空格
    
    # [] 操作符:只用于整数
    c=$[ a + b ] # 等号前后不可以有空格,[] 号里边可以有
    c=$[ $a + $b ]
    c=$[ $a + 6 ]
    
    # (()) 类似于 [] 操作符:只用于整数
    c=$(( a + b )) # 等号前后不可以有空格,(()) 号里边可以有
    c=$(( $a + $b ))
    c=$(( $a + 6 ))
    
    # expr:只用于整数
    re=`expr 3 + 4`
    re=`expr $a + 4`
    re=$(expr $a + $b)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    可适用于浮点数

    #!/bin/bash
    
    # bc 可用于浮点数
    echo "4 * 0.56" | bc # 2.24
    
    no=54;  
    result=`echo "$no * 1.5" | bc` # 81.0
    
    # 设置答案精度 scale
    echo "scale=2;22/7" | bc # 3.14
    
    # 进行进制转换
    no=100 
    echo "obase=2;$no" | bc # 1100100 
    no=1100100 
    echo "obase=10;ibase=2;$no" | bc # 100
    
    # 平方和平方根
    echo "sqrt(100)" | bc #Square root 
    echo "10^10" | bc #Square 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.5 文件描述符与重定向

    文件描述符是与输入和输出流相关联的整数。最广为人知的文件描述符是 stdin 、stdout和 stderr 。我们可以将某个文件描述符的内容重定向到另一个文件描述符中。

    在编写脚本的时候会频繁用到标准输入( stdin )、标准输出( stdout )和标准错误( stderr )。脚本可以使用大于号将输出重定向到文件中。命令产生的文本可能是正常输出,也可能是错误信息。默认情况下,正常输出( stdout )和错误信息( stderr )都会显示在屏幕上。

    文件描述符是与某个打开的文件或数据流相关联的整数。文件描述符 0 、 1 以及 2 是系统预留的。

    • 0 —— stdin(标准输入)
    • 1 —— stdout(标准输出)
    • 2 —— stderr(标准错误输出)

    如何判断一条命令是否执行成功

    当一个命令发生错误并退回时,它会返回一个非0的退出状态;而当命令成功完成后,它会返回为0的退出状态。退出状态可以从特殊变量 $? 中获得(在命令结束之后立刻运行 echo $? ,就可以打印出退出状态)。

    1.5.1 实验

    # 使用大于号将文本保存到文件
    echo "zjl" > zjl.txt
    
    # 将文本追加到文件
    echo "sdy" >> zyl.txt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • >:如果目标文件中已经存在,会将文件中的内容进行清空
    • >>:不会将原有文件的内容清空,而是追加到末尾

    # 将标准错误输出重定向到文件
    cmd 2> out.txt
    
    # 将标准错误输出和标准输出重定向到不同的文件
    cmd 2>stderr.txt 1>stdout.txt
    
    cmd > output.txt 2>&1
    
    cmd &> output.txt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • cmd:表示执行的命令
    • 注意 &> 和 >& 顺序不同
    • 如果不想看到或保存错误信息,那么可以将 stderr 的输出重定向到/dev/null。

    1.5.1.1 tee 命令

    我们在处理一些命令输出的同时还想将其保存下来,以备后用。 stdout 作为单数据流(single stream),可以被重定向到文件或是通过管道传入其他程序,但是无法两者兼得。

    使用 tee 命令,可以将数据重定向到多个文件,还可以提供一份重定向数据的副本作为管道中后续命令的 stdin 。

    command | tee FILE1 FILE2 | other_command

    需要注意的是 tee 命令只可以从 stdin 中读取数据,并且默认情况下 tee 会首先将原始文件的内容清空,使用 -a 选项可以在文件末尾追加内容。

    1.5.2 将文件重定向到命令

    借助小于号( < ),我们可以像使用 stdin 那样从文件中读取数据:

    echo "-e" > zjl.txt
    # ps < zjl.txt 是无效的,zjl.txt 会作为标准输入,但是 ps 不接受标准输入
    ps $(< zjl.txt) # zjl.txt 作为标准输入,$(...)从标准输入中读取内容作为参数
    
    # 以下均是 ok 的
    cat zjl.txt # zjl.txt 作为参数,cat 会查找这个文件,然后读取,显示
    cat < zjl.txt # zjl.txt 作为标准输入,cat 从标准输入中读取数据显示
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    标准输入和命令行参数的区别:【为什么上述 ps 命令需要使用 $( … ),而如果是 cat 命令却不需要】

    • 标准输入就是编程语言中诸如scanf或者readline这种命令读取数据的地方;而命令行参数是指程序的main函数传入的args字符数组。

    • 管道符和重定向符是将数据作为程序的标准输入,而$(cmd)是读取cmd命令输出的数据作为参数。

    • 对于某些命令来说,不接受标准输入,只接收命令行参数(eg. ps 命令);而有的命令即接受标准输入,也可以接受命令行参数(eg. cat 命令)

    参考链接:

    1.5.3 自定义文件描述符

    文件描述符是一种用于访问文件的抽象指示器(abstract indicator)。存取文件离不开被称为“文件描述符”的特殊数字。 0 、 1 和 2 分别是 stdin 、 stdout 和 stderr 预留的描述符编号。

    exec 命令创建全新的文件描述符。

    • 只读模式
    • 追加写入模式
    • 截断写入模式

    < 操作符可以将文件读入 stdin 。 > 操作符用于截断模式的文件写入(数据在目标文件内容被截断之后写入)。 >> 操作符用于追加模式的文件写入(数据被追加到文件的现有内容之后,而且该目标文件中原有的内容不会丢失)。文件描述符可以用以上3种模式中的任意一种来创建。

    截断写入模式的时候,有以下特点:

    • 创建一个截断写入模式的描述符时会将该文件的内容清空
    • 在该模式下,会从上一次写入结束位置开始,截取指定长度(新写入内容的长度)进行覆盖
    # 创建使用只读模式文件描述符
    echo this is a test line > input.txt 
    exec 3<input.txt 
    cat<&3
    
    # 创建并使用截断写入模式文件描述符
    exec 4>output.txt 
    echo newline >&4 
    cat output.txt 
    
    # 创建并使用追加写入模式文件描述符
    exec 5>>input.txt 
    echo appended line >&5 
    cat input.txt  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 读取写入文件不需要 &,但文件描述符需要
    • 只读文件描述符只能使用一次,如果要再次读取,我们就不能继续使用文件描述符 3 了,而是需要用 exec 重新创建一个新的文件描述符(可以是 4 )来从另一个文件中读取或是重新读取上一个文件。

    1.5.4 总结

    重定向操作符( > 和 >> )可以将输出发送到文件中,而不是终端。>和 >> 略有差异。尽管两者都可以将文本重定向到文件,但是前者会先清空文件,然后再写入内容,而后者会将内容追加到现有文件的尾部。 默认情况下,重定向操作针对的是标准输出。如果想使用特定的文件描述符,你必须将描述符编号置于操作符之前。

    > 等同于 1> ;对于 >> 来说,情况也类似(即 >> 等同于 1>> )。

    处理错误时,来自 stderr 的输出被倾倒入文件/dev/null中。./dev/null是一个特殊的设备文件,它会丢弃接收到的任何数据。null设备通常也被称为黑洞,因为凡是进入其中的数据都将一去不返。

    1.6 数组与关联数组

    数组允许脚本利用索引将数据集合保存为独立的条目。Bash支持普通数组和关联数组,前者使用整数作为数组索引,后者使用字符串作为数组索引。

    普通数组

    # 使用数值列表定义数组
    array_var=(test1 test2 test3 test4)
    
    # 通过索引的方式赋值
    array_var[0]="test1" 
    array_var[1]="test2" 
    array_var[2]="test3" 
    array_var[3]="test4" 
    array_var[4]="test5" 
    array_var[5]="test6" 
    
    # 打印数组的值
    echo ${array_var[0]}  # test1 
    index=5 
    echo ${array_var[$index]} # test6 
    
    # 打印数组的全部值
    echo ${array_var[*]} 
    echo ${array_var[@]} 
    
    # 打印数组长度
    echo ${#array_var[*]} # 6 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    关联数组(Bash 4.0 开始支持):类似字典

    # 首先需要声明关联数组
    declare -A ass_array 
    
    # 添加元素的方式
    ass_array=([index1]=val1 [index2]=val2) # 一行初始化
    
    ass_array[index1]=val1 
    ass_array[index2]=val1 # 按索引赋值
    
    # 例子:定义水果价格
    declare -A fruits_value 
    fruits_value=([apple]='100 dollars' [orange]='150 dollars') 
    echo "Apple costs ${fruits_value[apple]}"  # Apple costs 100 dollars 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    显示数组索引:

    echo ${!array_var[*]} 或者 echo ${!array_var[@]} ;对于普通数组和关联数组均有效。

    相关命令

    # 输入输出
    echo
    cat
    printf
    
    # 权限管理
    chmod
    
    # 字符串操作
    
    # 对来自标准输入的字符进行替换、压缩和删除。
    # -c或——complerment:取代所有不属于第一字符集的字符;
    # -d或——delete:删除所有属于第一字符集的字符;
    # -s或--squeeze-repeats:把连续重复的字符以单独一个字符表示;
    # -t或--truncate-set1:先删除第一字符集较第二字符集多出的字符。
    tr(选项)(参数)
    
    # shell 相关
    echo
    exec
    shopt
    
    # 环境变量
    env
    printenv
    
    # 进程
    
    # 以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
    # -o:仅显示找到的最小(起始)进程号;
    # -n:仅显示找到的最大(结束)进程号;
    # -l:显示进程名称;
    # -P:指定父进程号;
    # -g:指定进程组;
    # -t:指定开启进程的终端;
    # -u:指定进程的有效用户ID。
    pgrep(选项)(参数)
    
    # 重定向
    tee 命令
    
    # 创建文件描述符
    exec 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    除。

    -c或——complerment:取代所有不属于第一字符集的字符;

    -d或——delete:删除所有属于第一字符集的字符;

    -s或–squeeze-repeats:把连续重复的字符以单独一个字符表示;

    -t或–truncate-set1:先删除第一字符集较第二字符集多出的字符。

    tr(选项)(参数)

    shell 相关

    echo
    exec
    shopt

    环境变量

    env
    printenv

    进程

    以名称为依据从运行进程队列中查找进程,并显示查找到的进程id
    -o:仅显示找到的最小(起始)进程号;
    -n:仅显示找到的最大(结束)进程号;
    -l:显示进程名称;
    -P:指定父进程号; # -g:指定进程组; # -t:指定开启进程的终端; # -u:指定进程的有效用户ID。

    pgrep(选项)(参数)

    重定向

    tee 命令

    创建文件描述符

    exec

  • 相关阅读:
    Bytebase数据库 Schema 变更管理工具
    金和OA SQL注入漏洞
    Web标准
    【交付高质量,用户高增长】-用户增长质量保证方法论 | 京东云技术团队
    PAT 1027 Colors in Mars
    介孔二氧化硅纳米球 Mesoporous silica nanosphere 的介孔二氧化硅纳米球
    web前端面试-- js深拷贝的一些bug,特殊对象属性(RegExp,Date,Error,Symbol,Function)处理,循环引用weekmap处理
    Qt的定时器QTimer
    操作系统体系结构和OS
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java双笙映画ou5oj
  • 原文地址:https://blog.csdn.net/weixin_43786143/article/details/126390647