linux_shell编程
SHELL编程
编程语言
机器语言 01010101
低级语言(汇编语言) 助记词(转义器 将助记词编译成机器语言)
高级语言 跨平台
编译型(编译器) 将标准文档转成对应平台的汇编
C / C++
解析型(解析器) 将文档实时转义成平台指令
Shell PHP Python JavaScript, GO
编译解析型(编译器/虚拟机)
Java C# PHP Python JS, 易, OC, swift
编程入口
编辑文档, 文档的第一行开始,逐行解析
SHELL解析器 /bin/bash
#!/bin/bash
快捷键
快捷键模式
emacs 默认
ctrl + a
ctrl + e
ctrl + d
...
vi
临时更换模式: set -o vi
tab键补全
特殊符号
* 通配符
\ 转义符, 去除后面字符的特殊意义
"" 批量转义符, 去除引号引起来的内容大部分特殊字符的特殊意义
'' 批量完成转义符, 去除引号引起来的内容所有特殊字符的特殊意义
`` 引起来内容当作shell命令去执行并且将执行标准输出替换, 尽量使用$()
$()可以嵌套, 反引号不能嵌套
``和$()都是开新进程去执行
{} 展开,代表多个的意思
字符串截取
数组的增. 删. 改. 查
变量的默认值
正则表达式匹配
awk
() 子程序
$()
& 将程序放入后台执行(多进程)
! 命令历史特殊符号
~ 当前登录用户的家目录
/ 根
# 注释符
- 选项(标准输入)
$ 组合使用, 访问变量
| 管道
< > >>.. 重定向
. 当前目录
.. 上一级目录
[ ] 判断表达式
: 永真(if while)
; 一条命令的结束
&& 逻辑与
前面的是"真"的才是会执行后面的
|| 逻辑或
前面的是"假"的才是会执行后面的
命令历史
查看命令历史
history
history 10
存储命令历史
history -w /tmp/history.txt
读取命令历史
history -r /tmp/history.txt
删除命令历史
history -c
执行命令执行
执行最后一条命令历史
!!
执行109的命令
!109
执行root开头的命令
!root
获取最后一条命令历史的最后一个参数
!$
命令历史的环境变量
HISTFILE 控制命令历史默认写入的文件(~/.bash_history)
echo $HISTFILE 打印环境变量的值
HISTFILESIZE 控制命令历史写入文件的最大条目
echo $HISTFILESIZE 打印环境变量的值
HISTSIZE 控制命令历史最大条目数
HISTTIMEFORMAT 控制命令历史时间显示格式
export HISTTIMEFORMAT="%F %T "
命令别名 alias
简化命令的输入, 只影响当前终端
创建命令别名
格式: alias 别名='执行命令名字及参数'
例:
alias i='ifconfig enp0s25'
alias p='ping -c 3 192.168.2.1'
alias pb='ping -c 3 www.baidu.com'
删除命令别名
格式: unalias 别名
查看命令别名
alias
**环境变量**
提升程序运行效率, 将系统环境相关的信息存储到变量里,减少与内核交互
会影响子进程(将自己的环境变量复制给子进程)
输出当前shell所有环境变量
env
访问环境变量的值
echo $环境变量名
定义和修改环境变量的值(PPP0)
export 环境变量名=值
删除环境变量
unset 环境变量名
配置文件
bash启动时会执行配置文件(shell脚本)
/.bashrc/.bash_history
当bash结束时会自动将命令历史写入
学习编程语言的流程:
了解准备学习的语言概述
编程入口
变量的使用(数据类型)
操作符(操作变量) -> 语句
流程控制语句(分支 循环)
函数(代码的打包)
数组
字符串处理
I/O文件操作
变量
变量默认没有数据类型之分, 一切皆于字符串
变量 : 以一组文字或符号来替换一些比较复杂或者容易变动的数据
变量的用途
简单的用途就是为了方便搜索
常用于编程里,对经常使用的值使用变量,方便引用
变量的分类
自定义变量 不影响子进程
环境变量 影响子进程
自定义变量转环境变量
export 自定义变量名
变量命名规则:
严格区分大小写,同名称但大小写不同的变量名是不同的变量
变量名可以是数字 字母特殊字符进行组合,
但不能以数字和特殊字符开头, 最好别用下划线开头更不能以纯数字为变量名
定义变量时两边不能有空格,有空格的字符串要用引号引起来
单引号和双引号的区别,单引号会转义所有特殊字符
尽可能描述变量存储的值(见名知意)
定义变量
变量名=值
=两边不能有空格
例:
a=123
a="hello world"
a=34.6778
特殊的变量定义方式:
var=`ls -l` 把ls -l的标准输出结果赋给var变量
a=$(ls -l) 将命令执行标准输出赋值给a变量
更新变量值
变量名=新值
num1=2333
num2=$(($num1 + 1000)) 结果是3333
num2=$[num1 + 1000]
let num2=($num1 + 1000) 结果是3333
str1="hello world "
str2=$str1"hello shell" hello world hello shell
变量名+=追加值
获取变量值(PPP1)
$变量名
或
${变量名}
删除变量
unset 变量名
${}的特殊访问
解决变量与其它字符串连接混乱问题
按位置进行截取(PPP2)
${变量名:起始值:截取个数}
${变量名:起始值:-从后面定位结束值}
${a:7} 从第8个开始到结束
${var:0-7:3} 从倒数第7个开始, 截取3个
${var:0-7} 从倒数第7个开始, 截取到最后
按关键词进行截取(PPP3)(PPP4)
${变量名#*关键词} 截取第一个关键词后面内容
${变量名##*关键词} 截取最后一个关键词后面内容
${变量名%关键词*} 截取最后一个关键词前面内容
${变量名%%关键词*} 截取第一个关键词前面内容
获取字符串长度
${#变量名}
替换(PPP5)
${变量名/查找关键词/替换关键词} 替换第一个关键词
${变量名//查找关键词/替换关键词} 替换全部关键词
变量默认值(PPP6)
echo ${变量名-默认值}
此变量名值为空或不存在,默认值才会使用
num=${1-$0}
source或 . 内部命令, 使用本进程执行脚本, (默认情况下执行程序都开新进程)
内置变量
$$ 代表本脚本的PID(进程标识符) (PPPz)
终端的pid是这个终端运行的脚本的PPID,
这个脚本中运行的sleep命令会开启子进程去运行,
脚本退出之后,里面sleep进程还是会继续运行, 只是变成了孤儿进程
1 | $0 代表本脚本名 |
输入/输出语句(PPP7)
echo/printf
echo(见练习echo)
-n 不换行输出
-e 启用反斜杠转义解释
\c 最后不加上换行符号
printf "%d%s\n" 34 "hello"
"%10d" 按10个空位右对齐, 整个字符占10个位置, 不足补空格
"%-10d" 按10个空位左对齐, 整个字符占10个位置, 不足补空格
"%010d" 按10 个空位右对齐, 前面补0
"%10.2f" 按10个空位右对齐, 保留两位小数点
read
-p 指定输入提示信息
-s 关闭回显
-n 指定输入最大字符个数
-t 指定超时时间(秒)
如果在read命令行中不指定变量的话,read命令会将它接收到的所有参数放到特殊环境变量REPLY中。
read var 等待用户给var变量赋值
read -p "input your name:" var 提示信息等待用户给var变量赋值
read -p "input your name:" -t 10 var 提示信息等待用户给var变量赋值,10秒不输入的话直接跳过
read -s -p "input your password:" passwd 提示信息等待用户给passwd变量赋值并且隐藏显示
一行多条命令执行
; 以分号分隔多条命令顺序执行
&& 前面命令执行成功才会执行后面命令
|| 前面命令执行失败才会执行后面命令
代码规范(可读性)
加空行 使代码具有段落感
解析器和第一行代码之间要加空行
假如有连续定义多行变量, 在这之后要加空行
if/while/for/until/case 之间加空行
函数之间加空行
按代码功能分段加空行
缩进对齐 使代码具有层次感
缩进量为4个空格, 遇到if/while/for/until/case语句,里面内容进行缩进对齐
函数内部要缩进
加空格 使代码更清晰
双目操作符两边加空格(有左值右值的操作符)
命名规则 尽量能描述存储数据或功能
首字母小写驼峰法 gameStatus inputNum
首字母大写驼峰法 GameStatus InputNum
下划线连接 game_status input_num
强烈不建议使用中文和拼音
折行 一行不要超过80个字符(包含空白字符)
使用 \ 进行连接行, \后不能跟任何字符
流程控制语句
分支(PPP8)
格式:
if 命令; then
命令成功要执行的代码块
fi
if 命令; then
命令成功要执行的代码块
else
命令失败要执行的代码块
fi
if 命令1; then
命令1成功要执行的代码块
elif 命令2; then
命令2成功要执行的代码块
elif 命令3; then
命令3成功要执行的代码块
...
else
命令失败要执行的代码块
fi
test命令(判断 条件表达式)
test "$a" = "$b"
或
[ "$a" = "$b" ]
或
(("$a" == "$b"))
[[ "$a" == "$b" ]]
得到条件表达式所有帮助
man test
整型数据比较: -le -lt -ge -gt -ne -eq
-eq 等于,如:if [ "$a" -eq "$b" ]
equal
-ne 不等于,如:if [ "$a" -ne "$b" ]
no equal
-gt 大于,如:if [ "$a" -gt "$b" ]
greater than
-ge 大于等于,如:if [ "$a" -ge "$b" ]
greater equal
-lt 小于,如:if [ "$a" -lt "$b" ]
less than
-le 小于等于,如:if [ "$a" -le "$b" ]
less equal
< 小于(需要双括号),如:(("$a" < "$b"))
<= 小于等于(需要双括号),如:(("$a" <= "$b"))
> 大于(需要双括号),如:(("$a" > "$b"))
>= 大于等于(需要双括号),如:(("$a" >= "$b"))
字符串比较: = != == (演练)
= 等于,如:if [ "$a" = "$b" ]
== 等于,如:if [ "$a" == "$b" ],与=等价
!= 不等于, 如: if [ "$a" != "$b" ]
逻辑比较: -a -o !
! 非,后面要跟空格
-o 或, 条件只要达成1个就是真(or)
-a 且,条件必须全部达成才是真(and)
一些特殊的参数:
-a 文件存在。
-e 文件存在(与-a相同)。
-n 字符串长度不是零。
-z 字符串长度为零。
-r 文件存在并且可由当前进程读取。
-w 文件存在并且可由当前进程写入。
-x 文件存在并且可由当前进程执行。
-d 文件存在并且是一个目录。
-f 文件存在并且是一个常规文件。
-s 文件存在且大小大于零。
-b 文件存在,是一个块特殊文件。
-c 文件存在,是一个字符特殊文件。
-G 文件存在,并具有与此进程相同的组ID。
-O 文件存在,并由该进程的用户ID拥有。
-l 文件存在,是一个符号链接。
-p 文件存在,是一个先入先出(FIFO)的特殊文件或命名管道。
-S 文件存在,是一个套接字。
if test -d "/tmp"; then
echo "判断/tmp是否存在且是个目录"
fi
if [ -d "/tmp"]; then
echo "判断/tmp是否存在且是个目录"
fi
对变量最好使用双引号引起来, 保护起来, 以免报语法错误
case(处理菜单使用, 多路匹配)(PPP9)
格式:
简略版:
case "$num" in
A|a) echo "A";;
B|b) echo "B";;
C|c) echo "C";;
*) echo "Other";;
esac
完整版:
case "$num" in
A|a)
echo "A"
;;
B|b)
echo "B"
;;
C|c)
echo "C"
;;
*)
echo "Other"
;;
esac
循环(PPP11 PPP10)
循环控制语句
break 退出循环
continue 跳过本次循环
while(演练目录)
格式:
while 命令(条件表达式); do
循环代码块
done
命令成功或条件表达式为真才会执行循环体
until
格式:
until 命令(条件表达式); do
循环代码块
done
命令失败或条件表达式为假才会执行循环体
for(用for循环去做PPP5)
格式:
for 迭代变量 in 迭代对象 ; do
循环代码块
done
迭代对象是以空格为分隔符序列
for i in 1 2 3 4 5 6 7 8 9 10; do
echo $i
done
for i in 1 2.334 "hello" "1.txt" 5 678.45 7 8 9 10; do
echo $i
done
for ((i = 0; i < 5; i++)); do
echo $i
done
seq
产生一个序列, 可进行迭代
count=18
for i in $(seq 0 $count); do
echo $i
done
函数 打包代码(PPP23 PPP24)
函数定义格式:
[function] 函数名 () {
函数代码块
}
函数调用格式:
函数名 [参数]…
函数就是命令,与命令用法一样
exit和return后面接的必须是0-255的数字
小于0会报错, 且最终的值是一个1-255的随机数字, 大多数是2
大于255, 最终的值是一个1-255的随机数字, 大多数是1
返回的不是数字, 会报错, 且返回的状态码是1-255之间的一个随机数字
函数中出现了return, 执行到这一行的时候就会结束,这个函数下面的代码不会执行了
在脚本中, 碰到exit会直接退出脚本, 下面的代码不会再执行
注意点:
1. shell是面向过程的语言, 从上到下依次执行(类似C语言)
2. 定义的变量要在使用之前,不然值是空的
3. 定义函数要在使用之前, 不然会报错
4. 函数外和函数内通信可以以传参的形式, 改变共同使用的变量
5. 函数内和函数外通信可以使用的标准输出的形式(echo/printf)和return/exit
6. 函数名要和变量名的起名规则是一样的, 做到见名知意
7. 函数不调用不执行
8. 假如函数名和linux的命令起冲突的时候, 会先执行自定义的函数
数组
与变量一样是存储数据
批量操作同类型的数据
SHELL只支持一维数组
数组不能直接作为函数参数进行传递
定义
1. 索引数组
declare -a 数组名
数组名=(值1 值2 值3…)
2. 键值对数组
declare -A 数组名
数组名=([“name1”]=soul [“name2”]=shell)
数组名[键]=值
3. 索引键值对混合数组(强烈不建议使用)
只是shell的语法允许这样做,但是正常使用的时候不要这样用
数组名[键]=值
数组名=(值1 值2 值3...)
访问(演练)
访问指定元素
echo ${数组名[下标]}
下标是正整数, 从0开始描述数组的第几个元素
下标是负整数, 倒数第几个, 从1开始
下标是-0就相当于是0
下标是浮点数会报错
echo ${数组名[键名]}
访问所有元素
echo ${数组名[*]}
echo ${数组名[@]}
访问数组长度
${#数组名[*]}
${#数组名[@]}
访问数组所有键名
${!数组名[*]}
${!数组名[@]}
索引数组的键名类似于{0..n}
更新
更新数组的指定元素
数组名[下标]=新值
数组名+=(追加值1 追加值2...)
给数组追加一个变量
a=(1 2 3 4)
num=5
a[${#a[*]}]=$num
删除(PPP12)
删除数组指定元素
unset 数组名[下标]
只是清空对应下标元素的值, 并不会改变数组其它元素的位置
a=(1 2 3 4 5)
len1=${#a[*]} <-- 5
unset a[3] <-- 删除下标为3的元素
len2=${#a[*]} <-- 4
for i in "${a[*]}"; do
echo "$i" <-- 这种算法不能把a[3]的实际值打出来
这个时候打印的第4个数字只是第4个数字
而不是a[3]的值
done
x=0
for i in "${a[*]}"; do
echo "下标为$x""的值是: ${a[$x]}" <-- 这个时候打印的第4个数字才是a[3]的值
let x+=1
done
删除整个数组
unset 数组名
declare
定义只读变量(常量)
declare -r 只读变量名=值
定义索引数组
declare -a 数组名
定义键值对数组
declare -A 数组名
定义自动转化大写的变量
declare -u 变量名
给此变量名赋值,会自动转成大写
定义自动转化小写的变量
declare -l 变量名
给此变量名赋值,会自动转成小写
定义环境变量
declare -x 环境变量名
变量作用域
$* 和 $@的区别: 它们的区别在于使用双引号, $*代表一个元素, $@代表多个元素
函数直接可以访问和修改函数外的变量
shell里的变量都是全局变量, 在函数里定义的变量在函数调用后也可以直接访问
在函数内可以使用local关键词定义局部变量 (只能在函数中使用)
查找命令
which 查找$PATH变量指定的目录里的命令
which ifconfig 查出ifconfig命令的路径
whereis 查找命令,显示命令更多的信息
whereis ifconfig 命令路径,帮助文档等
locate 文件查找命令
速度快,通过系统自带的一个数据库去查找
/var/lib/mlocate/mlocate.db
locate hello 查找带hello的文件,如果hello是刚刚新建的就找不到,因为数据库还没有没保存现在的信息
updatedb 手动更新查找数据库,然后再查找就会很快找到刚刚新建的hello文件
find
文件查找命令,功能最强大,速度慢,因为会扫描整个查找的范围
find 范围 参数 关键字
find /etc -name grub.conf 查找/etc目录下的grub.conf文件
find / -name "*.conf" 查找/下所有.conf文件
find / -iname grub.conf 查找/目录下的grub.conf文件,忽略大小写
find / -maxdepth 2 -name grub.conf 可以使用-maxdepath参数来控制查找的层次,就是说只查当前目录和子目录,最多查2级目录
find / -mindepth 2 -name grub.conf 最少查二级目录
find /etc -type d 查找/etc/下所有的目录
find /etc -type f 查找/etc/下的所有普通文件
find /etc -type l -name "*.conf" 查找/etc/下软链接文件是.conf结尾的文件
find /etc -type s 查找/etc/下所有socket文件
find /etc -type c 查找/etc/下的所有字符设备文件
find /etc -type p 查找/etc/下所有管道文件
find /etc -user root 查找/etc/所属用户是root的文件
find /etc -group root 查找/etc/所属用户组是root的文件
find /etc -uid 500 查找/etc/下uid是500的文件,和-user类似
find /etc -gid 500 查找/etc/下gid是500的文件,和-group类似
find /etc -nouser 查找没有所属用户的文件
find /etc -nogroup 查找没有所属用户组的文件
find /etc -perm 777 -type d 查找/etc/下权限为777的目录
find . -perm 111 查找权限是111的文件
find . -size +10M 查找当前目录下大于10M的文件,单位可以有K,M,G,b等
find / -size -2M 查找根目录下少于2M的文件
find / -size +10M -size -100M 查找跟目录下面大于10M且小于100M的文件
find / -mtime 1 查找根目录下1小时以前修改的所有文件
find / -mtime +2 查找根目录下2个多小时以前修改的所有文件
find / -mtime -3 查找根目录下最近3小时内修改的所有文件
find / -atime 1 查找根目录下1天以前访问或读过的所有文件
find / -atime -1 查找根目录下最近1天内读过或访问的文件
find / -ctime -3 查找根目录下最近3天内状态发生改变的文件
find / -cmin -3 查找根目录下最近3分钟内状态发生改变的文件
find / -empty 查找根目录下所有空白文件或者空目录
find / -false 查找根目录下总是错误的文件
find / -false -exec ls -l {} \; 查找根目录下总是错误的文件并且用ls -l查看
exec
把前面命令的输出作为后面命令的参数
对find出来的文件进行进一步的操作
用ls -hl格式化输出/root下面属于root的大于10M的普通文件
find / -size +10M -user root -type f 2> /dev/null -exec ls -hl "{}" \;
PPP13
文本处理工具
wc 统计命令
wc -c /etc/passwd 统计/etc/passwd文件里有多少个字符
wc -w /etc/passwd 统计/etc/passwd文件里有多少个单词
wc -l /etc/passwd 统计/etc/passwd文件里有多少行
sort 排序命令
sort -f /etc/passwd 忽略大小写排序
sort -b /etc/passwd 忽略最前面的空格符部分
sort -n /etc/passwd 按数字大小排序
sort -u /etc/passwd 去除重复行显示
sort -r /etc/passwd 反向排序
sort -n -k5 /etc/passwd 指定第五列进行按数字大小排序
sort -t: -n -k5 /etc/passwd 以:为分隔符指定第五列按数字大小排序
cut 字段截取
cut -d: -f1 /etc/passwd 以:为分隔符, 只显示第一列数据
cut -d: -f1,2,5 /etc/passwd 只显示第一列,第二列,第五列的数据
cut -d: -f1,2 /etc/passwd 以:为分隔符只显示第一列,第二列的数据
uniq 去除文件中相邻的重复行
uniq -u /etc/passwd 只显示没有被重复过的行
重复行不显示
uniq -d /etc/passwd 只显示被重复过的行
重复的行中间只显示一行
uniq -i /etc/passwd 忽略大小写去除文件中相邻的重复行
重复的行中间只显示一行
uniq -c /etc/passwd 统计相邻重复行数
diff 比较文件差异
diff -B /etc/passwd passwd 忽略空行造成的不同
3a4,6 表示4行和6行文件内容不一样,
> i 表示不一样的字符内容
>
> 9
-c 显示全部内文,并标出不同之处,用!号标出
diff -c /etc/passwd passwd
说明:
"|"表示前后2个文件内容有不同
"<"表示后面文件比前面文件少了1行内容
">"表示后面文件比前面文件多了1行内容
tr 替换字符
cat /etc/passwd | tr a b 查看/etc/passwd文件并把里面的a替换成b
tr a-z A-Z < /etc/passwd 把/etc/passwd文件里的小写全转成大写
不会修改源文件,输出到标准输出
grep 文本查找(查找到结果打印对应行)
格式: grep 参数 关键词 [文件路径]...
当有参数r的时候, 没有写文件路径的话就是在当前文件
参数
-r 递归查找文件
-n 输出行号
-i 忽略大小写
-l 只输出路径
-w 完全匹配
-q 查找的结果不输出
-A 输出结果后面的行(包括查找结果行)
-B 输出结果前面的行(包括查找结果行)
-v 输出没有关键词的行
-o 代表的是只输出匹配的选项。
在passwd文件中查找root输出对应行内容
grep root /etc/passwd
在passwd文件中查找root输出对应行内容并且带行号
grep -n root /etc/passwd
在passwd文件中查找nfs输出结果后3行(包含查找结果行)
grep -A 3 nfs /etc/passwd
在passwd文件中查找nfs输出结果前3行
grep -B 3 nfs /etc/passwd
在passwd文件中查找nfs输出结果前3行后2行
grep -A 2 -B 3 nfs /etc/passwd
在多个文件中查找root关键词并且带颜色输出结果(文件名:行号:行内容)
grep -rn root ./
在一个目录下所有文件查找root关键词
grep -r root ./
在一个目录下的所有文件查找root关键词,只输出有结果的文件路径
grep -rl root ./
查找没有root关键词的行 小写-v
grep -v root /etc/passwd
查找多个关键词(逻辑或的关系)
grep --color=auto -e 'bin' -e 'nologin' /etc/passwd
查找单词(关键词)
grep -w root /etc/passwd
忽略大小写查找
grep -i root /etc/passwd
关闭输出查找(用于shell判断使用)
grep -q root /etc/passwd
使用扩展正则表达式(egrep)
grep -E 'a+' /etc/passwd
或
egrep 'a+' /etc/passwd
2 : egrep = grep -E 可以使用基本的正则表达外, 还可以用扩展表达式. 注意区别.
扩展表达式:
+ 匹配一个或者多个先前的字符, 至少一个先前字符.
? 匹配0个或者多个先前字符.
a|b|c 匹配a或b或c
() 字符组, 如: "love(able|ers)" 匹配loveable或lovers.
(..)(..)\1\2 模板匹配. \1代表前面第一个模板, \2代第二个括弧里面的模板.
x{m,n} =x\{m,n\} x的字符数量在m到n个之间.
basename $(pwd) 取得当前目录名
pwd | awk -F ‘/‘ ‘{print $NF}’
dirname $(pwd) 取得当前路径,不包含当前目录名
pwd | sed -nr ‘s/(.)(/)(.)$/\1\2/p’
script_dir=$( cd $(dirname “$0”) && pwd ) 表示脚本所在目录的
tac 倒转文件里的内容
rev 倒转文件里的单词顺序
正则表达式 (字符串匹配 模糊匹配)
名字叫正规表示法
. 代表任意一个字符
r... 代表r开头的任意四个字符
\<root 代表root开头的单词
root\> 代表root结尾的单词
\<root\> 代表完全匹配root单词
\b <-- 单词边界
\B <-- 非单词边界
^ 代表行开头
^root 代表以root开头的行
$ 代表行结尾
root$ 代表以root结尾的行
* 匹配前一个字符0次或n次
a* 匹配0个a到多个a
.* 匹配0个到n个任意字符
+ 匹配前一个字符1次或n次(至少要有一个)
a+ 匹配至少一个a
? 匹配前一个字符0次或1次
a? 匹配一个a或没有a
默认情况下,数量表示符只作用于前面的【一个】字符,如果需要作用于
前面的多个字符,可以使用(...)把前面的字符括起来
ab+ 匹配ab, abb, abbb, abbbb...
(ab)+ 匹配ab, abab, ababab, abababab...
{} 匹配前一个字符指定次数
a{1} 匹配一个a
a{0,1} 与a?功能相同
a{0,} 与a*功能相同
a{1,} 与a+功能相同
a{1,3} 匹配一个a 或 二个a 或三个a
这些数字可以是任意的正整数
[] 字符类 匹配一个字符
[abc] 匹配这个字符必须是a或b或c
[^abc] 匹配这个字符不是abc的其它任意字符
[a-z] 匹配26个小写字母
[0-9] 匹配0到9的字符
[a-zA-Z0-9_] 匹配数字 大小写字母和下划线
[a-zA-Z]+ 匹配单词
[^a-zA-Z]+ 匹配单词分隔符
预定义字符类
[[:alnum:]] 字母和数字的组合,相当于[a-zA-Z0-9]
[[:digit:]] 十进制数字 [0-9]
[[:alpha:]] 字母组合 [a-zA-Z]
[[:blank:]] 空格和制表符
[[:punct:]] 特殊字符集合
...
\ 转义
\. 取消.的特殊意义
\n 代表换行
\d 任意一个十进制数字 [0-9]
\D 任意一个不是数字的字符 [^0-9]
\s 任意一个空白字符 [ \f\n\r\t]
\w 任意一个单词字符 [a-zA-Z0-9_]
\W [^a-zA-Z0-9_]
| 代表或
a|b 匹配a或b
() 子表达式
(root)|(kyo) 匹配root或kyo
默认情况下,数量表示符是最大匹配,好的正则表达式引擎支持用问号 ? 来启用最小匹配
.*b 匹配 aaabababa <-- 最大匹配
^^^^^^^^
.*?b 匹配 aaabababa <-- 最小匹配
^^^^
引用表示法
--------------------------------------------------------------
从左边开始数左小括号(openning brace),数字从1开始,被第一对括号匹配的字符
可以用\1 来引用,第二对可以用\2 来引用,以此类推。
echo abcabcabcaabb | grep -E '(a(bc)){2}\1' --color
abcabcabcaabb
echo abcabcabcaabb | grep -E '(a(bc)){2}a\2' --color
abcabcabcaabb
echo "hello world, hello world, hello beautiful world" \
| grep -E --color '((hello) (world)), \1, \2 .* \3'
hello world, hello world, hello beautiful world
PPP14
sed 流编辑器
格式: sed 参数 "表达式" [操作文件]...
基本格式包含以下部分:
1. 指定范围,不明确指定的话,默认是所有的行,可以是行号,或者正则表达式
2. 指定动作,常用的有d, s, p, i, a
d: delete, 删除
s: substitute, 替换
p: print, 打印
i: insert, 在前面插入
a: append, 在后面添加
选项:
-e 表达式
-n 取消非匹配行的文件输出
-i 操作原文件(慎用)
-r 支持更多正则表达式
() 域, 匹配子表达式,用于替换引用
第一个小括号对应\1来引用
第二个小括号对应\2来引用
...
以此类推
有-r参数, 小括号不需要转义, 没有-r参数需要转义 \(...\)
输出 p(PPP15)
输出第3行内容
sed -n '3p' /etc/passwd
输出第3行和第7行内容,中间为;表示单独的操作
sed -n '3p;7p' /etc/passwd
输出第3行到第5行和第7行内容,中间为逗号,表示范围
sed -n '3,5p;7p' /etc/passwd
输出有root关键词的行
sed -n '/root/p' /etc/passwd
输出以root开头的行
sed -n '/^root/p' /etc/passwd
输出有数字的行
sed -n '/[0-9]/p' /etc/passwd
打印非空格开头的行
sed -n '/^[^[:blank:]]/p' passwd
或
sed -n '/^[^\ ]/p' passwd
增加 i a (PPP16)
在第2行的上面, 也就是第2行加上一行内容
sed -i '2i sfasdfdsf' passwd
在第2行的下面, 也就是在第3行加一行内容
sed -i '2a sfasdfdsf' passwd
i和a的区别:
i是指定几行就在几行加,
a是指定几行就在几行的下一行加
删除 d
删除第3行
sed '3d' mypasswd
删除第10行到第30行
cat -n /etc/passwd | sed '10, 30d'
删除第3行, 影响原文件
sed -i '3d' mypasswd
删除第二行到第三行
cat -n passwd | sed -e '2,3d'
删除第一行和第五行
cat -n passwd | sed -e '1d;5d'
删除空行
cat passwd | sed -e '/^$/d'
删除空格开始的行
cat /etc/passwd |sed -e '/^[[:blank:]]/d' --删除以空格开头的
cat /etc/passwd |sed -e '/^ /d' --同上
cat /etc/passwd |sed -e '/^\ /d' --同上
删除/etc/passwd的空行和注释
cat passwd |sed -e '/^#/d;/^$/d'
...
删除一个字符串中指定位置的字符(PPP17)
删除第3至第5的字符
echo 123456789ABCDEF | sed -r 's/^(..)...(.*)$/\1\2/' 保留第1小括号和第2个的内容
删除第3个字符
echo 123456789ABCDEF | sed -r 's/^(.{2}).(.*)$/\1\2/'
删除从3个字符开始的10个字符
echo 123456789ABCDEF | sed -r 's/^(.{2}).{10}(.*)$/\1\2/'
替换 s(PPP18)(PPP19)
将每行的第一个root替换成soul
sed -e 's/root/soul/' mypasswd
将每行的第二个root替换成soul
sed -e 's/root/soul/2' mypasswd
将每行的所有root替换成soul
sed -e 's/root/soul/g' mypasswd
将每行的开头的空格删掉
cat -n /etc/passwd | sed -e 's/^[ ]*//g'
将1到10行的所有root替换成soul
sed -e '1,10s/root/soul/g' mypasswd
将10到最后行的所有root替换成soul
sed -e '10,$s/root/soul/g' mypasswd
将10到最后行的所有小写字母替换K
sed -e '10,$s/[a-z]/K/g' mypasswd
删除每行中第一个字符
sed -e "s/^.//" mypasswd
将每行中第一个字符替换成A
sed -e "s/^./A/" mypasswd
删除每行中第二个字符
sed -r -e "s/^(.)./\1/" mypasswd
删除每行中第一个有效字符(非空白字符)
sed -e "s/^([[:blank:]]*)./\1/" mypasswd
或者
sed -r -e 's/^([ ]*)./\1/' mypasswd
删除每行最后一个字符
sed -e 's/.$//' mypasswd
从左边开始数左小括号(openning brace),数字从1开始,被第一对括号匹配的字符
可以用\1 来引用,第二对可以用\2 来引用,以此类推。
删除每行倒数第二个字符
sed -r -e 's/.(.)$/\1/' mypasswd
把倒数第一个和倒数二个换成了倒数第一个
删除每行的第二个数字
sed -r -e 's/[0-9]+//2' passwd
练习:
2018-9-24
2018-12-1
2018-1-4
2018-10-1
把上面的日期格式替换成2018-10-01的形式
删除每行第二个单词([a-zA-Z]+)
sed -re 's/^([a-Z]+)([^a-Z])([a-Z]+)/\1\2/' 1_passwd.txt
或者
sed -re 's/[a-Z]+//2' 1_passwd.txt
删除每行第3个非单词
sed -re 's/[^a-Z]+//3' 1_passwd.txt
PPP20
PPP21
查找有线网卡设备名
ifconfig | sed -rn 's/^(e[0-9a-zA-Z]+).*$/\1/p'
截取IP地址(一行命令)
输出结果格式: 192.168.0.183/255.255.255.0
#!/bin/bash
# 再脚本运行的进程里,把语言环境改成英文,就不会受到语言环境的影响
export LANG=en_US.UTF-8
export LANGUAGE=en_US:en
# 截取出正在上网的网卡名称
link=$(route -n | sed -n '/UG/p' | sed -re 's/(.*)(\b[a-Z0-9]+$)/\2/')
或者
link=$(route -n | sed -n '/UG/p' | awk '{print $NF}')
# 截取出ip和子网掩码, 用/分开
ip=$(ifconfig $link | sed -n '/Mask/p' | sed -r 's/^([^0-9]+)([0-9.]+)(.*)([^0-9.]+)([0-9.]+$)/\2\/\5/')
echo $ip
Awk
Awk是一门编程语言,有很多版本,我们用的是GNU的gawk
以:为分隔符打印第三列
awk -F ":" '{print $3}' /etc/passwd
awk 命令的基本格式为:
awk [选项] ‘脚本命令’ 文件名
此命令常用的选项以及各自的含义,如表 1 所示。
选项 | 含义 |
---|---|
-F fs | 指定以 fs 作为输入行的分隔符,awk 命令默认分隔符为空格或制表符。 |
-f file | 从脚本文件中读取 awk 脚本指令,以取代直接在命令行中输入指令。 |
-v var=val | 在执行处理过程之前,设置一个变量 var,并给其设备初始值为 val。 |
awk 的强大之处在于脚本命令,它由 2 部分组成,分别为匹配规则和执行命令,如下所示:
1 | '匹配规则{执行命令}' |
这里的匹配规则,和 sed 命令中的 address 部分作用相同,用来指定脚本命令可以作用到文本内容中的具体行,可以使用字符串(比如 /demo/,表示查看含有 demo 字符串的行)或者正则表达式指定。另外需要注意的是,整个脚本命令是用单引号(’’)括起,而其中的执行命令部分需要用大括号({})括起来。
在 awk 程序执行时,如果没有指定执行命令,则默认会把匹配的行输出;如果不指定匹配规则,则默认匹配文本中所有的行。
举个简单的例子:
1 | awk '/^$/ {print "Blank line"}' test.txt |
在此命令中,/^$/
是一个正则表达式,功能是匹配文本中的空白行,同时可以看到,执行命令使用的是 print 命令,此命令经常会使用,它的作用很简单,就是将指定的文本进行输出。因此,整个命令的功能是,如果 test.txt 有 N 个空白行,那么执行此命令会输出 N 个 Blank line。
awk 使用数据字段变量
awk 的主要特性之一是其处理文本文件中数据的能力,它会自动给一行中的每个数据元素分配一个变量。
默认情况下,awk 会将如下变量分配给它在文本行中发现的数据字段:
- $0 代表整个文本行;
- $1 代表文本行中的第 1 个数据字段;
- $2 代表文本行中的第 2 个数据字段;
- $n 代表文本行中的第 n 个数据字段。
前面说过,在 awk 中,默认的字段分隔符是任意的空白字符(例如空格或制表符)。 在文本行中,每个数据字段都是通过字段分隔符划分的。awk 在读取一行文本时,会用预定义的字段分隔符划分每个数据字段。
所以在下面的例子中,awk 程序读取文本文件,只显示第 1 个数据字段的值:
1 | [root@localhost ~]# cat data2.txt |
该程序用 $1 字段变量来表示“仅显示每行文本的第 1 个数据字段”。当然,如果你要读取采用了其他字段分隔符的文件,可以用 -F 选项手动指定。
awk 脚本命令使用多个命令
awk 允许将多条命令组合成一个正常的程序。要在命令行上的程序脚本中使用多条命令,只要在命令之间放个分号即可,例如:
1 | echo "My name is luke" | awk '{$4="yichen"; print $0}' |
第一条命令会给字段变量 $4 赋值。第二条命令会打印整个数据字段。可以看到,awk 程序在输出中已经将原文本中的第四个数据字段替换成了新值。
除此之外,也可以一次一行地输入程序脚本命令,比如说:
1 | [root@server1 ~]# awk '{ |
在你用了表示起始的单引号后,bash shell 会使用 > 来提示输入更多数据,我们可以每次在每行加一条命令,直到输入了结尾的单引号。
注意,此例中因为没有在命令行中指定文件名,awk 程序需要用户输入获得数据,因此当运行这个程序的时候,它会一直等着用户输入文本,此时如果要退出程序,只需按下 Ctrl+D 组合键即可。
awk从文件中读取程序
跟 sed 一样,awk 允许将脚本命令存储到文件中,然后再在命令行中引用,比如:
1 | cat awk.sh |
awk.sh 脚本文件会使用 print 命令打印 /etc/passwd 文件的主目录数据字段(字段变量 $6),以及 userid 数据字段(字段变量 $1)。注意,在程序文件中,也可以指定多条命令,只要一条命令放一行即可,之间不需要用分号。
awk BEGIN关键字
awk 中还可以指定脚本命令的运行时机。默认情况下,awk 会从输入中读取一行文本,然后针对该行的数据执行程序脚本,但有时可能需要在处理数据前运行一些脚本命令,这就需要使用 BEGIN 关键字。
BEGIN 会强制 awk 在读取数据前执行该关键字后指定的脚本命令,例如:
1 | [root@localhost ~]# cat data3.txt |
可以看到,这里的脚本命令中分为 2 部分,BEGIN 部分的脚本指令会在 awk 命令处理数据前运行,而真正用来处理数据的是第二段脚本命令。
awk END关键字
和 BEGIN 关键字相对应,END 关键字允许我们指定一些脚本命令,awk 会在读完数据后执行它们,例如:
1 | [root@localhost ~]# awk 'BEGIN {print "The data3 File Contents:"} |
可以看到,当 awk 程序打印完文件内容后,才会执行 END 中的脚本命令。
以:为分隔符统计/sbin/nologin有多少个
awk 'BEGIN{FS=":" ;count=0}{if($7 = "/sbin/nologin")count+=1}END{print count}' /etc/passwd
BEGIN 在读记录之前执行
END 在读完记录后再执行
$0 代表所有记录
$1-n 代表一个记录的第n个字段
使用awk脚本
#!/bin/awk
BEGIN{
FS=:
count=0 定义变量
}
{
if($7 == "/sbin/nologin")
{
count += 1
print $0
}
}
END{
print "count="count
}
awk -f 1.awk /etc/passwd 运行上面的脚本
FNR 当前处理到第几个文件
NF 当前多少个字段
NR 多少行
OFS 输入指定字段分隔符
ORS 输出记录分隔符
FS 指定分隔符
tips:
awk没有指定分隔符的话, 就是默认以"空格"为分隔符
打印所有行, 以空格为分隔符
awk '{ print $0 }' /etc/passwd
打印第一列,以:为分隔符
awk -F ":" '{print $1}' /etc/passwd
打印每行的最后一列
awk -F : '{print $NF}' /etc/passwd
打印第一列和第三列, 并且加上welcome
awk -F ":" '{print $1 "\t" $3 "welcome"}' /etc/passwd
打印第八行
awk -F : '{if (NR == 8){print $0}}' /etc/passwd
awk -F : 'NR == 8 {print $0}' /etc/passwd
打印行总数
awk -F : 'END{print NR}' /etc/passwd
打印每行的字段数
awk -F : '{print NF}' /etc/passwd
打印最后一行的最后一列
awk -F : 'END{print $NF}' /etc/passwd
打印字段数大于4的行
awk -F : 'NF > 4 {print $0}' /etc/passwd
打印文件里所有字段总数
awk -F : 'BEGIN{c = 0}{c=c+NF}END{print c}' /etc/passwd
打印uid在30~40范围内的用户名
awk -F : '$3 >= 30 && $3 <= 40{print $1}' /etc/passwd
打印5到56行
awk -F : 'NR >= 5 && NR <= 56 {print $0}' /etc/passwd
打印偶数行
awk '{if (NR % 2 == 0)print $0}' /etc/passwd
打印偶基数行
awk '{if (NR % 2 == 1)print $0}' /etc/passwd
打印每行的第一单词
awk 'BEGIN{FS="[^A-Za-z]+"}{print $1}' /etc/passwd
打印每行的第一个和第三个单词
awk 'BEGIN{FS="[^a-zA-Z]+"}{print $1,$3}' /etc/passwd
打印字段数大于5个的行总数
awk -F ":" 'BEGIN {count=0 }(NF > 5){count+=1} END{print count}' /etc/passwd
- 本文标题:linux_shell编程
- 本文作者:yichen
- 本文链接:https://yc6.cool/2020/08/03/linux_shell编程/
- 版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
1.sed的常用命令的使用
2.linux根下目录文件介绍
3.vim的使用
4.linux中丢失grub.cfg引导文件修复
5.linux系统管理
6.linux磁盘管理
7.磁盘命令lvm
8.linux软件管理