Bash语法讲解
变量操作
变量声明(非必须操作)
首个字符必须为字母(a-z,A-Z)
中间不能有空格,可以使用下划线_
不能使用标点符号
不能使用bash里的关键字(help查看保留关键字)
在赋值等号 “=” 的两边没有空格,任何空格将导致错误
单双引号,注意具体场合
declare -a 声明一个数组
declare -i 声明一个整型
declare -x 声明为环境变量
declare -r 声明一个只读变量
可变变量
只读变量
内置变量
| 变量名 | 含义 |
| $0 | 脚本名字 |
| $1~$9 | 带入参数的位置 |
| ${10} | 第10个位置的参数 |
| $# | 带入参数的个数 |
| $* | 所有的位置参数(作为单个字符串) * |
| $@ | 所有的位置参数(每个都作为独立的字符串) |
| $? | 返回值 |
| $$ | 脚本的进程ID(PID) |
| $_ | 之前命令的最后一个参数 |
| $! | 运行在后台的最后一个作业的进程ID(PID) |
数据数组
names="I like python"
names=(hello world python)
if [[ "${names[@]}" =~ "python" ]]; then
echo '字符串存在'
fi
添加数据元素
array=()
array+=("first")
echo ${array[@]}
随机数组键值
IPS=(192.168.12.11 192.168.12.12 192.168.12.13 192.168.12.14)
key=$(( $RANDOM % ${#IPS[*]} ))
value=${IPS[$key]}
echo $key $value
数据字典
redisService=([1020]=marco [1021]=shanks [1022]=yupoo [1023]=updns [1024]=robin)
echo ${#redisService[*]}
for port in ${!redisService[*]};do
name=${redisService[$port]}
echo $name, $port
done
变量初始化
| 表达式 | 含义 |
| ${var} | 变量var的值, 与$var相同 |
| ${var-DEFAULT} | 如果var没有被声明, 那么就以$DEFAULT作为其值 |
| ${var:=DEFAULT} | 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 |
| ${var:?ERROR} | 如果var没被设置, 那么就打印$ERROR |
| ${!varprefix*} | 匹配之前所有以varprefix开头进行声明的变量 |
字符串变量操作
| 表达式 | 含义 |
| ${#string} | $string的长度 |
| ${string:position} | 在$string中, 从位置$position开始提取子串 |
| ${string:position:length} | 在$string中, 从位置$position开始提取长度为$length的子串 |
| ${string#substring} | 从变量$string的开头, 删除最短匹配$substring的子串 |
| ${string##substring} | 从变量$string的开头, 删除最长匹配$substring的子串 |
| ${string%substring} | 从变量$string的结尾, 删除最短匹配$substring的子串 |
| ${string%%substring} | 从变量$string的结尾, 删除最长匹配$substring的子串 |
| ${string/substring/replacement} | 使用$replacement, 来代替第一个匹配的$substring字符串 |
| ${string\/\/substring/replacement} | 使用$replacement, 代替所有匹配的$substring字符串 |
| ${string/#substring/replacement} | 如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring字符串 |
| ${string/%substring/replacement} | 如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring字符串 |
条件判断
文件类
| 标记 | 代表含义 |
| -e filename | 如果 filename 存在,则为真 |
| -b filename | 如果 filename 存在,并且是块文件,则为真 |
| -c filename | 如果 filename 存在,并且是字符文件,则为真 |
| -d filename | 如果 filename 存在,并且为目录,则为真 |
| -f filename | 如果 filename 存在,并且为常规文件,则为真 |
| -h filename | 如果 filename 存在,并且为符号连接,则为真 |
| -s filename | 如果 filename 存在,并且大小不为零,为真 |
| -u filename | 如果 filename 存在,并且为set-user-id,为真 |
| -g filename | 如果 filename 存在,并且为set-group-id,为真 |
| -r filename | 如果 filename 存在,并且可读,则为真 |
| -w filename | 如果 filename 存在,并且可写,则为真 |
| -x filename | 如果 filename 存在,并且可执行,则为真 |
| -k filename | 如果 filename 存在,并且设置了sticky位,为真 |
| -p filename | 如果 filename 存在,并且为有名管道(FIFO),真 |
| -o optname | 如果 shell 选项 optname 被开启,则为真 |
| -t fd | 如果文件描述符被打开并指向一个终端,则为真 |
| -O filename | 如果 filename 存在,并且被有效用户ID所拥有,则为真 |
| -G filename | 如果 filename 存在,并且被有效组ID所拥有,则为真 |
| -S filename | 如果 filename 存在,并且为一个socket,则为真 |
| -N filename | 如果 filename 存在,并且在上次读取后被修改过,则为真 |
| file1 -nt file2 | 如果 file1 比 file2 新,或者 file1 存在 file2 不存在,则为真 |
| file1 -ot file2 | 如果 file1 比 file2 旧,或者 file2 存在 file1 不存在,则为真 |
| file1 -ef file2 | 如果 file1 和 file2 都指向同样的设备(device)和索引节点号(inode numbers),则为真 |
数值类
| 标记 | 代表含义 |
| -eq 等于 | [ $num1 -eq $num2 ] |
| -ne 不等于 | [ 100 -ne $num1 ] |
| -lt 小于 | [ 100 -lt expr $num1 + $num2 ] |
| -le 小于或等于 | [ 100 -le expr $num1 \* $num2 ] |
| -gt 大于 | [ 100 -gt expr $num1 / $num2 ] |
| -ge 大于或等于 | [ 100 -ge expr $num1 % $num2 ] |
字符类
| 标记 | 代表含义 |
| -z string | 如果 string 空为真 |
| -n string | 如果 string 长度不为零为真 |
| string1 != string2 | 如果 string1 与 string2 不同,则为真 |
| string1 == string2 | 如果 string1 与 string2 相同,则为真 |
| string1 > string2 | 如果 string1 按字典顺序比较大于 string2,则为真 |
| string1 \< string2 | 如果 string1 按字典顺序比较小于 string2,则为真 |
流程控制
分支
if…then…fi
if…then…else..fi
if…then…elif…then…else…fi
case…in…esac
表达式分支判断
score="90"
if [ $score -ge 90 ];then
echo "A"
elif [ $score -ge 80 ];then
echo "B"
elif [ $score -ge 70 ];then
echo "C"
else
echo "D"
fi
字符匹配判断
case "$1" in
start)
echo "start server...";;
stop)
echo "stop server...";;
*)
echo "unknown action";;
esac
循环
for …in …do…done
while …do…done
while read…do…done
for循环版本
total=0
#for((i=0;i<=100;i++));do
for i in `seq 1 100`;do
((total+=i))
done
echo $total
while循环版本
total=0
i=0
while [ $i -le 100 ];do
total=$[$total+$i]
((i++))
done
echo $total
while循环读取参数
#!/bin/sh
verbose=''
while getopts 'af:v' flag; do
case "${flag}" in
a) echo "aaa";;
f) files="${OPTARG}" ;;
v) verbose='1.0.0' ;;
*) echo "Unexpected option ${flag}" ;;
esac
done
[ ! -z $files ] && echo $files
[ ! -z $verbose ] && echo $verbose
注意:如果字符后跟冒号(例如f:),则该选项预期具有参数。
用法示例:./script -v -a -b -f filename
while读取文件行
#!/bin/sh
while read -r LINE;do
echo $LINE
done < test.txt
菜单
MENU="TV Movie Game Sport"
echo "which do you like?"
select list in $MENU Quit;do
[ $list = "Quit" ] && echo "ByeBye" && break;
echo "Oh,you like $list !"
done
内置函数
| 命令 | 含义 |
| . file or source file | dot命令从文件file中读取命令并执行 |
| : | 空操作,返回退出状态0 |
| alias | 显示和创建已有命令的别名 |
| bg | 把作业放到后台 |
| bind | 显示当前关键字与函数的绑定情况,或将关键字与readline函数或宏进行绑定 |
| break | 从最内层循环跳出 |
| cd [arg] | 改变目录,如果不带参数,则回到主目录,带参数则切换到参数所指的目录 |
| declare [var] | 显示所有变量,或用可选属性声明变量 |
| dirs | 显示当前记录的目录(pushd的结果) |
| echo [args] | 通用打印 |
| prinf | 格式化输出 |
| eval [args] | 把args读入Shell,并执行产生的命令 |
| exec command | 运行命令,替换掉当前Shell |
| exit [n] | 以状态n退出Shell |
| export [var] | 使变量可被子Shell识别 |
| fc | 历史的修改命令,用于编辑历史命令 |
| fg | 把后台作业放到前台 |
| getopts | 解析并处理命令行选项 |
| history | 显示带行号的命令历史列表 |
| jobs | 显示放到后台的作业 |
| kill [-signal process] | 向由PID号或作业号指定的进程发送信号。输入kill-l查看信号列表 |
| let | 用来计算算术表达式的值,并把算术运算的结果赋给变量 |
| local | 用在函数中,把变量的作用域限制在函数内部 |
| logout | 退出登录Shell |
| popd | 从目录栈中删除项 |
| pushd | 向目录栈中增加项 |
| pwd | 打印出当前的工作目录 |
| read [var] | 从标准输入读取一行,保存到变量var中 |
| readonly [var] | 将变量var设为只读,不允许重置该变量 |
| return [n] | 从函数中退出,n是指定给return命令的退出状态值 |
| set | 设置选项和位置参量 |
| shift [n] | 将位置参量左移n次 |
| stop pid | 暂停第pid号进程的运行 |
| suspend | 终止当前Shell的运行(对登录Shell无效) |
| test | 检查文件类型,并计算条件表达式 |
| times | 显示由当前Shell启动的进程运行所累计用户时间和系统时间 |
| trap [arg] [n] | 当Shell收到信号n(n为0、1、2或15)时,执行arg |
| ulimit | 显示或设置进程可用资源的最大限额 |
| umask [八进制数字] | 用户文件关于属主、属组和其他用户的创建模式掩码 |
| unalias | 取消所有的命令别名设置 |
| unset [name] | 取消指定变量的值或函数的定义 |
| wait [pid#n] | 等待pid号为n的后台进程结束,并报告它的结束状态 |
read 命令
#!/bin/sh
read -t15 -n5 -p "请输入你的编号(<=5位数)" userid
read -t5 -n5 -s -p "请输入你的密码(限时5秒)" userpass
printf "\n%10s : %10s\n" $userid $userpass
#!/bin/bash
read -n1 -p "Do you want to continue [Y/N]? " answer
case $answer in
Y|y)
echo "fine ,continue";;
N|n)
echo "ok,good bye";;
*)
echo "error choice";;
esac
exit 0
#!/bin/sh
BONDING="bond0|eth0@eth1@eth2@eth3@eth4@eth5|0|118.123.118.134|255.255.255.224|118.123.118.129|1500"
xx=$IFS;IFS="|";read -r nick devs mode ip mask gw mtu <<<"$BONDING";IFS=$xx
printf 命令
#!/bin/bash
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
Bash命令行小技巧
ctrl-a 跳转到行首
ctrl-e 跳转到行尾
ctrl-b 往后移动字符
ctrl-f 往前移动字符
ctrl-w 回退剪切一个单词
ctrl-u 剪切至行首
ctrl-k 剪切至行尾
ctrl-y 粘贴剪切板内容
ctrl-r 反向搜索历史命令
Bash的颜色方案
下表是linux终端所支持的基本颜色的代码:
| 前景 | 背景 | 颜色 |
| 30 | 40 | 黑色 |
| 31 | 41 | 红色 |
| 32 | 42 | 绿色 |
| 33 | 43 | 黄色 |
| 34 | 44 | 蓝色 |
| 35 | 45 | 紫红色 |
| 36 | 46 | 青蓝色 |
| 37 | 47 | 白色 |
除了基本的色彩代码,linux还支持一些额外的样式控制代码,如下表所示:
| 代码 | 含义 |
| 0 | OFF |
| 1 | 高亮显示 |
| 4 | 下划线 |
| 5 | 闪烁 |
| 7 | 反白显示 |
| 8 | 不可见 |
演示代码
#/bin/bash
for STYLE in 0 1 4 5 7 8; do
for FG in 30 31 32 33 34 35 36 37; do
for BG in 40 41 42 43 44 45 46 47; do
CTRL="\033[${STYLE};${FG};${BG}m"
echo -en "${CTRL}"
echo -n "${STYLE};${FG};${BG}"
echo -en "\033[0m"
done
echo
done
echo
done
# Reset
echo -e "\033[0m"