shell # var="hexiaoqiang" # ${var//PATTERN/SUBSTI}:查找var所表示的字符串中,所有被PATTERN所匹配到的字符串,并将其全部替换为SUBSTI所表示的字符串; ${var/#PATTERN/SUBSTI}:查找var所表示的字符串中,行首被PATTERN所匹配到的字符串,将其替换为SUBSTI所表示的字符串; ${var/%PATTERN/SUBSTI}:查找var所表示的字符串中,行尾被PATTERN所匹配到的字符串,将其替换为SUBSTI所表示的字符串; notice:PATTERN中使用glob风格和通配符; 查找删除: ${var/PATTERN/SUBSTI}:以PATTERN为模式查找var字符串中第一次的匹配,并删除之; ${var//PATTERN}:以PATTERN为模式查找var字符串中的匹配,并全部删除; ${var/#PATTERN}:以PATTERN为模式查找var字符串行首的匹配,并全部删除; ${var/%PATTERN}:以PATTERN为模式查找var字符串中的匹配,并全部删除; ${var/%PATERN}:以PATTERN为模式查找var字符串末尾的匹配,并删除之; 字符串大小写转换: ${var^^}:把var中的所有的小写字符转换为大写; ${var,,}:把var中的所有大写字符转换为小写; 变量赋值: ${var:-VALUE}:如果var变量为空,或未设置,那么返回value;否则,则返回var变量的值; ${var:=VALUE}:如果var变量为空,或未设置,那么返回VALUE,并将VALUE赋值给var变量;否则,则返回var变量的值; ${var:+VALUE}:如果var变量不空,则返回VALUE; ${var:?ERRROR_INFO}:如果var为空,或未设置,那么返回ERROR_INFO为错误提示;否则,返回var值; 练习:写一个脚本,完成如下功能 (1)提示用户输入一个可执行命令的名称; (2)获取此命令所依赖的所有库文件列表; (3)复制命令至某目标目录(例如/mnt/sysroot,即把此目录当作根)下的对应的路径中 bash,/bin/bash ==> /mnt/sysroot/bin/bash useradd,/usr/sbin/useradd ==> /mnt/sysroot/lib64/ld-linux-x8664.so.2 进一步:每次复制完成一个命令之后,不要退出,而是提示用户继续输入要复制的其它命令,并重复完成如上所描述的功能;知道用户输入"quit"脚本 bash特性: 引用命令的执行结果:$(COMMAND) 或者 `` 反引号 示例: mkdir $(date +%H-%M-%s) stat:查看文件或者文件系统的状态 示例: stat /etc/fstab File: ‘/etc/fstab’ Size: 465 Blocks: 8 IO Block: 4096 regular file Device: fd00h/64768d Inode: 33554498 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2018-11-28 18:16:42.497510412 +0800 Modify: 2018-11-17 23:20:14.801999430 +0800 Change: 2018-11-17 23:26:58.495992201 +0800 Birth: - linux 文件包含2类属性: 元数据: metadata 数据:date 正则匹配: [[:upper:]] 所有的大写字母匹配 [[:lower:]] 所有的小写字母匹配 [[:alpha:]] 所有字母匹配 [[:digit:]] 所有数字匹配 [[:alnum:]] 所有字母数字匹配 [[:space:]] 所有空白字符匹配 [[:punct:]] 所有标点符号匹配 [^] 匹配直指定范围外的单个字符 [^[:upper]] 匹配任意大写字母之外的单个字符 [^0-9] [^[:alnum:]] 匹配非数字字母之外的单个字符 IO重定向及管理 程序:指令+数据 程序:IO 程序的数据流有三种: 输入的数据流: <----- 标准输入(stdin), 键盘 输出的数据流: -----> 标准输出(stdout), 显示器 错误输出流: -----> 错误输出(stderr), 显示器 fd: file descriptor 文件描述符 标准输入 0 标准输出 1 错误输出 2 输出重定向 > 特性 覆盖输出 >> 特性 追加输出 set -C 禁止覆盖输出重定向向至已存在的文件 此时可使用强制覆盖输出 >| set +C 关闭上述特性 这个效果仅对当前shell有效 错误输出流重定向:2> , 2>> 合并正常输出流和错误输出流 &> , &>> COMMAND > /PATH/TO/SOMEFILE 2>&1 COMMAND >> /PATH/TO/SOMEFILE 2>&1 ----这个地方不太理解 要反复练习理解 输入重定向 < tr命令 tr [option] ... SET1 [SET2] 把输入的数据当中的字符,凡是在set1定义范围内出现的,通通对位转换为set2出现的字符 用法1: tr set1 set2 < /path/from/somefile 用法2: tr -d set1 < /path/from/somefile 以上2中操作都不修改源文件 Here Document : << cat << EOF cat > /path/to/somefile << EOF 这个知识点很重要 很多次都没有理解 管道:链接程序,实现将前一个命令的输出直接定向后一个程序当做输入数据流 COMMAND1 | COMMAND2 | COMMAND3 | ... tee 命令: COMMAND | tee /PATH/TO/SOMEFILE 既可以输出查看的文件 又保存至其他位置 #!/bin/bash # cat << EOF disk) show disks info mem) show memory info cpu) show cpu info *)QUIT EOF read -p "Your choice: " option if [[ "$option" == "disk" ]];then fdisk -l /dev/[sh]d[a-z] elif [[ "$option" == "mem" ]];then free -m elif [[ "$option" == "cpu" ]];then lscpu else echo "Unkown option" exit 3 fi #!/bin/bash # for username in user21 user22 user23;do useradd $username done #求100以内所有正整数之和 #!/bin/bash declare -i sum=0 for i in {1..100};do echo "$sum is $sum , $i is $i" sum=$[$sum+$1] done awk命令: FS 默认的内置输入变量 默认为空白字符 如果在awk命令行重新定义 只需要-v 定义就可以了 示例: awk -v FS=':' '{print $1}' /etc/passwd 指的是以 : 为分隔符好进行打印 awk -v FS=':' '{print "hello:",$1}' /etc/passwd 上面的2条命令也可以通过-F:就可以是分隔符为:号进行打印的需求了 awk -F: '{print "hello world: ",$1}' /etc/passwd OFS 内置的输出变量符 默认为空白字符 可以自定义 FS OFS 区别主要是输入判断的变量符号 和输出表现的变量符号 比较容易理解 示例 awk -v FS=':' -v OFS=':' '{print $1,$3,$7}' /etc/passwd RS: input record seperator 输入时的换行符 ORS: output record seperator 输出时的换行符 示例: awk -v RS=' ' -v ORS='#' '{print}' /etc/passwd awk -v RS=' ' -v ORS='#' '{print}' /var/log/messages 解释:输入的时候以空格为换行符 然后输出的时候 有空格的时候就会转换为#号 这个地方不好理解 NF: number of field 字段数量 记录每行以默认空格计算有多少字段 示例: awk '{print NF}' /etc/log/messages awk '{print NF}' /etc/passwd $NF: NF会得到每行以空格计算有多好字段 然后拿到这个字段数 然后打印这个字段的值 取字段变量的最后一个值 示例: awk '{print $NF}' /var/log/messages awk 内部引用变量不需要加$ 直接引用即可 NR: 记录文件的行数 后面跟多个文件的时候 继续增加行数 而是不区分文件进行计数 示例: awk '{print NR}' /etc/passwd awk '{print NR}' /var/log/messages /etc/passwd awk -F: '(NR>=2&&NR<=10) {print $1}' /etc/passwd FNR: awk后面跟多个文件的时候 分别记录文件的行数 示例: awk '{print FNR}' /etc/fstab /etc/passwd FILENAME 显示文件名 一个文件要是有多少行 就会显示多少遍 示例: awk '{print FILENAME}' /etc/fstab /etc/passwd 不建议常用 但是思想是可以遍历文件多少行 每行都会处理 ARGC: 命令行参数的个数 示例: awk '{print ARGC}' /etc/passwd /etc/issue /etc/fstab 记录命令行参数的个数 awk和自己的参数算只能算一个 文件有多少行就会显示多少遍 awk 'BEGIN{print ARGC}' /etc/passwd /etc/fstab /etc/issue 在打印前面加BEGIN就会只显示一遍 ARGV: 数组,保存的是命令行所给定的各参数 示例: awk '{print ARGV[0]}' /etc/passwd /etc/fstab /etc/issue 只会打印awk这个下标的数组 不加BEGIN的时候文件有多少行就会显示多少遍 awk 'BEGIN{print ARGV[0],ARGV[1],ARGV[2],ARGV[3]}' /etc/passwd /etc/fstab /etc/issue /var/log/messages 在前面print添加BEGIN之后就只显示一遍 根据下标打印相对应的数组值 2.2自定义变量 -v var=value 变量名区分大小写 在program中直接定义 示例: awk -v test="hello world" '{print test}' /etc/fstab awk -v test="hello world" 'BEGIN{print test}' /etc/fstab print 打印变量的时候直接引用变量名 /etc/fstab 这个地方值利用的是行数 文件有多少行就会打印多少遍hello world 前面加BEGIN的时候只会打印一遍了 awk '{test="hello world";print test}' 可以在print中直接定义变量 awk 'BEGIN{test="hello world";print test}' /etc/fstab 2.3 printf 命令 格式化输出: printf FORMAT,item1,item2,... FORMAT必须给出 printf不会自动换行,需要显示给出换行控制符,n FORMAT 中需要分别为后面的每个item指定一个格式化符号 格式符: %c: 显示字符的ASCII码 %d %i: 显示十进制整数 decimal intege 单词可能不太准确 %e %E: 科学计数法数值显示 %f: 显示为浮点数 %g %G: 以科学计数法或浮点形式显示数值 %s: 显示字符串 %u: 无符号整数 %%: 显示%自身 示例: awk -F: '{printf "%s",$1}' /etc/passwd 把$1套到%s进行以字符串进行显示 默认不加换行符就会显示在一行中 awk -F: '{printf "%s ",$1}' /etc/passwd n就会每一行只显示一个用户名 awk -F: '{printf "Username: %s ",$1}' /etc/passwd 还可以加前缀提示字符进行显示 awk -F: '{printf "Username: %s,UID: %i ",$1,$3}' /etc/passwd awk -F: '{printf "Username: %s,UID: %i ",$1,$3}' /etc/passwd 第一个%s后面不能加 不然每一行都会在2行进行显示了 在最后一个FORMAT定义的变量之后加n符号 这个是显示 用户名和UID %d %i 都是以数值的方式显示用户的UID 修饰符: #[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度; %3.1f 默认这样的使用 以f居多 默认对齐方式为右对齐 示例: awk -F: '{printf "Username: %15s,UID: %d ",$1,$3}' /etc/passwd -: 表示左对齐 示例: awk -F: '{printf "Username: %-15s,UID: %d ",$1,$3}' /etc/passwd +: 显示数值的符号 正数的时候前面会有+号 示例: awk -F: '{printf "Username: %15s,UID: %+d ",$1,$3}' /etc/passwd 4.操作符 算数操作符 x+y, x-y, x*y, x/y, x^y[x的y次方], x%y[取模] -x +x:转换为数值 [把字符串转换为数值] 字符串操作符:没有符号的操作符,表示字符串连接 赋值操作符: =,+=,-=, *=, /=, %=, ^= ++, -- 比较操作符: >, >=, <, <=, !=, == 模式匹配符: ~:左侧的字符是否能匹配右侧的字符 !~:左侧的字符不匹配右侧的字符 逻辑操作符: && 与 || 或 ! 非 函数调用:定义函数 后面跟上()就可以了 ()里面可以加上调用的参数 function_name(argu1,argu2,argu2,...) 条件表达式: selector:条件挑选器 ?:表示为真执行 if-true-expression 否则执行 if-false-expression 示例: selector?if-true-expression:if-false-expression awk -F: '{$3>1000?usertype="Common User":usertype="System or Systemuser";printf "%15s:%-s ",$1,usertype}' /etc/passwd 解释:-F: 定义打印格式 $3>1000 判断第三个字段是否大于1000 然后定义条件表达式 格式上面有解释,以:为分隔符进行判断 第一个为真后面就会打印 第二个为假后面会打印 字段使用双引号进行包含 printf 开始匹配打印 "15%s:%-s " printf的打印格式 %s默认为右对齐 15表示15个空格 %-s表示左对齐 $1是取字段第一个值 usertype是调用定义的变量字符串进行打印 后面是需要的条件输入文件位置 5、PATTERN (1) empty:孔模式,匹配每一行 (2) /regular expression/:仅处理能够被此处的模式匹配到的行 (3) relational expression:关系表达式:结果有"真"有"假":结果为"真"才会被处理; 真:结果为非0值,非空字符串 (4) line ranges:行范围 startline,endline: /pat1/,/pat2/ 注意:不支持直接给出数字的格式 示例: awk '/^UUID/{print $1}' /etc/fstab 在这儿就能实现UUID匹配行的打印 打印第一个字段 默认的变量还是以空格为分割 awk '!/^UUID/{print $1}' /etc/fstab 打印匹配模式以外的行 然后打印第一个字段 awk -F: '$3>100{print $1,$3}' /etc/passwd $3>100是进行模式匹配 然后再后面打印 就可以实现类似grep的功能了 awk -F: '$3>100{printf "%15s:%-s ",$1,$3}' /etc/passwd awk -F: '$3<100{printf "%15s:%-s ",$1,$3}' /etc/passwd awk -F: '$NF=="/bin/bash"{print $1,$NF}' /etc/passwd 查找/etc/passwd中shell为/bin/bash的用户 awk -F: '$NF~/bash$/{print $1,$NF}' /etc//passwd $NF模式匹配 ~进行模式匹配 匹配的内容要用//括起来 /bash$/以bash结尾的用户 awk -F: '$NF!~/bash$/{print $1,$NF}' /etc//passwd 不以/bash$/结尾的在$NF后面加一个!就可以 awk -F: '/^r/,/^s/{print $1}' /etc/passwd /pat1/,/pat2/ 模式匹配 从r字母开头的行匹配到s开头的行结束 awk -F: 'BEGIN{print " username uid ---------------"}{print $1,$3}' /etc/passwd BEGIN 和 END的使用方法 awk '/^[[:space:]]*linux16/{print}' /etc/grub2.cfg 打印这个文件中以开头为空格的字符 然后后面匹配Linux16的行 (5) BEGIN / END 模式 BEGIN{}: 仅在开始处理文件中的文本之前执行一次; END{}:仅在文本处理完成之后执行一次; 示例: awk -F: '{print " username uid ---------------"}{print $1,$3}END{print "===================== end"}' /etc/passwd END用在最后进行使用 打印结尾 表示 6. 常用的action (1) Expressions (2) Control statements: if while等: (3) Compound statements:组合语句 (4) input statements (5) output statements 7. 控制语句 if(condition) {statements} if(condition) {statements} else {statements} while(condition) {statements} do {statements} while{condition} for(expr1;expr2;expr3) {statements} break continue delete array[index] delete array exit {statements} 7.1 if-else 语法:if(condition) {statements} else {statements} 使用场景:对awk取得的整行或某个字段做条件判断 示例: awk -F: '{if($NF=="/bin/bash") print $1}' /etc/passwd awk -F: '{if($3>100) print $1,$3}' /etc/passwd awk -F: '{if($3>100) {printf "Common user: %s ",$1} else {printf "root or Systemuser: %s ",$1}}' /etc/passwd if-else的时候要把{prinf}都用大括号个括住了 不然会报语法错误 awk '{if(NF>5) print $0}' /etc/fstab 对字段数大于5的进行print df -Th | awk -F[%] '/^/dev/{print $1}' | awk '{if($NF>4) print $1}' 7.2 while 循环 示例: awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) print $i,length($i);i++}}' /etc/grub2.cfg 嵌套if循环判断 7.3 do-while循环 语法:do statement while(condition) 意义:至少执行一次循环体 7.4 for循环 语法: for(expr1;expr2;expr3) statement for(variable assignment;condition;iteration process) {for-body} awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg 特殊用法: 能够遍历数组中的元素: 语法:for(var in array) {for-body} 7.5 switch语句 语法: switch(expression) {case VALUE1 or /REGEXP1/: statement;case VALUE2 or /REGEXP2/: statement;...;default: statement} 7.6 break和continue break [n] 退出n层循环 continue 7.7 next 提前结束对本行的处理而直接进入下一行; 示例: awk -F: '{if($3%2!=0) next; print $1,$3}' /etc//passwd 8.array 关联数组:array[index-expression] index-expression (1)可使用任意字符串:字符串要使用双引号; (2)如果某数组元素事先不存在,在引用时,awk会自动创建,并将其初始化为"空串"; 若要判断数组中是否存在某元素,要使用"index in array" 格式进行 weekdays[mon]="Monday" 若要遍历数组中的每个元素,要使用for循环; for(var in array) {for-body} 示例: awk 'BEGIN{weekdays["mon"]="moday";weekdays["tue"]="Tuesday"; for(i in weekdays) {print weekdays[i]}}' 注意: var会遍历array的每个索引 使用场景:统计打印结果中某个字符串出现的次数 netstat -ntlp | awk '/^tcp>/{state[$NF]++}END { for(i in state) {print i.state[i]}}' awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log 统计ip访问的次数 俗称报告生成器 awk '/^UUID/{fs[$3]++}END{for(i in fs) {print i,fs[i]}}' /etc/fstab 统计系统文件类型出现的次数 awk '{for(i=1;i<NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}' /etc/fstab 统计文件单词出现的次数 {for(i=1;i<NF;$i++){count[$i]++}} 使用for循环做定义判断 NF记录的是文件字段出现的次数 count[$i]++ 则是使用$i下标进行统计次数 每次+1 9.函数 9.1内置函数 函数处理; rand();返回0和1直接的随机数 awk在系统第一次打印时 是随机的而后的打印都是保持第一次的随机数 字符串处理; length([s]): 返回指定字符串的长度 sub(r,s,[t]): 以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容 这个只会替换第一次替换不会 全局进行替换 gsub(r,s,[t]):表示全局进行替换 示例: awk -F: '{print sub(o,O,$1)}' /etc/passwd 使用print的时候会打印结果1表示不成功 0表示成功 split(s,a,[r]) : 以r为字符切割字符s,并将切割后的结果保存至a所表示的数组中 示例: netstat -tan | awk '/^tcp/{split($5,ip,":");count[ip[1]]++}END{for(i in count) {print i,count[i]}}' 详解: {split($5,ip,":")} 以第5个字段进行切割保存至ip数组中,":"以分隔符进行切割 ip[1] 是以":"切割之后拿到左面第一个字段的值 count[ip[1]]++ 拿到左面第一个字段下表的记录 每次都+1 做数组循环 {split($5,ip,":");count[ip[1]]++}END最后只打印一次 {for(i in count) {print i,count[i]}} 做数组循环判断进行打印结果