脚本参数
特殊变量引用
$0
当前 shell 脚本的名字$NUM
位置参数,通过位置编号引用命令行上第 NUM 个参数$#
参数个数$*
所有的位置参数,用$IFS
分割,作为一个单一字符串展开/替换(token 数为 1)$@
所有的位置参数,每个参数作为一个字符串分别展开/替换(token 数为位置参数个数)
位置参数可以通过 set 命令和 shift 命令进行控制
set 控制
set 参数列表
将脚本原有位置参数替换为新的位置参数
shift 控制
shift
将脚本原有位置参数整体左移,删除原有的 $1
参数
数组
数组支持是 POSIX 标准一部分,但是不同的 shell 最数组的支持和拓展不一致
如 zsh 是从 1 开始,bash 从 0 开始
array=(a b c)
array=()
array[0]=a
array[1]=b
array[100]=c
访问元素
下标从 0 开始,-1 表示最后一个元素
echo ${array[0]}
array[1]=x
获取数组所有元素
echo ${array[*]}
echo ${array[@]}
获取数组长度
echo ${#array[*]}
echo ${#array[@]}
删除元素或者整个数组
unset array[1]
unset array
*
和@
的区别,和前面类似。*
用数组元素用$IFS
分割,作为一个字符串展开/替换。@
是将每个元素视为独立个体,独立展开(加双引号:每个元素作为独立带引号的字符串展开)
最佳实践:仅使用
"${array[@]}"
和"$@"
,在知道作用的前提下使用其他的
- 没有双引号的时候会被 IFS 切开来
@
本来就是分成多个 token 的,*
本来就是一个 token
分支
if 分支
if <condition>
then <command>
[elif <condition>
then <command>]
...
[else
<command>]
fi
回车的地方可以用分号替代
这个 if 语句的返回值是最后一条命令的返回值
最佳实践:如果是数字相关的测试,利用
$(())
得到真值,再用=
比较字符串 1
数字以 0 开头会被认成 8 进制,需要注意
case 分支
case WORD in
[PATTERN [|PATTERN] ...)
COMMANDS
;;]
....
esac
WORD 以字符串形式与给出的 PATTERN 进行匹配
;;
类似于 break
*)
为 defalt
PATTERN 支持路径通配符,有或 |
(优先级最低)
返回值是最后一条命令的返回值
循环
for
for ITERM [in ITERM-LIST]
do
COMMAND
done
in ITERM-LIST
如果未给出,则是 $@
ITERM-LIST 的分隔符为 $IFS
默认为空格、tab、回车
常见的形式有;
1 2 3 4
{1..9..2}
"${array[@]}"
while
while condition
do
COMMAND
done
until
until condition
do
COMMANDS
done
在判断条件上和 while 相反
break
break [num]
break 的循环层数
continue
continue [num]
continue 循环层数
函数
函数不能被 export,可以给出 return 语句,返回一个 0 到 255 之间的整数,也可以没有返回值,最后一条语句
# 方式 1(推荐)
my_func() {
echo "Hello, $1"
}
# 方式 2
function my_func {
echo "Hello, $1"
}
$FUNCNAME
获取函数名($0
不是函数中的局部变量,仍然是外部的东西,如 bash)
调用者可以使用 $?
获取函数的返回值
局部变量
local name=value
作用域限制在函数内,有 shadow
删除函数
unset -f funciton_name
函数的 shell 退出的时候,函数同样会失效
shell 选项
- u nounset 将对未定义的选项的引用作为错误
- pipefail 管道命令组执行中如果有任何一个运行出错,则整个执行失败
- errexit 有命令执行失败立即终止
- noexec 不真正执行脚本,而是检查语法
- C noclobber 不允许重定向覆盖到已经存在的文件
- v verbose 在执行命令前输出替换完成前的命令
- x xtrace 在执行命令前输出替换完成后的命令
设置方法
使用 set 命令
set -x # 打开当前shell的x选项
set -o xtrace
set +x # 关闭
set +o xtrace
启动的时候设置
bash -o nounset
bash -e script.sh #打开-e选项
查看当前 shell 打开的所有选项
echo $-
所以调试的时候尽量全打开
终端控制
终端可以认为是显示器 + 键盘,可以包括控制台 console(每台电脑只有一个,启动信息输出到这里)、物理终端 tty,虚拟终端 tty16(可以通过 pts<ctrl+alt+F1~F6>
切换),伪终端 ptmx
查看终端设置
➜ ~ stty -a
speed 9600 baud; 25 rows; 96 columns;
lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl
-echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
-extproc
iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8
-ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
-dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V;
min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
stop = ^S; susp = ^Z; time = 0; werase = ^W;
stty -echo;read aaaa;stty echo;echo $aaaa
终端特殊字符能够美化,但是对于不同终端字符可能有区别,这个时候就有
terminfo 数据库,使用 tput 命令输出指定功能的转义控制字符
tput clear
tput bel
这些是带状态的,可以通过 tput reset
恢复默认的状态
当你执行 tput
命令时,它会执行以下操作:
- 确定终端类型:
tput
首先会尝试从环境变量$TERM
中获取当前终端的类型。你也可以使用-T
选项显式指定终端类型。 - 查询 terminfo 数据库:
tput
接着会在 terminfo 数据库中查找与该终端类型匹配的条目。terminfo 数据库通常位于/usr/share/terminfo
目录下,并按照终端名称的首字母分级存放。 - 获取功能代码: 根据你提供的
tput
参数(即你想要执行的操作,比如clear
,cup
,bold
等),tput
会在找到的终端条目中查找对应的 capability name(功能名称)。每个 capability name 都关联着一个特定的控制序列。 - 输出控制序列: 如果找到了对应的 capability name,
tput
会将与其关联的控制序列输出到标准输出。这些控制序列是终端能够理解的指令,当你的 shell 或其他程序将这些序列发送给终端时,终端就会执行相应的操作。
常用 tput
命令示例及原理:
- 清屏 (
tput clear
):clear
是一个 capability name,它在 terminfo 数据库中对应着当前终端清屏的控制序列。- 执行
tput clear
会输出该控制序列,通常是 ANSI 转义序列\033[2J
。 - 当你的终端接收到这个序列时,它会清除屏幕上的所有内容并将光标移动到左上角。
- 移动光标 (
tput cup <row> <column>
):cup
(cursor position) 是一个需要参数的 capability name,<row>
和<column>
分别代表行号和列号(通常从 0 开始计数)。tput cup 5 10
会在 terminfo 数据库中查找cup
对应的控制序列,并将其中的参数替换为5
和10
。例如,对于 xterm 终端,这可能会生成\033[6;11H
。- 终端接收到这个序列后,会将光标移动到指定的行和列。
- 设置文本样式 (例如,粗体
tput bold
):bold
是一个 capability name,对应着设置文本为粗体的控制序列。tput bold
会输出该序列,例如\033[1m
。- 终端接收到后,后续输出的文本将会以粗体显示,直到遇到关闭粗体的控制序列 (
tput sgr0
,对应\033[0m
)。
- 设置下划线 (
tput smul
):smul
(start underline mode) 对应开始下划线的控制序列,例如\033[4m
。- 使用
tput rmul
(reset underline mode,对应\033[24m
) 关闭下划线。
- 获取终端信息 (例如,列数
tput cols
):cols
是一个 capability name,对应着终端的列数。tput cols
会直接输出一个数字,表示当前终端的宽度(列数)。