• 【系统性学习】Linux Shell易忘重点整理


    本文主要基于《实用Linux Shell编程》总结,并加入一些网上查询资料和博主自己的推断。
    其中命令相关的,已抽取出来在另一篇系统性学习】Linux Shell常用命令中,可以一起使用。

    一、基础知识

    1. Linux 系统主要目录及简单描述
      在这里插入图片描述
      在这里插入图片描述
    目录描述
    /binbin 是 Binaries (二进制文件) 的缩写, 这个目录存放着最经常使用的命令
    /boot内核及其他系统启动时需要的文件,包括一些连接文件以及镜像文件
    /devdev 是 Device(设备) 的缩写, 存放的Linux 的外部设备,Linux把所有外设都看做是一个文件,对文件的操作就是对外部设备的操作
    /etcetc 是 Etcetera(等等) 的缩写,用来存放所有的系统管理所需要的配置文件,该目录及子目录下有很多.conf文件
    /home用户的主目录,系统默认的普通用户主目录为/home/,如上图中的 alice、bob 和 eve,保存用户自己的配置文件、文档、数据等
    /liblib 是 Library(库) 的缩写,存放着系统最基本的动态连接共享库文件(由/bin和/sbin使用),其作用类似于 Windows 里的 DLL 文件。几乎所有的应用程序都需要用到这些共享库。/user/lib中包含更多用于用户程序的库文件
    /lost+found这个目录一般情况下是空的,当系统非法关机后,这里就存放了一些文件,用于系统修复和恢复
    /mnt系文件系统挂载点,一般用于安装移动介质,其他文件系统的分区、网络共享文件系统或者任何可安装的文件系统。比如可以将光驱挂载在 /mnt/ 上,然后进入该目录就可以查看光驱里的内容了。
    /optopt 是 optional(可选) 的缩写,主要由第三方开发者用于安装和卸载他们的软件包。比如你安装一个ORACLE数据库或者spark就可以放到这个目录下。默认是空的。
    /procproc 是 Processes(进程) 的缩写,是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,这个目录是一个虚拟的目录,它是系统内存的映射,我们可以通过直接访问这个目录来获取系统信息(如 /proc/version)。这个目录的内容不在硬盘上而是在内存里,我们也可以直接修改里面的某些文件,比如可以通过下面的命令来屏蔽主机的ping命令,使别人无法ping你的机器:echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
    /root该目录为系统管理员,也称作超级权限者的默认主目录。
    /sbins就是 Super User 的意思,是 Superuser Binaries (超级用户的二进制文件) 的缩写,这里存放的是超级用户管理系统时使用的系统管理程序。普通用户几乎没有权限执行这里面的命令。/user/sbin是超级用户使用的比较高级的管理程序和系统守护程序
    /tmptmp 是 temporary(临时) 的缩写这个目录是用来存放一些临时文件的。当系统重启时,改目录的文件会被自动清空
    /usrusr 是 unix shared resources(共享资源) 的缩写,这是一个非常重要的目录,用户的很多应用程序和文件都放在这个目录下,类似于 windows 下的 program files 目录。
    /varvar 是 variable(变量) 的缩写,这个目录中存放着在不断扩充着的东西,我们习惯将那些经常被修改的目录放在这个目录下。包括各种日志文件。
    1. ls -l 详情解释:
    -rw-------.  1 root root 1.3K Dec 14 01:38 anaconda-ks.cfg
    dr-xr-xr-x. 17 root root  224 Dec 14 01:38 bin
    
    • 1
    • 2
    • 第一列共10位,第1位表示文档类型,d表示目录,-表示文件,l表示链接文件,d表示可随机存取的设备,如U盘等,c表示一次性读取设备,如鼠标、键盘等。后9位,依次对应三种身份所拥有的权限,身份顺序为:owner、group、others,权限顺序为:readable、writable、excutable
    • 第二列,对于文件表示硬链接数,表示有多少个文件链接到inode号码。对于目录表示子目录数(包括隐藏文件),所有目录都包含".“和”…"两个隐藏目录,所以目录的第二列>=2
    • 第三列表示拥有者,user
    • 第四列表示所属群组,group
    • 第五列表示文档容量大小
    • 第六列表示文档最后修改时间,注意不是文档的创建时间
    • 第七列表示文档名称。以点(.)开头的是隐藏文档
    1. 账户权限说明:对于文件,读:可以查看 写:可以修改 执行:可以运行。对于目录,读:可以浏览,用ls 写:可以创建和删除文件 执行:可以进入该目录。
    2. SUID:有的可执行二进制文件的账户权限为rws,s是SUID(即set uid),表示其他账户在执行时,有文件所有者的权限。如passwd命令会改/etc/shadow文件,这个文件只有root用户可以修改。但其他账户同样可以执行passwd修改该文件。原因就是passwd文件是s权限。chmod u+s filechmod 4755 file添加该权限。rwS显示S是不正常的,给文件添加x权限才会正常。SUID仅对可执行二进制文件起作用。
    3. SGID:有的可执行二进制文件的所属组权限为rws,,s是SGID(即set gid)表示其他账户在执行时,具有文件所属组的权限。对目录设置SGID,则作用为:任何账户如果可以在该目录内建立新文件或新的子目录,那么新文件和子目录的所属组与该目录的所属组保持一致。SGID属性要保证目录或文件对所属组由x权限,否则为大写S。chmod g+s filechmod 2755 file添加该权限
    4. SBIT:粘滞位,t是SBIT只有目录可以设置,作用是:在一个大家都有权限的目录下,账户不能删除别人的文件或目录。chmod o+t filechmod 1755 file添加该权限。SBIT要求其他账户有执行权限,否则为大写T。

    二、命令与环境

    1. linux命令分为内置命令和外部命令:help查看所有内置命令和关键字列表, help command查看内置命令详情。外置命令:which command查看外置命令位置,which无法对内置命令生效。command --help显示外置命令详情。
    2. 内置变量PS1,主命令提示参数。重新登陆shell则恢复。永久保持写入~/.bashrc中。
    \h 计算机名
    \t 当前时间
    \u 账户名
    \w 当前路径
    > PS1="\h@\u@\t$" #输入
    host-10-31-17-80@user_client@16:17:03$ #提示符从>改变了
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 搜索路径PATH,路径用:隔开,按顺序先后查找,找到就停止。在原路径后面加路径PATH='$PATH:/tmp/bin'
    2. 要永久改变环境变量,将修改写进~/.bashrc文件中。
    3. 权限掩码umask
    4. source和点命令:souce file. file等价,如果file不在当前目录下,会在$PATH下找。
    5. 直接执行脚本文件和source都能执行脚本中的命令。差异:a. 脚本在子shell中运行,而source是在父shell b. 直接运行脚本需要脚本有x权限,source不需要
    6. 命令解释顺序及改变。命令类型查看用type command
    alias->keywords->function->built-in->$PATH
    别名->关键字->函数->内置命令->外部命令
    
    • 1
    • 2
    • command <命令> 禁用别名和函数,先处理内置命令和外部命令
    • builtin <命令> 只查找内置命令
    • enable 禁用(-n)和使能内置命令(尽量少用,要避免自己的编写的命令和内置命令重名)
    1. 命令或程序结束后会返回一个退出状态,取值0-255,0表示成功执行。内置变量$?存了上一条命令的退出状态 。
    2. 内置命令true用于返回成功的状态,false则是失败。
    3. 管道左侧|右侧,理解成左侧命令的输出,会给到右侧命令的输入,所以右侧命令不需要写代表输入的参数。
    4. 执行一个shell命令会自动打开标准输入文件(stdin)和标准输出文件(stdout),他们默认对应终端键盘和终端屏幕,分别对应文件描述符0和1。文件描述符(file descriptor,fd)是进程对其打开文件的索引,形式上是个非负整数。
    > ls -l /dev/std*
    lrwxrwxrwx 1 root root 15 Aug 19  2015 /dev/stderr -> /proc/self/fd/2
    lrwxrwxrwx 1 root root 15 Aug 19  2015 /dev/stdin -> /proc/self/fd/0
    lrwxrwxrwx 1 root root 15 Aug 19  2015 /dev/stdout -> /proc/self/fd/1
    > ls -l /proc/self/fd/*
    lrwx------ 1 user_client users 64 Oct 22 17:00 /proc/self/fd/0 -> /dev/pts/0
    lrwx------ 1 user_client users 64 Oct 22 17:00 /proc/self/fd/1 -> /dev/pts/0
    lrwx------ 1 user_client users 64 Oct 22 17:00 /proc/self/fd/2 -> /dev/pts/0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Linux下的tty和pts详解
    概述Linux TTY/PTS的区别
    简单的说,/dev/pts/0 可以当做一个终端,这个终端连接了输入和输出,所以fd/0(标准输入)、fd/1(标准输出)、fd/2(标准错误)都可以连到这个终端上来。

      Input  +--------------------------+  R/W   +------+
    ----------->|             |<---------->| bash |
          |     pts/1      |      +------+
    <-----------|             |<---------->| lsof |
      Output  | Foreground process group |  R/W   +------+
          +--------------------------+                
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 输入重定向: 命令 输入->命令 < 文件名(不能是带输出的命令)
    2. 输出重定向: 命令(能产生输出)->命令(能产生输出) >文件名命令(能产生输出) >>文件名追加模式。> 文件名可以创建空文件或者清空文件。输出重定向是不安全的。如果想不覆盖源文件,可开启noclobber选项,set -o noclobber
    3. 标准错误输出(stderr),对应fd/2,普通重定向>不起作用。需要 2> 实现重定向。
    4. 同时处理,1>file1 2>file2标准到file1,错误到file2,>file 2>&1同时输出到file(等价 &> file>& file)。
    5. “黑洞”,/dev/null一般用作垃圾箱,把不要的输出重定向到这里,如2> /dev/null
    6. 一行多命令,用;隔开。
    7. 后台执行:command &,执行后会输出 [工作号] 进程号
    8. 续行:命令太长可用\来分行,PS2控制续行提示符。
    9. 特殊文件名处理:空格前面加\,-a.txt这样-开头的,用vi -- -a.txtvi ./-a.txt。因为-后面会被认为是选项。Bash规定--(空格)后面的东西不是选项,而是文件名或参数。
    10. 小括号,大括号,中括号

    三、变量和数组

    1. Bash只有字符串类型。整数型字符串赋值被变量时,变量相当于多了个整数属性,可以进行整数运算。
    2. 变量定义时,等号左右两边不能有空格。引用变量时,前面加$
    3. 双引号作用,保留空格。a= b=" ",a是为空的,b才能保留空格。
    • 单引号:相当于raw字符串:被单引号括起的内容不管是常量还是变量者不会发生替换。
    • 双引号:把双引号内的内容输出出来;如果内容中有命令、变量等,会先把变量、命令解析出结果,发生替换,然后在输出最终内容来。
    • 不加引号:不会将含有空格的字符串视为一个整体输出, 如果内容中有命令、变量等,会先把变量、命令解析出结果,然后在输出最终内容来,如果字符串中带有空格等特殊字符,则不能完整的输出,需要改加双引号,一般连续的字符串,数字,路径等可以用。
    1. 取命令执行结果:`命令`,$(命令)都可以取命令输出的结果。要保留换行符、首尾空格等符号,则用"$(命令)"。可以把()当做子shell,里面的操作不会对父shell产生影响。少用反引号,因为$()便于嵌套。注意对比取变量${变量}和取命令输出结果$(命令)
    # 测试父shell中变量
    > x=10
    > b=$(echo $x)
    > echo b=$b x=$10
    b=10 x=0
    > b=$(x=5;echo $x)
    > echo b=$b x=$x
    b=5 x=10 --父shell中的x没被改变
    
    # 测试嵌套
    > time=$(echo Today is $(date))
    > echo $time
    Today is Sun Oct 23 14:06:37 CST 2022
    
    # 取变量和取命令
    > aa=${date}
    > echo aa
      --空,因为变量date没有定义
      
    # 测试定义命令同名变量,不会影响命令执行
    > date=3 #同名变量
    > t1=$(date)
    > t2=$date
    > t3=${date}
    >  echo -e "t1=$t1\nt2=$t2\nt3=$t3"
    t1=Sun Oct 23 14:11:21 CST 2022
    t2=3
    t3=3
    
    # 反斜杠取消符号原有功能
    > echo "`date`"
    Sun Oct 23 14:19:05 CST 2022
    > echo "\`date\`"
    `date`
    > echo "$(date)"
    Sun Oct 23 14:19:22 CST 2022
    > echo "\$(date)"
    $(date)
    
    • 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
    1. 整型计算:放在双括号间或let后面。$((表达式))可取出表达式计算结果。单独的((表达式))仅仅是计算,而没有输出。i++(先用后加),++i先加后用,支持+=,-=,*=,/=,%=,^=等运算。支持算木运算((条件?结果1:结果2))$(())可替换成$[],注意,有$时才能替换。另外,declare -i var定义了整型变量后,var=表达式不需要括号也可进行整型计算。
    # 命令行理解
    w=date
    > echo $w
    date
    > $w  #直接被执行
    Sun Oct 23 16:03:32 CST 2022
    
    # 表达式结果
    > echo $((50/20))
    2
    > echo $((w=50/20))
    2
    > echo $w
    2
    > echo $[a=10+5]
    15
    > echo $a
    15
    
    # 整型变量
    >declare -i v
    >v=$a+$w
    >echo $v
    30
    >v=a+10 # 甚至不需要引用
    >echo $v
    25
    
    • 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
    1. 浮点运算:用bc或者awk。echo "scale=5;表达式|bc"或者直接bc开客户端运算,quit退出。
    2. 其他进制:bash支持其他进制运算,只要在算数表达式能生效的地方都可以使用。两种书写方法,a. 基数#数值,基数可取2到64。b. 0开头表示8进制,0x开头表示十六进制:
    ((w=8#16)) #8进制的16赋值给w
    ((w=016))  #等价
    ((w=16#25)) #16进制的25赋值给w
    ((w=0x25))  #等价
    
    • 1
    • 2
    • 3
    • 4
    1. 数组:bash只支持一维数组,下标默认从0开始。单独引用数组名时,返回数组第一个元素。打印数组全部详细信息,用 declare -p 数组名
    # 定义
    y=(aa bb cc)
    y=([1]=aa [3]=bb [5]=cc) # 非连续下标定义
    >echo $y
    aa
    > declare -p y
    declare -a y='([0]="aa" [1]="bb" [2]="cc")'
    
    # 引用,${数组名[下标]}
    echo $y  # 打印数组第一个元素
    echo ${y[1]}
    echo ${y[*]} #得到数组所有元素,作为一个整体
    echo ${y[@]} #得到数组所有元素,作为一个个个体
    echo ${#y[*]} #得到数组个数
    echo ${!y[*]} #得到数组所有下标
    # 增加或修改元素
    y[3]=dd
    y[2]=cc2
    # 删除元素或数组
    unset y[1]
    unset y
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    1. 关联数组(其他语言里的map):Bash4以上版本才支持。使用和数组类似,定义如下:
    declare -A age=([Mike]=12 [Jack]=24 [Tom]=30)
    # 显示详情
    declare -p age
    
    # 取建
    echo ${!age[@]}
    # 取值
    echo ${age[@]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. Bash中的特殊变量。P118
    变量含义
    $0脚本自身的名字,或者shell的名字
    $N脚本或函数的位置参数,$1,$2,...${10},注意大于9要用{}包起来
    $#位置参数个数,不包括$0
    $*所有位置参数(整体作为一个字符串),不包括$0
    $@所有位置参数(每个作为独立字符串),不包括$0
    $?上一条命令退出状态
    $$当前shell进程ID
    $!最后一个命令的进程ID
    $-当前shell的选项,若echo $- 的输出中包含i,则表示是交互式shell,包含C则表示noclobber开启
    $_上条命令的最后一个参数
    1. set --清除所有位置参数,不包括$0
    2. 父shell与子shell及其进程ID
    3. 一些常用内置变量(或者说环境变量)P121
    变量含义
    BASHbash的完整路径,默认为/bin/bash
    BASH_ENV非交互式分登陆模式中(比如执行shell脚本时),先执行一遍该变量下指定的脚本
    CDPATH命令cd的搜索路径,多个路径用:隔开,用于减少输入全路径的情况
    DISTACK当前目录栈存放的数组,配合poshd,popd,dirs使用
    FUNCNAME当某函数被调用时,该变量为函数名;实际上它是数组,记录调用链上所有的函数名
    GLOBIGNORE要忽略的通配模式列表,冒号分割,定义了文件名扩展时(通配符模式下)要忽略的文件名集合,本身也支持通配符,例如GLOBIGNORE=a*;b*会使得ls *忽略所有a和b开头的文件
    HISTFILE存放命令历史的文件,通常为~/.bash_history
    HISTFILESIZE命令历史文件保存的最大行数
    HISTIGNORE不需保存的历史命令序列,多个通配符模式列表组成,由冒号分隔。用冒号隔开。如HISTIGNORE=ls:t*:\&,将忽略ls命令,和t开头的命令,并且&表示连续输入的相同命令只被记录一次,赋值时需要用\屏蔽其将命令后台挂起含义
    HOME用户主目录,通常为/home/用户名,不要改,会影响cdcd ~的结果
    LINENO脚本中当前行号
    OLDPWD前一个工作目录,cd -等价于cd $OLDPWD
    OPTARG存放getopts参数的值
    OPTIND待处理的下一个getopts参数索引,初始值为1
    PATH外部命令搜索路径,多个以冒号隔开
    PPID父进程(父shell)的进程ID
    PWD当前工作目录
    RANDOM0-32767之间一个随机数
    REPLYread不加变量时,变量缓存。select 用户选择项缓存
    SECONDS当前shell的启动时间
    SHELL登陆linux后的默认shell,/bin/bash比较常用
    SHELLOPTSshell的选项,冒号隔开,例:braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
    SHLVL第一次打开一个shell终端,值为1,没进入一层子shell,值增加1
    TMOUT如果该值大于0,当前shell在等待TMOUT秒后没有任何输入就会自动退出
    BASH_SOURCE更稳健地获取自身脚本路径。BASH_SOURCE是数组,不过它的第一个元素是当前脚本的名称。这在source的时候非常有用,因为在被source的脚本中,$0是父脚本的名称,而不是被source的脚本名称。而BASH_SOURCE就可以派上用场了。还可和FUNAME配合打印哪个脚本调用了哪个函数。
    BASH_SUBSHELL检查当前环境是不是在subshell中,这个值在非subshell中是0;每进入一层subshell就加1
    1. 常用变量赋值表达式。下面的Default可以是变量,取$变量。var也可以是0(不能赋值)或者位置变量1,2,3…10(不能赋值)。
    表达式含义
    ${var:-Default}若var未定义或为空值,则表达式的值为Default,var不变
    ${var:=Default}若var未定义或为空值,则表达式的值为Default,var赋值Default
    ${var:?Message}若var未定义或为空值,则打印Message且后续代码不再执行,var不变。
    ${!pre*}匹配之前所有声明的以pre开头的变量
    ${!pre@}匹配之前所有声明的以pre开头的变量
    1. 常用内置字符串操作(支持通配符)。sed和awk也有字符串处理函数,但内置操作最快。下表中的str可替换成数组名[*],则表达式对于数组每个元素做处理。expr命令也可处理字符串,支持正则表达式。注意,str必须变量名。
    表达式含义
    ${#str}字符串str的长度
    ${str:pos}从str的pos位置提取子串,到结尾
    ${str:pos:len}从str的pos位置提取长度为len的子串
    ${str#glob}从str的开头,删除最短匹配的通配符模式glob
    ${str##glob}从str的开头,删除最长匹配的通配符模式glob
    ${str%glob}从str的结尾,删除最短匹配的通配符模式glob
    ${str%%glob}从str的结尾,删除最长匹配的通配符模式glob
    ${str/glob/replacement}用replacement代替第一个glob,最长匹配
    ${str//glob/replacement}用replacement代替所有glob,最长匹配。
    ${str/#glob/replacement}用replacement代替第一个包含开头(从字符串开头算起)的glob,最长匹配
    ${str/%glob/replacement}用replacement代替第一个包含结尾(从字符串结尾算起)的glob,最长匹配
    > a=(one on1 tow)
    > echo ${a[*]#o*}
    ne n1 tow
    > echo ${a[*]##o*}
    tow
    > b=${a[*]##o.*}
    >  declare -p b
    declare -- b="one on1 tow"
    > echo ${a[@]##o*}
    tow
    > c=${a[@]##o*}
    > declare -p c
    declare -- c="tow"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    四、条件流程控制

    1. 条件判断命令:test 条件表达式[ 条件表达式 ],中括号前后一定要有空格。$?取命令退出结果来看条件是否满足。满足则退出结果为0,否则为1。
    2. 整型关系运算:
    条件表达式含义
    [ int1 -lt int2 ]int1小于int2
    [ int1 -le int2 ]int1小于等于int2
    [ int1 -gt int2 ]int1大于int2
    [ int1 -ge int2 ]int1大于等于int2
    [ int1 -eq int2 ]int1等于int2
    [ int1 -ne int2] int1不等于int2
    1. 字符串关系运算,注意需要加\转义,且str最好用""保护首尾的空格。
    条件表达式含义
    [ str1 == str2 ]str1等于str2,也可以用str1=str2
    [ str1 != str2 ]str1不等于str2
    [ str1 > str2 ]字典序,str1大于str2,str1在str2后面
    [ str1 < str2 ]字典序,str1小于str2,str1在str2前面
    [ -z str ]str为空串zero,长度为0
    [ -n str ]str非空not-zero,长度大于0
    [ str ]同上
    [[ str =~ regex ]]str是否包含(子串关系)正则表达式regex,注意=~只能在双中括号中使用,若regex用引号括起来,则当做普通字符串。
    1. 文件属性判断,
    条件表达式含义
    [ -a file ]file存在
    [ -e file ]file存在,同a
    [ -s file ]file存在且非空(字节数大于0)
    [ -d file ]file存在且是一个目录
    [ -f file]file存在且是一个普通文件
    [ -h file]file存在且是一个符号链接
    [ -L file]file存在且是一个符号链接,同h
    [ -p file]file存在且为已命名管道
    [ -u file ]file存在且设置了SUID
    [ -g file ]file存在且设置了SGID
    [ -k file ]file存在且设置了粘滞位
    [ -r file ]file存在且当前用户有读权限
    [ -w file ]file存在且当前用户有写权限
    [ -x file ]file存在且当前用户有执行权限,如果是目录,则有进入该目录权限
    [ -N file ]file存在且最后一次读取后有更改
    [ -O file ]file存在且当前用户是该文件所有者
    [ -N file ]file存在且当前用户所属组是该文件所属组
    [ -t FD]文件描述符打开,并指向一个终端
    [ f1 -nt f2 ]f1修改时间比f2新newer than,或f1存在且f2不存在
    [ f1 -ot f2 ]f1修改时间比f2旧older than,或f2存在且f1不存在
    [ f1 -ef f2 ]f1和f2有相同的设备和节点号(互为硬链接)
    1. 逻辑与:[ 条件表达式1 -a 条件表达式2 ],逻辑或:[ 条件表达式1 -o 条件表达式2 ],逻辑非:[ ! 条件表达式 ]。test同样。优先级:非、与、或,多条件可用(),括号最优先。
    2. 双中括号代替test或[].[[]]使得P146:
    • a. (,<等不用加反斜杠;
    • b. 保留字符串首尾有空格时,也不需要加双引号
    • c. 判断字符串相等或者不等时,右侧支持通配模式。*代表0或多个字符,?代表一个字符
    • d. 支持&&|| 表达 代替 -a ,-o
    1. 双小括号助力整数条件运算。(())使用场景:
      I. 计算表达式,((表达式))等价于 let 表达式 。表达式结果不是逻辑0或1的时候,该表达式不报错的退出状态永远是0。
      II. 上述表达式可以是整数相关的条件表达式,使得
      a. (,<等不用加反斜杠;
      b. >,<,!=可应用于整数,并且>=,<=,==也可使用
      c. 支持&&|| 表达 代替 -a ,-o

    2. 命令的与或非:
      I. 与:命令1 && 命令2 ,命令结果都为0,才为0。若命令1结果为非0,则命令2不再执行。可用来实现命令依赖。
      II. 或:命令1 || 命令2 ,命令结果有一个为0,则为0。若命令1结果为0,则命令2不再执行。可用来按优先级至少执行一个命令。
      III. 非: ! 命令1 ,命令结果有一个为0,则为0。若命令1结果为0,则命令2不再执行。可用来按优先级至少执行一个命令。

    # 样例
    >[-r a.txt -a -w b.txt ] #判断文件存在和权限
    >echo $?
    0
    
    # 优先级
    > x=5;y=10;z=20
    >[ \($x -eq 5 -o $y -gt 5\) -a $z -lt 5 ]
    > echo $?
    0
    # 双中括号
    >[[ ($x -eq 5 || $y -gt 5) && $z -lt 5 ]]
    > echo $?
    0
    # 双小括号
    >(( ($x == 5 || $y > 5) && $z < 5 ))
    > echo $?
    0
    
    # 双中括号字符串相等或不相等右侧通配模式
    >[[ $s1 == a* && $s2 != b? ]]
    
    # 命令与或非
    > [ 1 -eq 2 ] && [ 1 -lt 3 ] 
    > echo $?
    1
    
    • 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
    1. 判断变量是否有定义:[-v 变量名]test -v 变量名。结果为0则表示有定义。或者set,从结果中找,存在则表示有定义,但这种方式太繁琐。
    2. if控制结构,可嵌套使用。
    # if命令
    if 命令 #该命令的退出结果为0,则执行then后面的语句
    then
    	命令1
    	命令2
    	...
    if
    
    if 命令;then 命令1;命令2...;fi  # 单行模式
    
    #if-else 命令
    if 命令
    then 
    	命令(命令组)
    else #可以没有
    	命令(命令组)
    fi
    
    if 命令;then 命令(命令组);else 命令(命令组);fi
    
    #if-elif 命令
    if 命令1
    then 
    	命令(命令组)
    elif 命令2
    then 
    	命令(命令组)
    elif 命令3
    then 
    	命令(命令组)
    else  #可以没有
    	命令(命令组)
    fi
    
    • 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
    # 命令中的重定向
    if cp a.txt b.txt 2>/dev/null #重定向不影响命令退出状态
    then
     	echo “copy done”
    fi
    
    # if-else的替换
    if cmd1; then cmd2;else cmd3;fi
    cmd1 && cmd2 ||cmd3 # 有可能等价
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. case 控制结构,如下模式可以是1个,或者多个,多个用|分割,或关系。模式支持正则表达式。如[6-7][0-9]表示60~79。
    case $变量 in
    	模式1)
    		命令(命令组);;
    	模式2)
    		命令(命令组);;
    	...
    	*) #表示其他情况
    		命令(命令组);;
    esac
    # 样例
    case $score in
    	100)
    		echo "Full Mark";;
    	9[0-9])
    		echo "Excellent";;
    	8[0-5] | 8[6-9])
    		echo "Very Good";;
    	[6-7][0-9])
    		echo "Passing";;
    	*)
    		echo "Failed,or Error";;
    esac
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. exit的退出状态默认为前一条命令的退出状态。也可以exit N带整型参数,则退出状态为N,这个特点可以配合if(产生exit 不同结果)和case(对不同结果做处理)命令使用。
    2. here文档和case可模拟select命令的功能。详见常用命令here。
    # 结合case使用,模拟select命令
    cat << INPUT
    choose your role
    A)student
    B) teacher
    INPUT
    
    read var
    case $var in
    	A)
    		...;;
    	B) 
    	 	...;;
    	*)
    		echo "error";;
    esac
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    五、循环

    1. for控制流程,如下“”列表“”可以是字符串字面量,字符串变量,或位置参数枚举$*,$@,或数组枚举${数组名[*]${数组名[@]
      I. 为字符串时,元素分割符由IFS指定(Internal Filed Seperator,字段分割符),默认为“空白字符”,linux中的"空白字符"包括:空格、\t、换行\n、回车\r\n\r 是不同的:\r是指 在同一行中, 使光标回到该行的行首。\n是指 光标转到下一行)。IFS使用后要记得恢复。IFS可以是多个,表示任意一个做分割,如IFS=";:",表示:;都是分割符。
      II. $*,$@的表现受双引号限制,有引号时"$*"仍是一个整体,"$@"则各元素分别作为一个整体;没有引号时,$*,$@表现一样,每个元素都分别作为一个整体。
      III. ${数组名[*]${数组名[@]同上。
      IV. [in 列表]部分不是必须的,没有的时候相当于 for 变量 in "#@"
    for 变量 [in 列表]
    do
    	命令(命令组)
    done
    #  单行
    for 变量 [in 列表];do 命令(命令组);done
    
    
    # IFS注意点
    > OLD_IFS="$IFS"
    > IFS="$OLD_IFS" #用完后要恢复
    
    
    
    # IFS 测试
    > for a in 1 2 3;do echo  "${a}T";done
    1T
    2T
    3T  --不带引号的字符串,触发天然分割,但该分割不是由IFS指定。
    > for a in "1 2 3";do echo  "${a}T";done
    1 2 3T  --不触发天然分割和IFS分割
    > str2="1 2 3"
    > for a in $str2;do echo  "${a}T";done
    1T
    2T
    3T --变量引用触发IFS分割,当前分割符为默认的空白字符
    >IFS=":"
    > for a in "1:2:3";do echo "${a}T";done
    1:2:3T
    > str="1:2:3"
    > for a in $str;do echo  "${a}T";done
    1T
    2T
    3T  --变量引用触发IFS分割,当前分割符为:
    > for a in 1 2 3;do echo  "${a}T";done
    1T
    2T
    3T  --触发天然分割未受影响
    > echo $str
    1 2 3 --此时,echo得到的str结果可以看出,IFS分割已触发,分割后的元素用空格连接打印
    > for a in $str2;do echo  "${a}T";done
    1 2 3T --分割符已变,所以即使触发IFS分割,也分割不了
    > a=$str
    > declare -p str
    declare -- str="1:2:3"
    > declare -p a
    declare -- a="1:2:3"
    > echo "1:2"
    1:2
    
    
    
    所以,可以猜测,IFS只作用于引用变量的时候,即变量引用触发IFS分割,并将变量以IFS字符分割。
    in后面只看有多少被分割元素,不触发分割。
    
    # 引号下不同表现
    >cat test.sh
    for a in "$@";do echo $a;done
    
    >test.sh A B C
    A
    B
    C
    >cat test.sh
    for a in "$*";do echo $a;done
    >test.sh A B C
    A B C
    >若不加双引号, 即 for a in $*$@,结果都如下
    >test.sh A B C
    A
    B
    C  --可理解成,$*也触发天然分割,被切开了
    
    # for 常用大括号扩展
    for i in {1..10} # 或{a..z}, a{b,c}k,{10..1}等
    do 
    	...
    done
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    1. 算数for循环,三个表达式不是必须的,但;需要保留。循环条件为空时,表示无限循环。
    for ((变量初始化;循环条件;变量值更新))
    do
    	命令(命令组)
    done
    
    # 样例1
    for ((i=0;i<10;i++));do echo $i;done
    # 样例2
    i=0
    for ((;i<10;));do echo $i;((i++));done
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. while循环。先判断while后面命令的退出状态,为0时,do后面的命令再执行。
    while 命令
    do
    	命令(命令组)
    done
    
    • 1
    • 2
    • 3
    • 4
    1. until循环。先判断while后面命令的退出状态,为0时,do后面的命令再停止。
    until 命令 #等价于 while !命令
    do
    	命令(命令组)
    done
    
    • 1
    • 2
    • 3
    • 4
    1. break和continue可以跳出循环,包括for、while、until。break Ncontinue N跳出N层循环。技巧if 命令;then break/continue;fi可写成命令 && break/continue
    2. 循环的重定向以及和管道的配合。循环结构for、while、until都是以done结尾的,done后面可以接<>做输入输出重定向,或者接| 命令做管道。因为while和until后面是命令,所以命令就有可能需要有输入,那么也是可以在前面加管道的,即 命令 | while 需要输入的命令
    # 输出管道,脚本:
    for i in c b a m p
    do
    	echo $i
    done | sort # 则该脚本可以输出排序后的值
    
    # 输入重定向:
    while read line
    do
    	echo $line
    done < test.txt 
    
    # 输入管道:
    cat test.txt|while read line
    do
    	echo $line
    done 
    
    # 输入和输出:
    cat test.txt|while read line
    do
    	echo $line
    done > out.txt 
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    六、函数

    1. 函数定义有三种形式,可接受位置参数,调用时用法和脚本一样
    function 函数名
    {
    	命令(命令组)
    }
    
    函数名()
    {
    	命令(命令组)
    }
    
    function 函数名()
    {
    	命令(命令组)
    }
    # 一行形式,右大括号前必须要有一个分号
    function 函数名 {命令(命令组);}
    
    # 使用
    函数名 arg1 arg2 arg3 #函数体力如果有echo,就打印echo内容
    
    # 调用
    source 函数定义脚本
    $(函数名)
    $(函数名 arg1 agr2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 函数内变量,如果和全局变量同名,则为全局变量,否则需要加上 local 或者declare声明成局部变量;如果是非同名变量,则为局部变量,加上declare -g 则可声明成全局变量。
    2. 当前函数名,存在内部变量FUNNAME中。FUNNAME是一个数组,第一个元素为当前函数名,第二个元素为上一层调用函数名,declare - p FUNNAMEecho ${FUNAME[*]}查看调用链。
    3. 函数的导出与清除。declare -fx fun1, 导出函数,等同 export -f fun1。清除 unset -f 函数名unset 函数名declare -f fun1查看函数定义。
    4. 函数返回命令return, 可带参数N。执行return后,函数中后续命令不再执行。return不带参数时,函数的返回状态为return上一条命令的状态。retrun N时,,函数的返回状态为N。可当做默认函数结尾有个隐含的return。
    5. 递归函数,可以实现,但运行效率较低,且占用系统资源较多,不提倡写递归函数。

    七、通配符、正则表达和文本处理

    1. 通配符模式,glob(也叫wildcard)。支持以下几种
    通配符含义
    *表示任意字符
    ?表示一个字符
    []范围替换[abc]表示a或b或c,[0-9],[a-z]前面的字符不能大于后面的字符。[A-Z0-9]组合在一起也可以。
    [!]不在范围内,[!abc]表示不为a或b或c,[!0-9]表示不为0-9
    1. 扩展通配模式,可通过shopt -s extglob打开扩展(-u 表示关闭),支持:
    通配模式含义
    *(pattern)匹配所给模式零次或多次,如*(m),表示0个或多个m
    ?(pattern)匹配所给模式零次或一次
    +(pattern)匹配所给模式一次或多次
    @(pattern)匹配所给模式仅一次
    !(pattern)不匹配所给模式
    1. 通配符模式使用场景
      I. shell命令行里,加单引号,双引号或反斜杠时,失去通配符能力。如ls "*"找的就是名为*的文件。
      II. find -name file,中file支持通配符,且本身就放在双引号或单引号中,如find -name "a*.txt",通配符能力不失去,加反斜杠才失去通配能力。
      III. 条件判断双中括号[[]]的等号或不等号右边支持通配符,右边如果带双引号或单引号或反斜杠,通配符失效。
      IV. 第三章15节的常用内置字符串操作(支持通配符)。如果带双引号或单引号或反斜杠,通配符失效。
    # 条件判断中
    > [[ "ads" == a* ]]
    > echo $?
    0
    > [[ "ads" == "a*" ]]
    > > echo $?
    1 --双引号,通配符失效
    # 内置字符串操作中
    > str="abcd"
    > echo ${str/a*/K}
    K
    > echo ${str/"a*"/K}
    abcd  --双引号,通配符失效
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. GLOBIGNORE,被忽略的通配模式,用冒号分割。仅作用于命令行。
    > touch a.txt b.txt c.txt d.txt
    > find -name "*"
    .
    ./c.txt
    ./a.txt
    ./d.txt
    ./b.txt
    > ls *
    a.txt  b.txt  c.txt  d.txt
    > GLOBIGNORE=a*:b*
    > ls *
    c.txt  d.txt
    > find -name "*"
    .
    ./c.txt
    ./a.txt
    ./d.txt
    ./b.txt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 正则表达式,各语言的正则表达式支持范围略有不同,具体可查阅【中文英文】。linux中,平常的正则表达式简称BRE(Basic Regular Expressions),扩展的正则表达式叫ERE(Extended Regular Expressions)。Linux里的正则表达式都是贪婪的(当可匹配多个时,都匹配最长)。常用的正则模式有(其中,"扩展"在egrep命令中可使用):
    字符模式含义
    .匹配任何单个字符
    *匹配零次或多次
    ?匹配零次或一次(扩展)
    +匹配一次或多次(扩展)
    \{N\}匹配N次(扩展用{N}
    \{N,\}匹配N次或更多次(扩展用{N,}
    \{N,M\}匹配至少N次之多M次(扩展用{N,M}
    a|b|c匹配a或b或c(扩展)
    ( )分组符号,如r(able|dress)匹配rable或rdress(扩展)
    [x-y]范围,如[0-9]匹配数字
    [ ]匹配一组字符中的一个,如[abc]匹配a或b或c
    ^匹配行首
    $匹配行尾
    [^]匹配取反,[^0-9]表示非数字,[^abc]表示非a,b,c
    \b单词定界符,包含词首和词尾定界符
    \<词首定界符
    \>词尾定界符
    \w等价 [A-Za-z0-9]
    \W\w取反,等价 [^A-Za-z0-9]
    1. POSIX字符类。指定匹配字符范围的另一种方法。grep 中和egrep中使用方法一样。tr命令后也可使用单中括号tr [:punct:] X,但为了保持一致,建议都用双中括号。作用:有些字符类比正则表达简单,有些命令只支持POSIX不支持正则(如tr)。
    字符类含义
    [[:alnum:]]字符和数字,等同于[A-Za-z0-9]
    [[:punct:]]标点符号
    [[:cntrl:]]控制字符
    [[:space:]]空白字符,空格、制表符、竖向制表符、换行、回车等
    [[:alpha:]]字母,等同[A-Za-z]
    [[:blank:]]空格户制表符Tab
    [[:digit:]]所有数字,等同[0-9]
    [[:lower:]]所有小写字母,等同[a-z]
    [[:upper:]]所有大写字母,等同[A-Z]
    [[:xdigit:]]所有 16 进位制的数字
    # 样例
    grep [[:punct:]] t.txt #过滤出包含空白字符的所有行
    
    • 1
    • 2

    八、进程与作业

    1. 挂起进程,比如进入vi后,在命令模式下,按挂起进程。jobs查看作业状态。
    > vi test.txt # 按
    [1]+  Stopped                 vi test.txt
    > jobs
    [1]+  Stopped                 vi test.txt
    
    • 1
    • 2
    • 3
    • 4
    1. 熟悉命令 ps,jobs, fg, bg, kill, trap, suspend。

    九、其他话题

    1. 目录栈。通常,目录只保留了OLDPWD和PWD,无法再追踪更早之前的记录。pushd、popd,dirs提供了这方面的能力。使用pushd命令记录的目录栈保存在内置变量DIRSTACK中。
    pushd cd到目录,并将目录放入目录栈
    +/-N 目录栈的“环滑动”,+N从栈顶的第N个项目滑为栈顶,-N从栈底的第N个目录滑为栈顶
    
    >pwd
    /usr
    > dirs -c
    > pushd /usr/bin 
    /usr/bin /usr  --永远把当前目录放在栈底
    > pushd /var/lib
    /var/lib /usr/bin /usr
    > declare -p DIRSTACK 
    declare -a DIRSTACK='([0]="/var/lib" [1]="/usr/bin" [2]="/usr")'
    
    dirs
    -p 每行显示目录栈内一个目录
    -v p的基础上带编号,编号大的在栈底
    +/-N 不改变目录栈的内容,+N表示从栈顶数第N个目录,-N表示从栈底数第N个目录
    > dirs
    /var/lib /usr/bin /usr
    > dirs -v
     0  /var/lib
     1  /usr/bin
     2  /usr
    > dirs -p
    /var/lib
    /usr/bin
    /usr
    > dirs +1
    /usr/bin
    
    popd 移除栈顶目录,并cd到新栈顶目录
    +/-N 
    > popd
    /usr/bin /usr
    > pwd
    /usr/bin
    > dirs -v
     0  /usr/bin
     1  /usr
    
    ## 环滑动
    > dirs -v
     0  /opt
     1  /usr/lib
     2  /usr/bin  --从栈顶第2位
     3  /usr
    > pushd +2
    /usr/bin /usr /opt /usr/lib
    > dirs -v
     0  /usr/bin  --原从栈顶第2位
     1  /usr
     2  /opt   --原栈顶滑下来了
     3  /usr/lib
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    1. 波浪号扩展
    命令含义
    cd ~等价cd $HOME
    cd ~username到username的home
    ~+等价PWD,cd ~+
    ~-等价OLDPWD,cd ~-
    ~+N~N等价dirs +N
    ~-N等价dirs -N
    1. 交互式shell和非交互式shell、登录shell和非登录shell
    2. expand_aliases别名功能,交互式shell默认打开,非交互式shell默认关闭。
      I. 不要在脚本内使用别名,不然要显示打开expand_aliases。
      II. 别名不能export,要想在其他shell中使用,将别名放到脚本里,再source 脚本名。
      III. 函数内定义的别名,在函数调用前不能使用。
      IV. 综上,不要在脚本和函数内使用别名。
    3. 并行处理。常用名命令grep,wc,awk和sed等都是单线程的,只能使用一个cpu。服务器有多个cpu时,可以利用GNU的parallel命令,把负载分配到各个cpu上。
      I. parrallel命令通常需要下载安装,官网
      II. 使用说明
    mpstat # 查看cpu信息,该命令不是所有bash都有
    > cat /proc/cpuinfo |grep processor | wc -l # 获取cpu数
    2
    
    • 1
    • 2
    • 3

    十、Bash调试

    1. 正式运行脚本前,先set -n set -o noexce,读命令,解释但不执行,用来检查脚本语法
    2. set -uset -o nounset,有变量未定义时提示错误信息
    3. shopt -s shift_verbose,shift移动数大于参数个数时,提示错误
    4. set -vset -o verbose,为调试打开verbose模式,脚本运行时显示读入的每行命令,并原样打印
    5. set -xset -o xtrace,为调试打开echo模式,脚本运行时显示变量替换后的每行命令和参数。只调试一段代码时,在代码前后添加:
    set -xv
    待调试代码段
    set +xv
    
    • 1
    • 2
    • 3
    1. set -x的提示符为PS4,默认为+,可修改成PS4=+'$[LINENO]'打印代码行号。
    2. bash -x 脚本set -x是一样的,相当于在脚本前增加了set -x。便于脚本测试。
    3. 利用trap捕获信号来调试
    4. 打印内容调试
    5. 利用BASH_SOURCE,BASH_SUBSHELL,FUNNAME等内置变量。
    6. 输出重定向获取log文件来分析
    7. 在代码中增加“调试块”,if语句开控制变量调试信息是否打印
    if [$DEBUG = YES];then
    	打印调试信息
    fi
    
    • 1
    • 2
    • 3
    # 当前执行脚本的绝对路径,可以采用以下方式:
    DIR_T="$( cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    
    # 在tmp文件夹下创建脚本test.sh 和a.sh
    > cat test.sh
    echo BASH_SOURCE=${BASH_SOURCE[0]}
    echo \$0=$0
    DIR_T="$( cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    echo ${DIR_T}
    > cat a.sh
    echo "This is a.sh"
    source test.sh
    
    # 运行
    > ./test.sh
    BASH_SOURCE=./test.sh --相对路径
    $0=./test.sh --相对路径
    /opt/user/tmp --绝对路径
    > bash test.sh
    BASH_SOURCE=test.sh
    $0=test.sh
    /opt/user/tmp
    > ../tmp/test.sh
    BASH_SOURCE=../tmp/test.sh --相对路径
    $0=../tmp/test.sh --相对路径
    /opt/user/tmp
    > bash ../tmp/test.sh
    BASH_SOURCE=../tmp/test.sh
    $0=../tmp/test.sh
    /opt/user/tmp
    > ./a.sh
    This is a.sh
    BASH_SOURCE=test.sh
    $0=./a.sh  --父脚本的名字
    /opt/user/tmp
    
    # BASH_SUBSHELL
    > echo $BASH_SUBSHELL
    0
    > (echo $BASH_SUBSHELL)
    1
    > ( ( ( ( (echo $BASH_SUBSHELL) ) ) ) )
    5
    > bash
    > echo $SHLVL
    1
    > echo $BASH_SUBSHELL
    0
    
    • 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
    • 44
    • 45
    • 46
    • 47
    • 48
  • 相关阅读:
    【Verilog语法】比较不同计数器的运算方式,其中有一个数是延迟打一拍的效果,目的是使得两个计数器的结果相同。
    使用Flask开发简单接口
    【剑指Offer】28.对称的二叉树
    day04_java基础
    简单的学生信息管理系统
    腾讯mini项目-【指标监控服务重构】2023-08-20
    基于C语言的推选优秀班委投票系统
    前端实现搜索联想时防抖功能:
    服务器数据恢复—热备盘同步中断导致Raid5数据丢失的数据恢复案例
    docker从入门到入土
  • 原文地址:https://blog.csdn.net/Code_LT/article/details/127442364