基础编程
1. 创建
在文件的第一行指定要使用的运行方式 #!/bin/bash
系统会按照第一行!后指定的运行方式运行整个文件内容。
如果不写的话,系统可能会按照当前登录的shell执行,但只是可能,Ubuntu系统很可能出问题。
所以第一行指定运行方式是一种良好的习惯。
2. 显示与输入
输入
read命令 等待用户的输入,并将获取的数据存到一个变量中
read -p “Enter your name: ”name
其他参数:
-t 指定超时时间
-s 隐藏方式读取
显示
echo “hello world” 自动动添加换行符
控制输出不换行: -n 参数
让特殊转义符起作用 -e 参数
转义符一览
\b 其后有字符时,作为退格键
\c 取消输出行末的换行符
\e ESCAPE 键
\f 换行后连接上一行的结尾
\v 垂直制表符
\n 换行符
\r 光标移至行首
\t 制表符,也就是 Tab 键
printf “hello %s %f\n” world 11.11 不自动换行,需要手动加\n,使用类似C语言printf
printf 可以使用%对输出间距进行控制 使用间距控制,我们不用再担心显示输出时因变量长度的不确定性导致的混乱问题
%-10s 左对齐,显示宽度为10个字符的字符串,不足使用空格补齐
%10s 右对齐,显示宽度为10个字符的字符串,不足使用空格补齐
%10.2f 右对齐,显示宽度为10个字符的浮点数,小数点后面保留两位
重定向
输入和输出进程在Linux也是以文件的概念存在
Linux用文件描述符来标识每个文件对象,它是一个非负整数
每个进程一次最多可以有九个文件描述符(可以理解为指针变量),而bash shell保留了前3个。
文件描述符 缩写 描述
0 STDIN 标准输入
1 SIDOUT 标准输出
2 SIDERR 错误输出
符号(可以理解为把符号左边的内容传送到右边)
覆盖 >
追加 >>
临时重定向与永久重定向
临时
将标准输出重定向到文件 Command > file
将错误输出重定向到文件 Command 2> file
将所有输出重定向到文件 Command >file 2>file 或者 Command &>file
永久
exec命令: exec 1>file
exec命令会在脚本执行期间保持设定的重定向策略
永久重定向之后想再改回来怎么办?可以使用后6个自定义文件描述符
exec 3>&1
exec 1>file
exec 1>&3
既要显示又要存到文件
tee filename 覆盖
tee –a filename 追加
高级显示控制 使用SGR转义码
格式为 CSI[nm
CSI 代表转义标志字符
实际上就是ESC的ASCII值
产生方法:
Linux下编辑时的产生方法
Ctrl+v 后按 ESC Linux系统显示为 ^[
非Linux下编辑时的产生方法
\033 echo 需配合-e参数
echo -e "hello \033[31;47mworld\033[0m"
m 代表此语句为SGR转义码
[ 半中括号为固定格式作为分隔
n 代表控制码,可以有多个,使用 ; (分号)进行分隔
效果控制码 颜色控制码
代码 描述 两 代码 描述
0 重置为普通模式 位 0 黑色
1 设置为强亮度 数 1 红色
一 2 设置为弱亮度 3x 2 绿色
位 3 使用斜体 前 3 黄色
数 4 使用单下划线 景 4 蓝色
字 5 使用慢闪烁 4x 5 洋红色
6 使用快闪烁 背 6 青色
7 背景色和前景色反转 景 7 白色
8 将前景色设置为背景色(文字不可见 色
3. 变量
使用变量的方法:
1. $variable 美元符号后紧跟变量名
2. ${variable} 使用大括号
以上两种方法没有本质区别。
使用大括号的优点是:可以在调用变量后紧跟字符
比如: $ab 表示变量ab的值,而${a}b 表示变量a的值后紧跟b字符
命令替换:
1. $( ) 美元符号后加小括号
2. `` 反引号
小括号和反引号中的字符会作为运行命令,其输出值作为返回值。
环境变量:
存储有关shell会话和工作环境的信息。
这项特性允许你在内存中存储数据,是存储持久数据的一种简便方法。
用户自定变量:
定义方法:
变量名=value 变量名='value' 变量名="value"
注意事项: 等号两边不能有空格
特殊变量:
$0 执行时脚本本身的路径与名称
$1 ~ $n 表示脚本的第n个参数
$# 脚本运行时携带的命令行参数的个数
$* 脚本的所有参数组成一个单词
$@ 脚本的所有参数当做一个字符串的独立单词
$? 上一个命令的执行状态
$$ 脚本运行的当前进程的ID号
$! 后台运行的最后一个进程的ID号
局部变量
使用普通方式定义的变量都是全局变量
如果要在函数中定义局部变量,需要使用 local 关键字
数组
定义方法:
array=(one two three four five)
使用:
echo “${array[2]}”
删除:
删除单个值 unset ${array[2]}
删除整个数组 unset array
4. 结构化命令
if命令
if command 或 if command;then
then
command
elif 或 elif;then
command
else
command
fi
Command 部分是Linux下的命令,而if测试的是此命令执行完毕的退出码
使用格式 test condition 可以测试其他的条件,比如数值比较之类
linux提供了test命令的简便用法 [(空格) 待比较条件 (空格)] 代替test命令
使用示例:
If [ $a –eq 1 ]
condition部分分类
数值比较
-eq 等于
-ne 不等于
-gt 大于
-lt 小于
-ge 大于等于
-le 小于等于
字符串比较
-z Str “Str” 的长度为零则为真。 [ -z "$aaa" ] [[ -z $aaa]]
[ -n Str ] or [ Str ] “Str” 的长度为非零 non-zero则为真。
[ Str1 == Str2 ] 如果2个字符串相同则为真。
[ Str1 != Str2 ] 如果字符串不相等则为真。
[ Str1 < Str2 ] 如果 “Str1” sorts before “Str2” lexicographically in the current locale则为真。
[ Str1 > Str2 ] 如果 “Str1” sorts after “Str2” lexicographically in the current locale则为真。
文件比较
-a FILE 如果 FILE 存在则为真
-b FILE 如果 FILE 存在且是一个块文件则为真。
-c FILE 如果 FILE 存在且是一个字特殊文件则为真。
-d FILE 如果 FILE 存在且是一个目录则为真
-e FILE 如果 FILE 存在则为真
-f FILE 如果 FILE 存在且是一个普通文件则为真
-g FILE 如果 FILE 存在且已经设置了SGID则为真。
-h FILE 如果 FILE 存在且是一个符号连接则为真
-k FILE 如果 FILE 存在且已经设置了粘制位则为真
-p FILE 如果 FILE 存在且是一个管道则为真
-r FILE 如果 FILE 存在且是可读的则为真
-s FILE 如果 FILE 存在且大小不为0则为真
-t FD 如果文件描述符打开且指向一个终端则为真
-u FILE 如果 FILE 存在且设置了SUID则为真
-w FILE 如果 FILE 存在且是可写的则为真
-x FILE 如果 FILE 存在且是可执行的则为真
-O FILE 如果 FILE 存在且属有效用户ID则为真
-G FILE 如果 FILE 存在且属有效用户组则为真。
-L FILE 如果 FILE 存在且是一个符号连接则为真
-N FILE 如果 FILE 存在并已经被修改 如果ied since it was last read则为真
-S FILE 如果 FILE 存在且是一个套接字则为真。
FILE1 -nt FILE2 如果 FILE1比FILE2新或 FILE1 存在 FILE2 不存在则为真
FILE1 -ot FILE2 如果 FILE1 比 FILE2 要老,或者 FILE2 存在 FILE1 不存在则为真
FILE1 -ef FILE2 如果 FILE1 和 FILE2 指向相同的设备和节点号则为真
复合条件测试: []&&[] []||[]
双括号 (( expression ))
Expression 中除了支持之前提到的比较运算符,还提供了如下预算符
val++ 后增
val-- 后减
++val 先增
--val 先减
! 逻辑求反
~ 位求反
** 幂运算
<< 左移
>> 右移
& 按位与
| 按位或
&& 逻辑与
|| 逻辑或
双方括号 [[ expression ]]
提供了字符串比较的高级特性
特殊字符不需要转义( >, >>, <, <<等 )
可以进行模式匹配
if [[ $USER == r* ]]
case命令
case variable in
pattern1 | pattern2)
commands0
commands1;;
pattern3)
commands2;;
*)
commands;;
esac
注意事项:
选项列表使用右半括号 )
每个选项下的最一条命令后用两个分号作为结束 ;;
Case结束之后使用esac结尾
for命令
for var in list
do
commands
done
注意事项:
list参数:需要迭代的一系列值,
在每次迭代时,var等于列表中的当前值
最后一次迭代完成之后,var会保留最后的迭代值
while命令
while test command
do
other commands
done
意义:
只要成立,就循环0
注意事项:
test command中可以有多个命令,但是只把最后一个命令的返回值作为判断条件
Test command和if 命令中一模一样,不做解释
until命令
until test commands
do
other commands
done
意义:
只要成立,就停止,否则就循环
5. 函数
创建
function func1 {
commands
}
func2() {
commands
}
和C语言的函数一样,使用函数需在定义函数后
同名函数会覆盖之前的定义,没有报错
运行
bash shell会把函数当做小型脚本执行,运行结束之后会返回一个退出状态码
函数退出
退出状态码有三种生成方式
默认退出状态码
函数中最后一条命令返回的退出状态码
使用特殊变量$? 获取
使用return命令
直接获取函数的输出
函数入参
函数传入参数的方法和给shell脚本传入参数的方法是一样的
作为入参的特殊变量是局部的,只在函数内部起作用。
shift命令会根据它们的相对位置来移动命令行参数,移出的参数值被丢弃
当你不知道到底有多少参数时,可以使用shift命令,只需操作第一个参数
$0 不会被移动。
高级编程
shell父子关系
我们在登录某个linux终端时,默认是以命令行的形式,这个命令行就是一个shell提供的CLI提示符
我们在CLI提示符后输入的命令,都是由当前shell去执行。
那么,如果我们再输入一个bash命令,或者其他的shell命令,会怎么样呢?
其实bash命令已经重新创建了一个shell,我们需要借助ps命令查看。
我们称新建立的shell为子shell,原来的shell为父shell。
子shell仍然可以继续建立子shell。
需要注意的是
生成子shell的成本不低,速度还很慢,子子孙孙的嵌套更是很占用资源。
在生成子shell时,只有部分父进程环境被复制到子shell环境中。
其他诸如协程、进程列表相关内容,请自行学习。
脚本控制-信号
进程的信号就是预定义好的一个消息,进程能识别它并决定忽略还是作出反应。
Linux利用信号与运行在系统中的进程进行通信,包括停止、启动、终止进程等等。
有哪些信号:
信号 值 描述 简单描述
1 SIGHUP 挂起进程
2 SIGINT 终止进程 Ctrl + C
3 SIGQUIT 停止进程
9 SIGKILL 无条件终止进程
15 SIGTERM 尽可能终止进程
17 SIGSTOP 无条件停止进程,但不是终止
18 SIGTSTP 停止或暂停进程,但不终止 Ctrl + Z
19 SIGCONT 继续运行停止的进程
Ctrl + Z暂停进程后恢复的方法
jobs 查看当前暂停的进程
bg %N 将第N个进程恢复到后台
fg %N 将第N个进程恢复到前台
其他信号:
Ctrl + S : 锁定屏幕
Ctrl + Q : 恢复显示
信号捕获 我们可以主动捕获到上述信号并执行其他的命令
Trap命令:
启动捕获 : Trap commands signals
移除捕获 : Trap --(-) signals
有助于脚本调试的方法
-n 不要执行脚本,仅检查语法错误
-v 在执行脚本前,先将脚本内容输出到屏幕上。
-x 将执行到的脚本语句,输出到屏幕上。
其他常用方法
在脚本中读入文件
有时,在脚本中使用管道等处理大量文件时不够方便
我们可以将要处理的数据存入文件中,然后一行一行的读出来处理。
本质:while 和 read 命令的高级使用方法。
while read line
do
echo $line
done < "./file"
重要的原则
重定向,必须在尾部
对循环重定向的输出,可适用于循环中所有需要向标准输出写数据的命令。
对循环重定向的输入,可适用于循环中所有需要从标准输入读数据的命令。
当在循环内部显式的使用重定向时,内部重定向覆盖外部重定向。
创建选单
本质: select 与 case 的配合使用
Select : 向用户提供选项,供用户选择。将选择结果 的字符串存入变量中。
PS3 : 使用select时的提示符
PS3="Enter option: "
select option in "one" "exit" ;
do
case $option in
"one")
echo -e "\nselect one\n"
;;
"exit")
break
;;
esac
done
脚本参数补全
原理
Compgen –W - 从选单中匹配候选单词列表
complete - 说明命令如何进行补全
-F 指定执行函数名 , 候选结果保存在COMPREPLY变量中
-f 补全文件名
-X 过滤表达式
-o 补全类型(filenames, dirnames)
示例
complete -o default -F _test_tab_func test.sh
_test_tab_func()
{
local cur prev opts
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
opts="-help -usage -version -vvv "
if [[ ${cur} == * ]];then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
生效方法
将刚才编写的代码,加入~/.bashrc 等(不推荐)
将上述代码,写入/etc/bash_completion.d/下,与命令名相同的脚本中。