一、位置参数是命令行和脚本之间的桥梁¶
原笔记把位置参数列为 Shell 编程核心内容,最常见的是下面几个:
$1、$2、$3:脚本的第 1、2、3 个参数$0:脚本本身的名字$#:脚本参数个数$@:所有参数$*:所有参数
最基础的演示脚本如下:
#!/bin/bash
echo "脚本第1个参数:$1"
echo "脚本第2个参数:$2"
echo "脚本参数数量:$#"
执行效果:
bash /server/scripts/devops-shell/02.vars.sh a b c d e
二、位置参数最常见的使用场景¶
2.1 接收用户名并做检查¶
例如执行脚本时传入用户名,然后用 id 判断用户是否存在:
bash /server/scripts/devops-shell/03.check_user.sh zq
这种写法很适合做:
- 用户检查
- 服务管理脚本
- 发布脚本参数传递
- 巡检脚本指定主机或目录
2.2 输出脚本帮助¶
$0 非常适合用来打印使用方法:
#!/bin/bash
echo "Usage: $0 {start|stop|restart}"
2.3 检查参数个数¶
$# 最常与判断结合:
if [ $# -ne 1 ]; then
echo "Help: $0 {start|stop|restart}"
exit 6
fi
三、$10 为什么会出问题¶
这是原笔记专门强调的面试题:
$10在 Shell 里会被解释成$1加上字符0- 参数位数大于 9 时,应写成
${10}、${11}
例如:
echo $1 $2 $3 $4 $5 $10 $11
echo $1 $2 $3 $4 $5 ${10} ${11}
四、$@ 和 $* 的区别要看双引号¶
不加双引号时,$@ 和 $* 看起来差别不大;真正的差异出现在加上双引号之后。
set -- "I am oldboy" teacher lidao
for i in "$*"; do echo "$i"; done
for j in "$@"; do echo "$j"; done
结果是:
"$*"会把所有参数合并成一个整体"$@"会保留每个参数的边界
因此,只要你打算在循环或函数中逐个处理参数,优先用 "$@"。
五、状态变量用于判断命令有没有执行成功¶
原笔记列出的状态类特殊变量包括:
$?:上一个命令或脚本的返回值,0表示成功$$:当前脚本的 PID$!:上一个后台命令的 PID$_:上一个命令的最后一个参数
其中最常用的还是 $?。
5.1 实战:输入命令并检查执行结果¶
#!/bin/bash
cmd="$@"
$cmd &>/dev/null
if [ $? -eq 0 ]; then
echo "$cmd 运行成功"
else
echo "$cmd 运行失败"
fi
调用示例:
bash /server/scripts/devops-shell/07.check_cmd.sh ls -a
六、参数展开让变量处理更高效¶
原笔记把参数展开分成几类:长度统计、删除、截取和替换。
6.1 统计长度¶
oldboy="lidao996"
echo ${#oldboy}
6.2 删除左边或右边的内容¶
var=oldboylidao996
echo ${var#oldboy}
echo ${var##*o}
dir=/etc/sysconfig/network-scripts/ifcfg-eth0
echo ${dir##*/}
echo ${dir%/*}
这个技巧很适合:
- 提取文件名
- 提取目录名
- 删除固定前后缀
6.3 截取变量¶
var=oldboy
echo ${var:3}
echo ${var:1:3}
6.4 替换变量内容¶
var=oldboylidao996
echo ${var/o/-}
echo ${var//o/-}
七、实战:统计句子里长度不超过 6 的单词¶
原笔记给出的 Shell 版本示例如下:
#!/bin/bash
hua="I am oldboy teacher welcome to oldboy training class."
hua_del=$(echo "$hua" | sed 's#\.##g')
for word in ${hua_del}; do
if [ ${#word} -le 6 ]; then
echo "单词字符数小于等于6: ${word}"
fi
done
同样的需求也可以用 awk 一条命令完成,这也说明参数展开和三剑客是可以互相替补的。
八、默认值扩展适合给变量兜底¶
原笔记列出的 4 种常见写法如下:
${parameter:-word}:变量未定义或为空时,输出默认值,但不改变量本身${parameter:=word}:变量未定义或为空时,赋默认值并写回变量${parameter:?word}:变量未定义或为空时,输出错误信息${parameter:+word}:变量存在且非空时,用word替换输出
例如:
echo ${name:-root}
echo ${lidao:=996}
echo ${name:+root}
其中最常用的是 :- 和 :=,非常适合给脚本参数、目录变量、服务名变量设置保底值。
九、变量赋值方式有哪些¶
原笔记把变量赋值分成 5 种常见来源:
- 直接赋值:
oldboy=lidao996 - 命令结果赋值:
hostname=$(hostname) - 脚本传参赋值:
user_name=$1 read交互赋值- 从文件读取内容赋值
其中 read 是交互脚本里必须掌握的。
9.1 read 常用参数¶
-p:提示信息-t:超时退出-s:不显示输入内容,适合密码输入
示例:
read -p "请输入2个数字num1 num2:" num1 num2
read -s -p "请输入密码:" pass
十、实战:用户输入密码后倒序输出¶
原笔记里的示例脚本如下:
#!/bin/bash
read -s -p "请输入密码:" pass
echo
pass_rev=$(echo "$pass" | rev)
echo "正在猜测你的密码....."
echo "马上破解成功"
echo "$pass_rev"
echo "看看对不对?"
这个例子虽然简单,但正好把 read -s、命令替换、变量输出串起来了。
十一、写参数处理脚本时的推荐顺序¶
把原笔记里的内容合起来,日常写脚本时可以遵循这个顺序:
1、用 $# 检查参数个数。
2、用 $0 输出帮助信息。
3、用 ${1:-default} 给参数设置默认值。
4、用 $? 检查关键命令是否成功。
5、用 read 补充必须的交互输入。
按这个套路写,脚本的参数入口会清晰很多,也更不容易因为输入异常而出错。