判断操作
# 二元比较用于配合 test、[ ]、[[ ]] 进行测试 ...
# 算数比较
-eq # 等于
-ne # 不等于
-lt # 小于
-le # 小于等于
-gt # 大于
-ge # 大于等于
# 算数比较,针对双括号 (( ... )) 结构
> # 大于
>= # 大于等于
< # 小于
<= # 小于等于
== # 等于
# 字串比较: 若在 [[ ... ]] 测试则不需要转义
== # 等于
!= # 不等于
\< # 小于(ASCII)
\> # 大于(ASCII)
-z # 字符串为空
-n # 字符串不为空
# 文件类型测试
-e # 文件是否存在
-s # 文件大小不为 0
-f # 是文件
-d # 是目录
-p # 是管道
-S # 是 socket
-r # 有读权限
-w # 有写权限
-x # 执行权限
-h # 符号链接
-L # 符号链接
-b # 块设备
-c # 字符设备
-g # 设置了 sgid 标记
-u # 设置了 suid 标记
-k # 设置了粘贴位
-t # 文件与一个终端相关联
-N # 从这个文件最后一次被读取之后,又被修改过
-O # 这个文件的属主是当前用户
-G # 文件组 id 与用户所属组相同
F1 -ef F2 # 文件 F1 和文件 F2 都是同一个文件的硬链接
F1 -nt F2 # 文件 F1 比文件 F2 新
F1 -ot F2 # 文件 F1 比文件 F2 旧
-a # 逻辑与
-o # 逻辑或
! # 逻辑否(反转测试结果)
# 测试两个文件是否均可写
[ -w result.txt -a -w score.txt ] && echo OK
变量的间接引用、变量追加
aa=120
type=aa
echo ${!type}set # 提取 type 的值再将其作为变量名,此处即获取 $aa 的值(其实就是间接引用,类似指针的味道)
# 120set
# 下例两者相等
var=$var$append
var+=$append
变量的处理
${#var} # 获取变量的长度
${var#str} # 若从头开始的数据符合 str 则将符合的最短数据切除,${var#*str} 从左开始删除第一个 str 及其左边所有
${var##str} # 若从头开始的数据符合 str 则将符合的最长数据切除
${var%str} # 若从尾向前的数据符合 str 则将符合的最短数据切除,${var%str*} 从右开始删除第一个 str 及其右边所有
${var%%str} # 若从尾向前的数据符合 str 则将符合的最长数据切除
${var/oldStr/newStr} # 若符合 oldStr 则第一个旧字符串会被 newStr 替代
${var//oldStr/newStr} # 若符合 oldStr 则全部的旧字符串会被 newStr 替代
${var:0:5} # 取字串索引从 0 起并向后共 5 个字符的内容(偏移)
${var:7} # 从字串索引第 8 个字符到最后(从第 7 位开始截取)
${var:2:-2} # 从字串索引第 2 位开始截取、从最后截取 2 位
${var:0-7:3} # 从右向左第几 7 个字符起再向其右边取共 3 个字符
${var:-"value"} # 若变量未定义或为空则临时用 value 代替,如 ${NO_DEFINE:-~} 未定义时输出为 /root
${var:="value"} # 若变量未定义或为空则赋值为 value 如 ${username=$(date)}
${var:+"value"} # 若变量不为空则返回 value,若为空则仍返回空值(反向判断)
${var:?"errorString"} # 若变量未定义或为空则输出标准错误并退出
${var/old/new} # 替换一次
${var//old/new} # 替换全部,字符串判断 [[ $string == *My* ]] && echo ok
${!varprefix*} # 匹配所有以 varprefix 开头的变量
${!varprefix@} # 匹配所有以 varprefix 开头的变量
${string/#substr/replace} # 若前缀匹配 substr 则用 replace 取代 substr
${string/%substr/replace} # 若后缀匹配 substr 则用 replace 取代 substr
$(command) # 命令执行后的结果(它比 `command...` 的格式更稳定)
$((.....)) # 算数表达式的结果
unset var # 销毁变量、函数
$(($RANDOM%10)) # 生成 0~9 之间的随机数
# —————————————————————————————————————————————————————————————————————————————————————————— 大小写转换
# 大小写字母转换,如果变量值的首字母匹配模式 pattern(通配符匹配,只能是一个字符,可以是 ? * [...] 或一个英文字母,多个字符不起作用)
${var^pattern} # 将首字母转大写
${var^^pattern} # 将所有匹配字母转大写
${var,pattern} # 将首字母转小写
${var,,pattern} # 将所有匹配字母转小写
var_5='hello WORLD'
var_6='HELLO world'
echo ${var_5^[a-z]} # Hello WORLD
echo ${var_5^^*} # HELLO WORLD
echo ${var_5^^} # HELLO WORLD
echo ${var_6,} # hELLO world
echo ${var_6,,[A-Z]} # hello world
# Tips ...
# 针对数组变量使用 @、* 的情况和前述相同,大小写转换将作用于每个参数
# —————————————————————————————————————————————————————————————————————————————————————————— 进制转换
echo $((2#0000011)) # 计算 2 进制数据的 10 进制表示,结果为 3
echo $((8#12322)) # 计算 8 进制数据的 10 进制表示,结果为 5330
环境变量、特殊变量
# 环境变量分 set、env 两种
# set 用来显示环境变量、私有变量,它能显示当前 SHELL 的变量(私有变量),包括当前用户的变量(环境变量)
# env 用来显示环境变量,它是当前用户的变量(环境变量)
# export 用来显示和设置环境变量 ...
# 不同类的 SHELL 有不同的私有变量 bash、ksh、csh 每种 SHELL 私有变量都不一样
# SHELL 在初始化时会在执行 /etc/profile、~/.bash_profile 等初始化脚本,它们都定义了若干环境变量,这些变量会在创建子进程时传递给子进程
# 常见的环境变量设置文件:
# ● /etc/profile
# ● /etc/profile.d/
# ● ~/.profile
# ● /etc/bashrc
# ● ~/.bashrc
# ● /etc/rc.local、...
# 定义环境变量
export VAR=VALUE
# 执行 env 命令查看当前环境变量,常用系统环境变量如下(其中有部分不是环境变量)
_ # 上条命令的最后一个参数
$BASH # 展开为调用 BASH 实例时使用的全路径名
$BASH_VERSION # BASH 版本
$BASHPID # BASH 进程的进程 ID
$BASHOPTS # 当前 SHELL 的参数,可以用 shopt 命令修改
$BASH_SUBSHELL # 记录 BASH 进程实例中多个子 SHELL "subshell" 嵌套深度的累加器,例如使用 (command...) 的场景
$SHLVL # 记录 SHELL 的嵌套层数,启动第一个 SHELL 时值为 1,若在其中调用脚本则脚本中其值为 2,类似的还有 $BASH_SUBSHELL
$SHELLOPTS # 已开启的 SHELL 选项,如 braceexpand、hashall、monitor 等
$SHELL # 内部变量 SHELL 通过 BASH 的值确定当前 SHELL 类型
$HOSTNAME # 主机名
$SECONDS # 记录脚本从执行到结束所经过的秒数,调试时有用
$EDITOR # 内置编辑器 emacs、gmacs、vi 的路径名
$EUID # 在 SHELL 启动时被初始化的当前用户的 UID
$GROUPS # 当前用户所属的 GID
$HISTTIMEFORMAT # 定义 history 命令显示的时间格式
$HISTIGNORE # 忽略历史中的特定命令,如 HISTIGNORE="pwd:ls:ls -ltr:"
$HISTCONTROL # 将值设为 ignoredups 可以将连续(重复)执行的命令剔除,仅保留一条、将值设为 erasedups 可清除整个命令历史中的重复条目
$HISTFILE # 保存命令行历史的文件,默认路径 ~/.bash_history
$HISTSIZE # 记录在命令行历史文件中的命令数,默认 500
$HOME # 默认为主目录,这是未指定路径时 cd 的默认路径
$IFS # 内部字段的域分隔符,通常是空格、制表、换行,用于命令替换、循环结构、读取输入时的字段划分
$IFS # SHELL 把 IFS 中的每个字符都作为一个分隔符,并用它们来将参数扩展的结果切分成 word
$LANG # 用来为没有以 LC_ 开头的变量明确选取的种类确定 locale 类
$OLDPWD # 上一个工作目录,与 cd - 的作用相同
$PATH # 命令搜索路径,它的值是由冒号分隔的目录列表
$PPID # 父进程 PID
$PS1 # 主提示符串,[\u@\h \W]\$ 其中的 \$ 对 root 用户是 #、对普通用户是 $
$PS2 # 次提示符串,默认值是 >,当输入的命令分为好几行时,新行前出现的字符串即为 PS2 的值,例如当在 SHELL 中运行 for 循环时
$PS3 # 与 select 命令一起使用的选择提示符串,默认是 #?
$PS4 # 当使用 bash -x 追踪时使用的调试提示符串,默认是 +
$LOGNAME # 登录者帐号
$UID # 当前用户的 ID
$USER # 当前用户名
$PWD # 当前工作目录,由 cd 设置
$RANDOM # 随机数变量
$REPLY # 当未给 read 提供参数时的默认接收变量,是记录多个 BASH 进程实例嵌套深度的累加器
$MACHTYPE # 处理器架构及发行版信息,例如 x86_64-redhat-linux-gnu
$OSTYPE # 操作系统类型,例如 linux-gnu
$SSH_CLIENT # SSH 会话的客户端来源 IP
$SSH_CONNECTION # SSH 的连接信息
$SSH_TTY # 设备
$PROMPT_COMMAND # 在显示 PS1 提示之前,该变量的文本内容作为常规的 BASH 命令执行,它是位于 /etc/bashrc 的特殊变量
$LS_OPTIONS # 决定如何显示命令 ls 的结果,例如 export LS_OPTIONS=--color=auto(经测试不可行)
$TMOUT # 表示 SHELL 自动终止命令之间的秒数
$EPOCHSECONDS # 返回 1970 年以来的秒数
$PIPESTATUS # 提供管道中每个命令的状态
$FUNCNAME # 当前正在运行的函数名称,可以函数内使用 "echo $FUNCNAME" 来引用
$LINENO # 返回脚本中当前的行号
$XDG_SESSION_ID
$HOSTNAME
$SELINUX_ROLE_REQUESTED
$SELINUX_USE_CURRENT_RANGE
$LD_LIBRARY_PATH
$TERM
$LS_COLORS
$rs
$MAIL
$SELINUX_LEVEL_REQUESTED
$LESSOPEN
$XDG_RUNTIME_DIR
# —————————————————————————————————————————————————————————————————————————————————————————— 特殊变量
$0 # 当前脚本名称
$num # num 为从 1 开始的数字,它是脚本参数的位置索引
$# # 传入脚本的参数的总数量
$* # 所有位置参数,作为单个字符串
$@ # 所有位置参数,每个都作为独立的字符串,形成参数组成的数组
${#@} # 脚本的命令行参数个数
$? # 当前进程中上个命令的返回值,常配合 if 或 &&
$$ # 脚本自身 PID
$! # 后台运行的最后一个 PID
$- # 输出 SHELL 使用的当前选项
$_ # 之前命令的最后一个参数
${!#} # 获取命令行最后面的参数,因为 $# 的值是当前进程的参数个数,因此使用 ! 引用该变量的值即为最后一个参数值
set、env、export
# set [+-abCdefhHklmnpPtuvx]
# -a 将后续定义的变量或函数自动导出
# -e 如果命令以非零状态退出,则立即退出(停止后续的脚本逻辑)
# -f 禁用文件名扩展
# -x 执行指令后,先显示该指令及所下的参数
# -n 只读取指令,而不实际执行(相当于全局级别的注释)
# -b 使被中止的后台程序立刻回报执行状态
# -k 将所有参数当作变量赋值
# -m 使用监视模式(启用作业控制)
# -p 启动特权模式
# -v 打印 SHELL 输入行
# -t 执行完随后的指令,即退出 SHELL
# -u 当执行时使用到未定义过的变量,则显示错误信息
# +<arg> 取消某个 set 曾启动的参数
# 设置位置参数的值,可以使用 set 命令为脚本或程序中的位置参数赋值
set Ubuntu Fedora LinuxMint Debian Kubuntu
echo $3
# LinuxMint
set -- "arg 1" "arg 2" "arg 3"
echo "$1" # arg 1
echo "$2" # arg 2
echo "$3" # arg 3
# 双破折号用于取消位置参数
set --
# 可以使用 + 选项来禁用 SHELL 选项,例如 set +o nounset
# 可以使用 - 选项来启用 SHELL 选项,例如 set -o nounset
# —————————————————————————————————————————————————————————————————————————————————————————— export
# 内建 builtin 命令 export 用于把当前 SHELL 的变量、函数导出到子 SHELL,这样在子 SHELL 中就可以使用父 SHELL 中定义的变量、函数
export # 显示当前导出的变量
export -p # 显示当前导出的变量
export name # 导出变量 name
export name=word # 导出变量 name 并赋值为 word
export -n name # 取消导出的变量 name
export -f name # 导出函数 name
export -f # 显示当前导出的函数
export -fn name # 取消导出的函数 name
BASH 中的常见特殊符号
# 通配扩展及代码块: {}
mkdir /home/{userA,userB,userC}/{source,data}
{command...;} && { command1;command2;....;} &> /dev/null
echo {01..10}
echo ${string##endword}
# 通配扩展及判断: []
ls /[eh][to][cm]*
if [ "$?" != 0 ] # 等价于 if test "$?" != 0
# 执行替换: `...` 与 $( ... ) 及求值运算: $(( expr... ))
result=`command...`
result=$(command...)
result=$(( 10**10 ))
# 强/弱引用
a=1
echo '$a' # 输出 $a
echo "$a" # 输出 1
# and/or 即 &&、||
[ condition ] && command FOR true || command FOR false
if ! revcondition; then ....... ; fi
# 特殊字符冒号作为内建空指令其返回值永远为 0,while : 与 while true 及 while 1 均可实现无限循环
Tips
if [ CONDITION ] # 测试结构
if [[ CONDITION ]] # 扩展的测试结构,支持更多操作符,如 &&、||、=~、<=、>=、......
{ command1; command2; ... ; } # 代码块
{1..100} # 花括号展开
>(COMMAND) # 进程替换 ?
<(COMMAND) # 进程替换 ?
( command... ) # 在子 SHELL 中执行命令
result=$( command... ) # 在子 SHELL 中执行命令并将结果赋给变量
Array=(element1 element2 element3) # 初始化下标类型的数组
(( var = 78 )) # 进行整型运算
var=$(( 20 + 5 )) # 进行整型运算并将结果赋值给变量
C=$A$B # 变量值的字串拼接,不需要空格
cd - # 切换到之前的目录,上一个目录
cd ~ # 用户的主目录
command & # 将当前任务放到后台进行
# 当使用 let 进行时在变量名前面不能添加 $
var1=2
var2=3
let result=var1+var2
echo $result # 输出 5
let var++ # 自加
let var-- # 自减
let var+=6 # 等同于 let var=var+6
let var-=6 # 等同于 let var=var-6
# 操作符 []、(()) 的使用和 let 类似,当使用它们时在变量名前面的 $ 可加可不加,但操作符前必须加 $
var1=2
var2=3
result=$[var1+var2] # 也可以用 result=$[$var1+$var2]、result=$((var1+var2))
echo $result # 输出 5
# expr 同样可用于基本算术操作,但需要注意在进行乘法运算时需要在 * 前面加上转义符 \ 否则会报错
# 除此之外表达式中的每个部分都要用空格分开