SHELL学习

第一章 Linux Shell基础和使用
1.shell 是用户和linux内核之间的接口程序,为用户提供使用操作系统的接口,通过shell传递命令,内核会做出反应
2.shell分类
(1)bourne shell(sh) 、C shell、Korn Shell;linux中常用 bourne again shell(bash),是sh的扩展版本,兼容sh,无论何种shell,作用都是解释使用者在命令行中输入的命令给内核
(2)etc/shells中可查看系统支持的shell脚本,具体用户使用哪一种,由etc/passwd中定义
(3)查看当前用户用的shell 命令:echo $SHELL
3.第二章 Linux Shell简介
1.linux环境构成
内核 ----linux核心
Shell-----用户和内核交互的接口
终端模拟器----使得用户输入命令并在屏幕上回显结果
linux图形界面
2.登录时首先调用的是登录shell,其中会设置当前用户的权限,加载如Path路径等变量,可以定制
3.shell中的变量
(1)分类为系统(环境)变量和自定义变量
(2)系统变量
1)系统变量是由Linux bash shell创建和维护的变量,可以人为更改
2)常见的系统变量
i.HISTFILE 保存命令历史的文件名
ii.HOME 当前用户的主目录
iii.HOSTNAME 当前机器主机名
iv. PATH 即搜索命令的路径,shell会在path中的全部路径中搜索匹配的可执行文件
v.PWD 当前路径,每次执行cd命令都会更新该变量的值
vi. TMOUT shell内建命令read等待用户输入的时间,超时后脚本执行自动退出
(3)定义变量和给变量赋值
i.varName=varValue 等号两边不能有空格,否则报错
ii.bash默认赋值时字符串赋值,eg:sum=1+1 echo $sum 结果1+1,
iii.算数运算用let let sum=1+1 echo s u m 结 果 2 i i i ∗ ∗ 变 量 值 付 给 另 一 个 变 量 ∗ ∗ a = z s x ; b = sum 结果2 iii **变量值付给另一个变量** a=zsx;b= sum2iiia=zsxb=a ;echo a 结 果 z s x i v . ∗ ∗ 命 令 的 执 行 结 果 赋 值 给 变 量 ∗ ∗ v a r = ‘ p w d ‘ 或 者 v a r = a 结果 zsx iv.**命令的执行结果赋值给变量** var=`pwd`或者 var= azsxiv.var=pwdvar=(pwd)
(4)变量命名规则
1)变量名必须字母下划线开头,后面跟字母数字下划线,第一个字符不为数字,符号不要参与
2)变量名大小写敏感
(5)echo和printf打印变量
1)printf格式化打印
格式为: printf format格式 arguments打印内容
eg: printf “firstName:\s\n” “$firstName” 格式化打印变量firstName的内容
printf “%2s\n” $var %n,当n小于实际总长度,则打印完整字符,当n大于总长度,则左边用空格弥补
printf “%2.4s\n” $var 小数点右边的数字表示打印几个字符,从左边第一个开始算,小数点前面的数字不起作用
var=1233.33
printf “%d\n” $var 结果1233 只打印十进制整数
printf '%e\n" $var 科学计数法表示数字
printf “%3.1f\n” $var 1233.3 浮点数打印,保留一位小数
printf ‘\b \b’ 向左删除前一个字符 在这里插入图片描述
在这里插入图片描述

(6)变量的引用
当变量值中有空格存在时,在引用变量时应该尽量加双引号,可以防止变量值中多个单词分离

eg:有些执行频繁的命令可以写成变量 通过echo执行
reload="/usr/local/sbin/nginx -s reload"  引用时应该echo "$reload"
list="one two three"
for var in $list 
do echo $var
done
以上打印 
one 
two
three
若改为 in "$list"  则打印 one two three

(7)删除变量 unset ,无法删除只读变量

eg:reload="nginx -s rload"
unset reload
readonly reload="nginx -reload"
unset reload 报警告信息:无法删除

(8)变量永久和临时
1)临时变量
普通变量都是临时变量,作用范围为当前shell,当前shell退出时(如重新登录操作)变量失效
eg:reload=“nginx -reload” 重新登录shell 变量失效
2)永久变量
将变量定义在/etc/profile中并导出
export reload=“nginx -s reload”
保存后执行命令source /etc/profile 则该变量立即生效并在shell重新登录时仍有效
4.shell环境相关
(1)命令历史
i.history 查看之前使用过的命令,查看多少由环境变量HISTSIZE决定,在/etc/profile中定义
ii.!历史命令编号,调用某个命令:eg: !1021 假设history显示的1021命令是clear 则调用clear
(2)shell中的扩展
1)大括号扩展,一种能够生成任意字符串的机制

i.常用案例:echo {a,b,c}{1,2,3}组合生成:a1 a2 a3 b1 b2 b3 c1 c2 c3
echo {a..z}//中间只有两个点 结果:a b c d e f g h i j k l m n o p q r s t u v w x y z
rm -rf {q,e,w} //一个命令同时用于多个文件
ii.大括号扩展在其他扩展之前,其中类似波浪线这种特殊字符会被保留
iii.当大扩号中字符含有大括号时则进行转义:mkdir {\{q,\}w} 反斜杠\转义;字符中${不会被识别

2)波浪号扩展 ~
~字符串,字符串会被当成某个用户名,整个表达式代表用户的主目录,如果不加字符波浪线则代表HOME变量的值,如果没有则代表当前shell用户的值
+=pwd,-表示进入当前目录前的上一个目录,eg:cd ~+;cd ~-上一个目录
3)命令替换,其实就是$(command) 和’command’
其中命令的执行结果会用来替代命令本身作为新的命令执行
reload="/usr/local/nginx/sbin/nginx -t "
$(echo $reload)
结果:nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
4)文件名扩展,即有限的正则 只支持 ?和[]表示或,其他不支持*
find ./ -name ‘[ce]*’ //查找c或者e开头的文件
(3).创建和使用别名
1)alias aliasName=‘command’ 作用范围是当前shell
alias reload=’/usr/local/nginx/sbin/nginx -t’
直接执行reload命令 相当于执行nginx -t;
2)查看所有定义的别名 直接alias,像类似ll这种都是别名
3)删除别名 unalias reload
第三章 常用shell命令
1.查看文件和目录
(1)ll -R 递归列出子目录的文件
ls -ltr 按修改时间列出文件
ls -a 列出所有包括隐藏文件
(2)less查看文件,支持向前向后翻页,比more方便
1)less中输入/ 可搜索关键字并跳转到第一个关键字
2)? 搜关键字跳最后一个关键字
3)G跳转到文件末尾
4)q或者ZZ 退出文件
(3)head 显示头部文件,跟tail 功能相反
eg: head -5 nginx.conf //显示头5行
head -n 5 nginx.conf //显示头5行
(4) file -N *查文件类型, 能够去掉文件名和文件类型之间的空白,
eg: file * 结果: fastcgi.conf: ASCII text
file -N * 结果:fastcgi.conf: ASCII text
(5)wc 文件名 统计文件内容
wc nginx.conf 结果 X Y Z 文件名 分别是 行数 单词 字节
(6)mkdir -p 递归建立文件夹
(7)sort 文件名 对文件行按数字字母进行排序输出,只是输出有影响,并不真正改变文件内容
1)sort -u 文件名 ,对文件行进行去重输出
eg:

12
10
2
a
a
c
a
banam
number
money
cancel
apple
pear
sort -u number
10
12
2
a
apple
banam
c
cancel
money
number
pear

2)sort -r文件中若都是数字,sort默认是升序,-r改成倒叙
3)sort -o能将排序结果输出到文件中,类似于重定向,但如果目标文件是原文件的恶化,重定向会清空源文件
eg:

sort -r number >number 
cat number  #结果是空的
sort -r number -o number #结果直接替换到源文件中	

4)sort -n 如果都是数字,则按数值来排序
eg:

sort  number  #正序排列,2应该在前面,但因为是按照字符串排列的,1在2前,所以出现10比2小的情况

10
12
2
sort -n number 
2
10
12

(8)**uniq 文件名 去重后显示内容,**也是不改变真是文件
1)uniq -d ipaddr.txt 只显示重复了的行内容
2)uniq -c 文件名 显示每行内容重复出现的次数
(9)tr 替换或者删除字符,并不真正改变文件中的内容, 语法:tr [option] set1 set2 将set1中字符替换为set2中相同位置的字符
-d或——delete:删除所有属于第一字符集的字符;
-s或–squeeze-repeats:把连续重复的字符以单独一个字符表示
1)替换:

less ipaddr.txt  结果 :{qweee}
less ipaddr.txt|tr a-z A-Z {} 结果:{QWEEE}
less ipaddr.txt |tr '{}' '()' 结果:(qweee)  
cat person.txt 
cat person.txt
person_info:  zhangshaoxuan
job: java                engineer
experience:   3  years
address:   shannxi
ShannXi
   xi'an
zhouzhi
  this  is     the
  #使用tr

2)删除:
less ipaddr.txt |tr -d ‘{}’ 结果 qweee
less ipaddr.txt |tr -d a-z 结果就剩了{}
less ipaddr.txt |tr -cd [:digit:] 加上c表示删除除数字以外的所有字符
less ipaddr.txt |tr -d [:digit:] 删除其中的数字
(10)w 展示系统登录的用户 以及简单登录信息,who 也是展示登录信息
who -b 展示系统启动时间
who -q 只展示登录的用户名和用户总数
(11)uptime 展示系统运行时间
15:19:22 up 125 days, 2:01, 1 user, load average: 0.00, 0.01, 0.03
以上信息表示 系统已经运行了 125 days 两个小时1分钟,当前一个用户登录,load average表示系统过去1min 5min 15min平均负载
(12)date 展示或者修改当前系统时间
以下参数可以格式化显示日期,加号跟上以下参数

eg: date +%A,展示星期几;
 date "+%Y-%m-%d %H:%M:%S"  #这里的""要有否则有问题
 echo " `date \"+%Y-%m-%d\"`" #echo 里面执行date 则需要对双引号转义
 

%n : 下一行;%t : 跳格;%H : 小时(00…23);%I : 小(01…12);%k : 小时(0…23);%l : 小时(1…12);%M : 分钟(00…59)
%p : 显示本地 AM 或 PM;%r : 直接显示时间 (12 小时制,格式为 hh:mm:ss [AP]M);%s : 从 1970 年 1 月 1 日 00:00:00 UTC 到目前为止的秒数
%S : 秒(00…59);%T : 直接显示时间 (24 小时制)
%X : 相当于 %H:%M:%S;%Z : 显示时区 %a : 星期几 (Sun…Sat)
%A : 星期几 (Sunday…Saturday);%b : 月份 (Jan…Dec)
%B : 月份 (January…December);%c : 直接显示日期与时间
%d : 日 (01…31);%D : 直接显示日期 (mm/dd/yy)
%h : 同 %b;%j : 一年中的第几天 (001…366)
%m : 月份 (01…12);%U : 一年中的第几周 (00…53) (以 Sunday 为一周的第一天的情形)
%w : 一周中的第几天 (0…6)
%W : 一年中的第几周 (00…53) (以 Monday 为一周的第一天的情形)
%x : 直接显示日期 (mm/dd/yy)
%y : 年份的最后两位数字 (00.99)
%Y : 完整年份 (0000…9999)
(13)stat 文件名 ,展示某个文件大小,权限,最近修改时间
(14)id显示用户uid以及群组等
第四章 shell命令进阶
1.合并文本 paste,可以起到压缩文件内容的效果

paste  caseTest.sh tomcatBoot.sh //将文件每一行默认以制表符为分割对应进行合并成为新的一行,行数不等的直接打印出
paste -d '>' caseTest.sh tomcatBoot.sh   // -d指定一个字符在合并后进行分隔,只能指定一个字符,指定多个时默认以第一个字符为准
结果:#!/bin/bash>#1/bin/bash
paste -s -d ' ' caseTest.sh tomcatBoot.sh  // -s 将文件按顺序将文件中的内容以空格为分隔符合并为一行输出,第二个文件内容以空格为分隔合并为第二行
  1. dd if=文件 of=文件 做文件复制和备份,从标准输入流中复制到指定输出,还可作为测试cpu速度用
    dd if=/dev/zero of=/~w bs=1024 count=1024 //bs为块的大小,默认为b,count表示块的数量 1024*1024为1MB
    ps;/dev/zero中全为空字符 /dev/null 可以接收所有向它输入的数据,无底洞,但是却读不出任何数
    3.gzip压缩文档和归档,当文件比较大,又需要传输时可以使用压缩,默认以gz结尾
    (1)gzip -c test.txt >test.txt.gz //通过输出流将文件压缩到新文件,可以保留原始文件
    (2)gzip -d 文件名 //解压
    4.gunzip 解压文件 相当于 gip -d
    gunzip 文件名 //解压
    5 tar 归档
    6 du递归显示目录下所有子目录所占空间大小
du -a 目录  递归显示目录下所有文件
du -h bak  人类可读的方式显示  
du -sh java // -s表示显示某个具体文件夹的总大小,结果:388M java

7.执行计划任务 cron和crontab
(1)cron进程是执行定时计划任务的守护进程,每分钟会唤醒一次,检查(2)中的文件,看命令是否在当前时间运行
(2)crontab创建、修改、删除和查看定时计划任务,计划都保存在 /var/spool/cron/userName 这个文件里
1)crontab -l 查询所有命令
2)crontab -e 创建或者修改

41 14 * * *  echo "it is now to do sb"  // 格式分别为:分钟(0-59)小时(0-23)日(1-31)月(1-12)星期(0-6)0为周日,星号表示所有
 37  13 */1 * *  echo "hello=====" //斜杠表示每隔多长时间

3)crontab -ir 有确认信息的删除当前用户所有的定时任务
8 at
参考:https://blog.51cto.com/12822117/2121101
9.&将命令或者脚本放在后台执行,jobs可以查看后台进程

eg: ./nginx &   后台执行nginx启动命令

(1)将后台任务放前台用fg job-id,或者% job-id
eg: fg 1 //1为进程的任务号。jobs能显示当前所有后台运行的进程,且有编号12345等
(2)将前台任务放后台 ctrl+z先挂起任务,然后bg,将任务放后台
10.nohup command & 后台运行一个对挂起免疫的命令,区别如下:
eg:

	sleep 1000m & //后台运行命令,当shell退出时,命令会终止,再次登录ps 将搜索不到
	nohup sleep & //后台运行,退出后再登录 ps会发现命令进程依然再运行

11 sleep 1000[s|m|h|d] 当前shell暂停一段时长

eg: sleep 10s  睡眠10s,10s后光标再次出现

第四章 shell脚本编程
1.#!+绝对路径 指定脚本的解释程序
(1)几乎所有的脚本都是使用解释程序/bin/bash,如果文件开头没有加#!,那么会默认使用/bin/bash解释程序
(2)程序加载器将#!后的路径作为一个解释程序的路径,并将使用该解释程序,并将当前的脚本作为参数传递给解释程序去运行解释
2.注释 单行注释#,多行注释用<<BLOCK 文本 BLOCK,block是Here Documents中定义的,内容无所谓,前后要对应

eg:<<c      //这里c可任意字符,但是前后必须要对应
注释1====
注释2===
注释3===
c

3.脚本的授权和普通文件授权是一样的 都使用chmod命令,另外脚本的执行直接脚本名 回车就行;也可将脚本加在环境变量中,$PATH

vim /etc/profile;
export PATH=$PATH:/home/shtest //打开文件后增加一行
source /etc/profile 重新使得文件生效

4.bash参数扩展
(1)间接参数扩展,参数赋值后直接传递值

eg: WORD=word;   word="test for word"
echo ${WORD}  结果:word;
echo ${!WORD} 结果:“test for word”

(2)变量名扩展 {!PREFIX*}和{!PREFIX@},这种扩展展示所有以PREFIX开头的变量
(3)变量内容的长度 : echo ${#WORD}
(4)使用默认值 :echo ${WORD-qwe}/{WORD:-qwe},如果WORD未定义或者为null,则展示其默认值qwe
(5)指定默认值 echo ${WORD=qwee} ,与(4)的区别在于该种使用会将值赋值给WORD
5.bash的内部变量
$BASH bash实例的全路径名
$HOME 当前用户的家目录
$SECONDS 当前shell运行的秒数
$TMOUT 在shell脚本中该变量不为0时,则默认是read 命令的超时秒数

eg:TMOUT=5
echo "input the true value:"
read value //会等待用户5s进行输入,如果没有输入,则执行结束=
  1. bash中的位置参数和分批参数
    (1)位置参数 N 表 示 匿 名 变 量 , N > 0 当 N > = 10 时 , 必 须 写 做 N 表示匿名变量,N>0 当N>=10时,必须写做 NN>0N>=10{N};匿名变量只能用set 赋值,不能通过赋值语句
eg:set one two three four five 6 7 8  9 10 
echo  $1 $2 $3 $4 $5  $10 ${10}
结果:one two three four five one0 10

(2)特殊参数的处理,只能引用不能修改他们得值,有 *#@?-$!0_等

echo $*  //打印set后所有位置参数的值  结果:one two three four five one0 10
echo $?//打印最近一次执行的命令退出状态,0表示执行成功,1表示执行失败
echo $@ //与$*一样,区别在for循环调用中才能显示出来
echo $$ //打印当前shell的进程号
echo $0 //当前脚本的绝对路径以及文件名
echo $# //返回到当前脚本的参数个数

(3)declare 设置变量类型 = typeset
i.不带参数,将显示所有参数的值
ii.declare -r NUMBER //设置为readonly,则不能用unset删除
iii.declare -i NUMER // 设置为整型,
NUMBER=9/4 //结果number为2,
NUMBER=SFDS//结果为0,非数字则为0
7.bash中的数组
(1)声明数组:
显式声明数组,declare -a ARRAYNAME
隐式声明:ARRAY[0]=qqq //这样array就是一个数组了
ARRAYNAME=(qqq 123 dd ‘mng’ ) //这样也是对数组的声明
(2)引用数组
eg:

declare -a ARRAY1 //显式声明
ARRAY1=(tom jerry lucy  lily) //赋值
 ARRAY2=(张三 李四 王五  赵六)   //隐式声明
ARRAY3[0]=韩梅梅 //隐式
ARRAY3[1]=123
echo "打印ARRAY1"
echo ${ARRAY1}   ${ARRAY1[0]}  ${ARRAY1[1]} //引用,必须放在{}中引用,另外,如果直接引用数组不加下标则显示第一个元素值
echo “打印ARRAY2”
echo ${ARRAY2[*]} ${ARRAY3[@]} //*和@表示对所有元素的引用
echo ${!array[@]} 输出所有下标

8.shell中的算术运算
(1)算术运算符,除常规的以外,还有非常规的
i.id++,id–,++id,–id //变量的自增自减,规则与java中一样
ii.num**3,//求幂操作,
iii.&& || !与或非
iv. expr1,expr2//逗号运算,每个运算都求值,但只有最后一个值被返回并赋值给变量

eg:let var=(1+1,1+2,1+3)  ;echo $var 结果为4
已一般在for循环中使用较多
iv.+=,*=,-=等赋值运算 

(2)算术扩展 $$((算术表达式)) ,对表达式求值并将整体的值替换为所求的值,只针对整数,浮点数无法算术扩展

var=5
var=\$((\$var+9))//此处必须是‘’$(()),因为一个括号的话会将表达式当成命令执行,找不到命令则报错 
echo $var //结果14

(3)let与算术扩展用法基本一样,但是使用let时 如果算术符号左右两边有空格,则需要双引号

eg:let var="$gg + 1" //运算符左右有空格,没有双引号的话会报错

(4)expr命令对表达式求值,支持整数运算数,运算符左右必须有空格, 否则直接输出整个表达式

eg: expr $var + 1 //  输出结果2
expr $var+2 //输出结果1+2
 c=`expr $var + 1` //赋值给c
expr $var \* 2 // 乘号 > <这些都需要转义,*可能是被当成通配符了

9退出脚本
(1)退出码,命令、程序、工具等成功退出都会返回0,不成功都会返回非0
i.shell脚本中最后一条命令的返回值决定了整个其返回状态
ii.$?返回上一条命令的状态码
iii.检查调用程序状态码的重要性举例

HOME_DIR='/hhh'
cd $HOME_DIR
if [ $? -eq 0 ] ; then //如果不判断,当目录不存在时会直接删除掉脚本所在目录下所有文件,造成损失
echo $?
rm -rf *
else
echo 'Cannot remove directory';
exit 1;
fi;

(2)exit N 命令退出
(1)如果发生错误,exit可以终结脚本运行
(2)N=0表示脚本成功退出,非0则表示脚本运行失败,退出码被省略,则以最后一条命令退出码为准
(3)脚本退出时应该返回一个明确的状态码

eg:FROM=/home/sss.sh
TO=/home/test
if [ ! -e $FROM ];then  echo 'File is not existed !';
exit 1;
elif  [ -d $TO ];then mv $FROM $TO;
else
echo "Directory  doesn't exist";
exit 2;
fi;
exit 0;

10 调试脚本
(1)bash -x 调试脚本

bash -x  bak.sh 
+ FROM=/home/sss.sh //+表示真正执行的语句
+ TO=/home/test
+ '[!' -e /home/sss.sh ']'
bak.sh:行4: [!: 未找到命令 //非+开头则是正常的打印输出的日志
+ '[' -d /home/test ']'
+ mv /home/sss.sh /home/test
mv: 无法获取"/home/sss.sh" 的文件状态(stat): 没有那个文件或目录
+ exit 0

(2)在脚本中某一段代码中条件set -选项,set -选项表示启动某项选项,set +选项 表示去掉某个选项

set -x 表示添加调试,set -x去除调试
eg: set -x 横杠和x选项应该在一起,不可有分隔
set -x
if [ ! -e $FROM ];then  echo 'File is not existed !';
exit 1;
elif  [ -d $TO ];then mv $FROM $TO;
else
echo "Directory  doesn't exist";
exit 2;
fi;
exit 0;
set +x;
结果:
+ '[' '!' -e /home/sss.sh ']'
+ echo 'File is not existed !'
File is not existed !
+ exit 1

(3)bash -n bak.sh ,能够检查类似于if和else不匹配的语法等错误,应该在跑脚本前先进行语法检查
11.shell脚本代码风格
(1)每行少于80字符(2)缩进一致(3)文件头注释(4)自定义变量或者函数一律采用小写字母,下划线分隔单词(5)命令和脚本返回值需要$?验证
第六章 shell条件执行
1.条件测试
(1)test命令测试 格式:test EXPRESSION
test命令用于文件属性测试,字符串和算术测试
i.文件属性测试,-r -w -x是否可读可写可执行,-e是否存在 -d是否是目录

eg:test -d /hh/;echo $? //打印1 
test "abc" == "cba" && echo "相等" || echo "不相等" //字符串相等则打印相等,否则打印不相等
test -x case_bak.sh && echo "可执行"||echo "不可执行"

在这里插入图片描述

ii.字符串测试的操作符 -z 判空 ,-n 判非空 = ,!=,< 小于,>大于(基于ASCII码排序);<>被用于重定向操作符,因此要转义

test -z "qqq";echo $? 
test -n "ee";echo $?
 test "qqq" = "qqq" ; echo $?
test "bq" \< "aw" ;echo $? //结果为1,按照首字母排列的,b在a后 b大于a 

iii.算术测试的操作符 -eq -ne -le -ge -lt -gt

eg:[ 4 -eq 5 ] && echo "相等" //其他类似

(2)if结构语法
i.if条件中判断条件可以为 test 命令或者成功返回0 否则其他的任何一个命令
语法如下:

#=======单分支if then fi实例
if ( test 6 -lt 5 ); then echo "4小于5==" ; fi #这里的then如果和if在一行,中间要有分号,then和fi也要有分号
#第二种==
read -p " enter the password:" password 
if ( test $password == "zhangshaoxuan" ) #这里如果使用括号,则需要使用test,并且==表示是否相等,如果使用[],则为[ $password = "zhangshaoxuan" ],>不用test和==
then
echo "USER VERIFIED"
exit 0
fi
exit 1
=====if then else fi 分支实例
read -sp "Please Enter the password:" pass
if (test $pass == 'zsx')
then
echo "USER VARIFIED"
exit 0;
else
echo "UARIFYING FAILED"
fi;
exit 1
#=======if嵌套实例======
read -p "please enter a number :" number
if (test $number -gt 100 )
then
  if (test $number -gt 150 )
  then
   echo "数字大于150===="
   exit 0
  else
   echo “数字在100和150之间====exit 1
  fi;
else
  echo "数字不大于100,非常的尴尬==="
  exit 2
fi
echo “中间值,无效!!!"
exit 3
#=======if嵌套实例======
read -p "please enter a number :" number
if (test $number -lt 10 )
then
 echo "数字小于10=="
elif (test $number -eq 100 )
then
 echo "数字等于100"
elif (test $number -gt 100)
then
 echo "数字大于100"
else
 echo "数字在10和100之间"
fi
(3)read -sp  输入的字符是隐藏的
2.条件执行  
$$ 前面不成功 不执行后面
eg:
echo $#=========
if ( test $# -ne 1 )
then
echo "参数个数不为1,退出脚本$0"
exit 1
fi
if (test $1 -gt 100 && test $1 -lt 150 ) //参数在100和150之间打印
then
echo “值在100和150之间”
fi
eg: if (test -z $HOME && -d $HOME;then //先判断变量是否为空,不为空再判断是否是目录

2.条件执行
(1)布尔非|| command0 || command1 当第一条不成立时才执行第二条

eg:grep "zhangshaouxan" /etc/passwd || echo "user is not found"

i**&&和||联合使用**

eg: test $(id -u) -eq 0 && echo "you are the root"|| echo "you are not the root" //id --u 打印userid root是0
当test为true则执行第一个echo,当第一个echo不执行时依次执行第二个echo

ii.if中的应用

read -p "enter a number:" number
if [ $number -eq 5 ]|| [ $number -eq 10 ]||[ $number -eq 15 ]
then echo "进来了"
exit 0;
else
echo "没进去"
exit 1
fi

(2)! 非

eg:test ! -d /home/shtest1/ && mkdir /home/shtest1/ || echo "this direcotry exists" //文件不存在则创建,否则打印

3.case语句,多级if的替代
(1)语法实例:

echo $# ======
if (test $# -ne 1 )
then
 echo "参数数量不对===";
exit;
fi
now=$1
case $now in //变量在case后,为输入值
"五") //右括号
echo 今天星期$now====
;; //每个case结束符为;;
"一"|"二"|"三"|"四")
echo 今天星期$now===
echo "应该开汽车"
;;
[0-9])
echo "您输入的是数字将退出"
exit 1;
;;
[A-Za-z])
echo "输入的是字母"
;;
*) //这个表示匹配所有,即执行默认操作
echo "没有匹配到任务东西,执行默认动作"
exit;  
esac; //退出时要加这个

(2)kill命令 常用格式为 kill +信号+进程号
i. kill -l列出所有信号
ii.常见信号 SIGHUP 终端断线 SIGINT中断(Ctrl+c)SIGOUT退出
第7章 bash循环
1.for循环
(1)语法结构举例

for i in 1 2 3 4  #使用数字作为列表
do echo "the for loop is running $i times" ;done
#//或者done换行写 
for city in ShenZhen Shanghai xian xianyang luoyang   #使用字符作为列表
do echo "the city is $city" ;done
all_test=/home/allTest.txt
for file in $(ls /home/shtest/)   #命令行作为列表
 do
#如果文件结果不存在,则创建文件
  if (! test -e $all_test );then echo "结果文件不存在开始创建===" && touch $all_test ;fi
#文件存在且为常规文件
  if ( test -f $file )
  then
   echo "=====================================文件$file开始进行汇总=======================================" >> $all_test;
   cat $file >>$all_test;
  fi
 done
 for file in $dir/*  #遍历一个文件夹的文件这样写
 do
 done 
#-------------------另一种for循环--------------------
 START=33
END=125
for ((i=$START;i<=$END;i++))  #这种for循环表示值从哪里开始到哪里结束 
do
echo $i | awk '{printf ("\t%d\t%x\t%c\n",$1, $1, $1) }'
done

(2)for循环指定的列表或者数值通常为 字符串、数字、命令行参数、文件名列表,linux命令输出 ;for循环可嵌套
eg:
for循环默认的分隔符是系统分隔符即空格,比如传入以下字符串,会自动分隔

words="Bash also interprets a number of multi-character options"
#IFS="" #如果加上这个,则会直接输出整句话
for word  in $words
do
        [ `expr length $word` -le 6  ] && { echo $word ; }
done
结果:Bash
also
a
number
of

2.while循环
(1)语法 while [condition ] //写法与for不一样

all=/home/all.txt
if ( ! test -f $all );then touch $all;fi
for file in $(ls /home/shtest)
do
if(test -f $file )
then
echo "==============开始打印文件$file的内容======================================">>$all
cat $file >>$all
fi
done
echo "开始打印all文件======"
while read -r line
do
echo $line
done <$all //<小于号是将后面的文件作为前面命令的输入

(2)linux中小于号和大于号的作用

> 覆盖输出到文本
>>追加到文本
< 将后面文件作为前面命令的输入
<< 带命令作用全文匹配某个字符串后结束
<<<部分匹配某个字符串

(3)while的无限循环语法

while : #或者将:替换成true,:是bash内部命令,
do
echo "测试:"
done

(4)使用w命令查看当前用户正在使用的进程信息 w命令用于显示已经登录系统的用户的名称,以及他们正在做的事
3.until循环语句实例
与while的区别在于until是条件为false时则循环,为true则退出,跟while刚好相反,

var=1
until (test $var -gt 5)
do
echo "进入$var次===="
 var=$((var+1))
done

4.循环控制,break和continue的使用与java中一样
(1)break[n] 如果没有指定n的话则退出状态码为0,且默认退出当前循环,否则退出码为n

path=$1 #第一个变量给path
dir=/home/shtest
dir1=/etc
if ( ! test -d $dir );then echo "目录不存在"; exit;fi
for file in $dir  $dir1  #遍历一个文件夹的文件这样写
do
 for file1 in $file/*
  do
   if ( test $path = $file1 ) #第一 这里要加test
   then
    echo "$path=======path"
    echo "相等=====$file1"
    break 2; #这里break2  则直接退出两层for循环,最后的echo不会执行,不加2的话,外层仍会循环完
   fi
 done
echo "当前遍历的目录是$file"
done

(2)continue

cd $1 || echo "cannot cd to this directory"
for file_name in $(ls)
do
for file_name1 in  *[[:upper:]]* #这个能查出所有当前目录含有大写字母的文件
do
if [ $file_name != $file_name1 ]
then
continue;
else
echo "重命名文件$file_name1"
new=`echo $file_name1 | tr 'A-Z' 'a-z'` #将文件名大写换小写
mv $file_name $new
fi
done
done

(3)[ $# -eq 0 ] && { echo “参数为0”;exit 1; } #这里表示两个命令都执行,大括号前后空格一定要有,每个命令后面都要有分号,否则非常难找到问题
(4)linux文件名通配符,返回的是一个结果集,不能直接与单个文件做是否等值比较
参考 https://blog.csdn.net/weixin_41789003/article/details/79652832
匹配任意长度任意字符, ll * 即列出所有文件
?匹配任意单个字符 ll ? 及列出文件名是单个字符的文件
[]匹配指定范围的任意单个字符 ll [abc]
列出a或者b或者c开头的文件
[^]匹配相反 ,同正则表达式
[:space:]匹配空格
eg:

ll  *[[:space:]]*  列出文件名中有空格的文件
sed 's/^[[:blank:]]*//g' person.txt #也可用来匹配空格

[:punct:]匹配标点符号;[:lower:]匹配小写字母;[:upper:]匹配大写字母;[:digit:]数字;[:alpha:]匹配大小写字母;[:alnum:]匹配数字和大小写字母;使用方法类似于匹配空格的例子

第8章 shell函数
1.函数定义,即复合命令块
function name() { commond1;commond2…} 或者name(){};有function的话()能够省略
2.函数传参、变量、返回值
(1)调用时传参 name arg1 arg2 内部使用$1 $2等占位符访问

function func1(){
if [ $# -eq 0 ];then echo "参数不够";exit 1;fi
echo "第一个参数为:$1"
}
echo "开始调用函数fun1()====="
func1  $1 //这里访问的是脚本执行时的第一个参数,调用函数的方式

(2)本地变量 local var 只能使用在函数中,同时将变量的作用范围限制在该函数中;shell中变量默认是全局的,通过local可改为局部
(3) return结束函数,返回到函数调用的下一个命令,即脚本继续向下执行,return [数字] 返回执行状态,如果没有默认返回函数中最后一个命令的返回状态

echo  "==========开始执行脚本$0====================="
function checkpid(){
if [ $# -eq 0 ];then echo "参数不够";exit 1;fi
echo "第一个参数为:$1"
local num=$1
for pid in `pgrep $1` #通过进程名找进程号,这里可能会找到多个进程号
do
#如果目录存在,即pid存在,则返回到调用地方, 并存下该目录
if [ -d "/proc/$pid" ]  //一般一个进程如果在运行中,都会在该目录下产生一个pid的目录存放具体执行路径等
then dir=/proc/$pid;
echo "列出目录为$dir=========================="
return ; //这里会结束掉函数
fi  
done
}
echo "开始调用函数fun1()====="
checkpid  $1
#echo "函数调用完毕列出结果================"
ls -l $dir

3.函数的调用
(1)脚本中调用 ,函数要定义在调用之前,否则报错
(2)pgrep nginx 这样直接显示出进程号,没有其他多余信息
(3)从函数文件中加载

eg:#!/bin/bash
#将脚本加载到shell环境中
source /home/shtest/function/function1.sh  #用source命令进行函数文件的加载
var="THis is AN APPle,DO you Know?"
 out=$(to_low $var) //这种方式能够将函数中echo的结果付给新变量
echo "打印结果:$out" 
function1.sh的内容
#大写字符转为小写
function to_low(){
if [ $# -eq 0 ];then "缺少参数";exit 1;fi
echo $*
local low=$*  #这里用双引号引住,表示将所有的参数加载进来后,都放在一个字符串中
output=$(echo $low | tr 'A-Z' 'a-z')
echo $output
}
#判断当前用户是否是root,是则返回true,否则false
function is_root(){
[ $UID -eq 0 ] &&  echo  "true" || echo  “false”;
}

(4)判断用户是否是root用户 [ U I D − e q 0 ] , UID -eq 0 ], UIDeq0],UID是当前用户的id,root用户的是0
(5)grep -q “aaaa” function.txt #-q表示静默模式,不打印找到的内容, $?判断 找到则结果为0,否则为1 ; grep中可以使用正则表达式
4.函数放在后台运行 用法 function &,放后台执行后,shell会继续往下执行命令,相当于并行执行两个命令,以下备份的程序,如果不将打印点点点的函数放后台执行,那么程序会串行执行,会一直打印点点点,而不往下执行
eg:#循环输出,,测试&后台运行函数用

function echo_dot(){
echo "please wait ..."
while true
do
echo -n "." #echo -n 输出不换行
sleep   1
done
}
do_backup(){
echo "开始备份..."
of=/home/shtest/zero.sh
if [ ! -e $of ];then touch $of;fi
dd if=/dev/zero of=$of  bs=1024 count=1024000
}
echo_dot &   #函数进程放到后台
 dot_id=$! #进程号保留
do_backup //备份文件
#结束备份后杀死函数进程
kill -9 $dot_id #结束之后杀死函数进程,不再打印点
echo -n "...done"

第9章 正则表达式
1.基本的正则
基本正则表达式元字符包括:* . ^ $ [] ,
\ 表示转义, <>标记单词边界 ,不管扩展还是基本正则,单词边界都要加转义
扩展正则表达式 :都需要用\进行转义, ? + ()圆括号表示子表达式,{}转义波浪括号表示匹配次数,一定要转义,否则当成普通字符串处理 ,| 或
eg: grep “[a-z]{14,}” redis.conf #要用转义大括号,要像搜索普通字符串一样带双引号
grep “dis(([a-z])|([0-9]))+bled” redis.conf #括号和|必须要转义

2.bash正则表达式比较操作符
=~,类似于其他比较符
*这里{}表示匹配次数,不需要加*

eg:
if [[ $1 =~ ^[0-9]+$ ]] #注意这里的符号为两层[[]]
then
echo "匹配成功"
else
echo "匹配失败"
fi;

ps:扩展正则表达式和基本正则表达式就是支持的元字符不一样,前者更广阔
参考:https://man.linuxde.net/docs/shell_regex.html
在这里插入图片描述
在这里插入图片描述
3.bash函数中不能使用exit,否则将退出的是整个shell脚本或者环境,而非函数本身,应该使用return退出
10.脚本输入处理
(1)使用shift命令处理命令行参数,作用是将传递的参数向左转移
i.使用: shift [n], 将前n位(包括n)参数全部归为0,然后将n+1值放在$1,n+2的值放在$2;
ii. 0 不 被 占 位 , n 大 于 0 且 小 于 0不被占位,n大于0且小于 0n0#
iii.shift 后面不跟数字,则默认为是1,即每次向前移动一个参数

while ( test $# -ne 0 )
do
echo "第一个参数值为$1,全部参数为$#"
shift 5
if [ $? -ne 0 ];then exit 1;fi //用这个退出,防止$#永远不等0的情况出现
done

(2) expr 用于在linux下求表达式变量的值,可用于整数和字符串,但是无法求乘方运算

expr 表达式 或者 expr [选项] 表达式 
eg: expr match "sfdsf" "^[a-z]\+$"    等价于 expr "ssfdse" : "^[a-z]\+$" #match选项用于模糊匹配
 expr substr "qweerr" 3 6 # substr截取字符串,下标从1开始算,3 6 截取的结果是eerr
expr  index "strinsg" s #求字符在字符串中第一次出现的下标,下标第一个为1
 expr length "string" #求字符串长度
expr 1 + 2 , expr 2 \* 1 ,  expr 2 - 1 ,  expr 4 % 2 , expr 4 / 2 #整数运算

ps:求变量的长度的几种方式:
参考:https://www.jb51.net/article/121290.htm

name='zhangsahoxua'
 expr length $name #求字符串长度
 echo ${#name}
 echo $name |awk -F "" '{print NF}'
echo $name|wc -L
 wc -L  -L参数
      1) 对多行文件来说,表示打印最长行的长度! 82,表示/etc/passwd文件最长行的长度为82
      2) 对单行字符串而言,表示当前行字符串的长度!

ps:求乘方运算的方式
eg:

let m=2**3  //结果m的值为8
 m=`echo $[2**4]`  #//m的值为16
 m=`echo $[2+4]` #//m的值为6  []是一种数值计算的方式

(3)for循环处理多参数脚本
eg:
for var in KaTeX parse error: Expected 'EOF', got '#' at position 4: * #̲这里还可以用"@",必须要有@符号,而"$*"会将所有参数用引号变成一个参数,即for循环次数为1
do
expr substr $var 2 8
done
(4)总结:对于脚本参数的检验主要是脚本参数的数量,其次是脚本参数是否是预期的,可以对其进行判断验证
(5)统计某个文件夹目录下的文件数量

ll|grep ".*\.part"|wc -l   # 求一个文件夹中的文件个数,其他求解类似,都是命令组合  

(6)第一次上生产的shell脚本记录
作用解释:使用oracle客户端的sqlldr命令进行批量的导数,先判断.ok文件是否存在,存在则进行倒数,循环生成多个文件的不同的ctl文件,定时导入不同表

check_ok.sh
#!/bin/bash
#先确定.ok文件是否存在,存在的话再获得当前日期的前一天,然后再调用另一个导数的脚本
#如果是传参的,则参数必须符合正则表达式
#==============用法说明===============
#function usage(){
#[ $# -eq 0 ]$$ { echo "Usage: $0 date(yyyymmdd)" ; exit 1 ; }
#}
#==================================
sleep_count=1
#前一天的日期
check_date=`date -d "1 day ago" "+%Y%m%d"`
echo "=============check_date is  $check_date============="
count_part=` ls -l  ./ctl_files/ | grep ".*\.part"| wc -l ` #求part文件的个数
result=$count_part
while [ $result -gt 0 ] #如果结果小于总数,说明前一次执行for循环有.ok没有生成
do
#在小循环里遍历每一个part文件名,判断是否有该文件的ok文件
for part  in $(ls ./ctl_files/)
do  
#==========获取表名===============
index=`expr index "$part" .`  #求下标志
index1=`expr $index - 1`
table_name=`expr substr $part 1 $index1 `
ok_file=${table_name}"_"${check_date}".ok"
#循环判断ok文件是否存在
[  ! -e $ok_file ]&&continue;
 result=`expr $result - 1`;#文件存在,则结果减1
echo "======轮询的表名为:$table_name====="
echo "=====ok文件名为$ok_file====="
echo "==============now it's time to load data to table $table_name====================="
./sqlloader.sh $check_date   $table_name  
[ $? -ne 0 ]&&{ echo "sqlloader occur error"; exit 1; }
done #for循环结束
[ $result -gt 0 ]&& sleep 10;
sleep_count=`expr $sleep_count + 1`;
[ $sleep_count -eq 5 ] && { echo "挂起导致结束" ;  break ; } #挂起的次数是100次
done #while的结束
[ $result -gt 0 ] && { echo "======ok文件未完全加载========" ; exit 1 ; }


sqlloader.sh
#!/bin/bash
echo "======slqloader start running===== "
function usage(){
#==============用法说明===============
 echo "Usage: $0 date(yyyymmdd)  table_name" ; exit 1 ; 
#==================================
}
if [ $# -lt 2 ]
then
usage
fi
table_name=$2
date=$1
#ctl文件名
ctl_file=${table_name}_${date}.ctl
#如果已经存在ctl文件,说明当日之前已经导过了,不再导数
if [ -e $ctl_file ]
then 
echo "$ctl_file exists"
else
#构造ctl文件并执行
echo " LOAD DATA                             ">$ctl_file
echo " INFILE ${table_name}_${date}.txt      ">>$ctl_file
echo "append                                 ">>$ctl_file
echo "INTO TABLE $table_name                 ">> $ctl_file
cat  ./ctl_files/${table_name}".part"      >>$ctl_file 
sqlldr sofa/sofa@orcl control=${ctl_file}  log=/home/oracle_client/test/${table_name}.log
[ $? -eq 0 ]&& { echo "========sqlloader finished sussessfully=========";exit 0 ; }
fi;

(7)选项处理,选项可以当成参数传递进去,通过case或者if等判断值进行操作
(8)read获取用户输入
i read -p “enter a value ” var ,将用户键入的值赋值给var变量
ii. read -t 5 -p “Enter your name” ,-t表示等待用于输入超时时间,超时则read命令执行失败,返回非0
iii.read -s 隐藏用户的输入,比如输入密码时可以用

eg: 
#简单打印密码为星号的例子
#!/bin/bash
while [ 1 -eq 1 ]
do
read -s -n1 -r char
if [ -z $char ]
then
echo   #如果输入的是ENter则表示直接退出,打印一个换行即可
exit 1;
elif [ $char = $'\x08' ]  ||[  $char = $'\x7f' ]
then
printf '\b \b' #向左删除前一个字符
else
echo -n  '*'  # 不换行打印
fi
done

(9)shell中单引号和双引号的区别:’'单引号表示直接打印,里面是什么就是什么,双引号会将其中的变量替换成值

eg:  qouta=zhangshaoxuan   
echo "$qouta"  #值为zhangshaoxuan
echo '$qouta'   #结果为 $qouta

(10)shell中字符串截取的几种方式
i.#*号截取,删除左侧字符,保留右边字符
eg:var=zhangshaoxuan echo ${var#g} 结果:shaoxuan 删除左起第一个g以及其左边的字符
ii. ##号街区,左起删除右边第一个字符左边所有的字符,保留右边的字符
echo ${var##a} 结果: n
iii. %字符组合 从右边开始删除第一个字符组合及其右边的字符,保留左边的字符
echo ${var%an
} 结果:zhangshaoxu
iv%%从右边开始,删除最左边一个字符组合及其右边的字符,保留左边字符
echo ${var%%an
} zh #要加星号
v从左边第几个开始截取,下标从0开始算起,以及截取的字符的个数,
echo ${var:0:5} //从第一个开始,截取5个字符,结果:zhang
vi.从左边第几个开始截取,下标从0开始,一直到最后
echo v a r : 2 结 果 : a n g s h a o x u a n v i i . 右 起 第 几 个 字 符 开 始 , 以 及 截 取 的 个 数 e c h o {var:2} 结果:angshaoxuan vii.右起第几个字符开始,以及截取的个数 echo var:2angshaoxuanvii.echo{var:0-4:4} 0-4表示右边数起第4个开始截取,截取4个字符
viii. 参考:https://www.cnblogs.com/yintian908/p/11157423.html
(11)从文件中读取
两种方式 第一种是使用read+while的方式;另一种是for var in (cat filename)可以一行一行读取
此处只研究第二种:
i.for +cat filename这种方式默认情况下是逐个单词读取文本的,因为定界符IFS的默认值就是空格,如果需要一行一行读取,则先要修改环境变量IFS的值

eg:OLD_IFS=$IFS  
IFS=$'\n' #必须先改变IFS的值,$'\n'表示的是将分界符改为回车,这样cat每次读取的是一行,而不是一个单词
for word in $(cat ./kill.sh)
do
echo "-------$word-------"
done
IFS=$OLD_IFS #使用完后改回去

(12**)linux系统下回车换行符 ‘\n’**,windows中回车符为’\r’,回到行首;换行符为’\n’,换到当前位置的下一行;
(13)IFS是shell内部字段分隔符,IFS的默认值为空白字符(换行符、制表符或者空格);当IFS被设置为逗号时,shell将逗号视为一个定界符,如上面的例子,如果要每次读取一行,则需要修改IFS的值,用完后再恢复,设置IFS为回车时,IFS=¥’\n’ 如果不加上¥美元符,则会以字母n为分隔符
eg:以逗号分隔 读取每个数字,将IFS=’,’,使用完后再改回去默认值
第十章 shell重定向
1.输入和输出
(1)标准输入是从键盘读取输入,标准输出时屏幕,改变输入或者输出默认路径的过程叫重定向
(2)输入重定向
i.标准输入设备是键盘
ii.输入重定向操作符 “<”
eg: cat < ./kill.sh #结果将展示kill.sh的内容
(3)输出重定向
i.标准输出时屏幕,也成为stdout
ii.操作符>是输出重定向操作符
(4)标准错误重定向
i.用数字2表示,默认的标准输出设备是屏幕
ii操作符 2>是标准错误重定向操作符,即当脚本执行发生错误时会重定向到指定的文件中
eg:./test_test.sh 2> error.log #脚本正常的日志不会输出到该log中,但错误日志会
2.重定向
(1)linux中3个默认的设备文件总是打开的,即标准输入(键盘)、标准输出(屏幕)和标准错误输出,每个被打开的文件都被指定了一个文件描述符,这三个分别是 0 1 2
(2)文件重定向
i.重定向输出>发生在命令执行之前默认情况下bash不检查文件是否存在,而是直接打开它,若文件已存在,则清空文件内容,否则创建空文件,使得文件描述符1指向它;如果要不清空而是追加内容则为>>
ii.关于cat命令的正确使用,应该使用输入重定向,而非直接cat 文件,无效使用cat命令会导致额外的进程被创建,因为单单cat命令会从标准输入中读取,即键盘,毫无意义,eg:cat < kill.sh #如果kill.sh不存在,则会报错
cat < kill.sh #正确使用方式
iii.重定向符号有作用范围,可以作用在一个命令上,也可以作用在循环上,或者脚本上

eg:for word in $(cat ./kill.sh)
do
echo "-------$word-------"
echo "------------test for >>-------"
done >> ./error.log  #这里作用在循环上,在开始循环之前,bash打开文件,for循环中所有命令都将继承打开的文件描述符
IFS=$OLD_IFS

(3)从文件输入,举例 while+read从文件中一行一行读取
i.ps:read 命令读取文件时,每次都会从文件中读取一行,当文件没有可读行时,read以非0状态退出

eg:while+read,每次读取文件中的一行,重定向输入文件给while
 COUNT=0
while read LINE
do
COUNT=`expr $COUNT + 1`
echo "----第 $COUNT 行内容:--$LINE--------"
done < $1

iii.也可以通过代码块进行重定向输入或者输出

eg:在sqlloader构建ctl文件时可以使用代码块 ,而不用每一行都重定向 
{
echo " LOAD DATA                             "
echo " INFILE ${table_name}_${date}.txt      "
echo "append                                 "
echo "INTO TABLE $table_name                 “
cat  ./ctl_files/${table_name}".part"  
} >> $ctl_file

iv 代码块的两种写法
eg:{ usage ; echo “you sb” ; exit 1 ; } #写在一行 则命令之间需要用分号隔开,大括号与命令也要隔开
eg:类似上面ctrl的写法,写在多行时,则不用分号也行,大括号得另起一行
(4)从文本或字符串输入
i.bash的另一种重定向是here-documents,操作符"<<MARKER",直到只包括MARKER的行为止;选择一个不在数据中的定界符,常用:MARK、 END、 EOF,<<mark所有行都会作为命令的标准输入,

eg:tr a-z A-Z << EOF   #定界符开始,这里 <<EOF符号与字母之前有无空格都可
zhangsan lisi  wangwu zhaoliu
`cat ./to_lower.sh`   内部的命令和变量也会被求值运算
EOF  #定界符结束

结果: 所有小写字母转为大写输出
ii.<<-EOF表示忽略首航的制表符
iii.单引号或者双引号加在定界符上会使得内部的变量和命令失效,当成普通文本输出

eg:
tr a-z A-Z <<-"EOF"
	zhangsan lisi  wangwu zhaoliu
`cat ./to_lower.sh`
EOF

结果: ZHANGSAN LISI WANGWU ZHAOLIU
CAT ./TO_LOWER.SH
iv.常用作向用户展示命令或者脚本的用法

function usage(){  //用法
 cat <<EOF 
Usage $0 filepath;a parameter is necessary;
you should give one 
EOF
}
if [ $# -eq 0 ]
then
usage
exit 1
fi

iv.here-strings是here-documents的一个变种,由操作符<<<和一个字符串构成,字符串如果是单个单词则不用双引号,否则需要双引号

eg:  
tr a-z A-Z < one   #报错找不到这个文件
echo --------一个单词 
tr a-z A-Z <<< one  #结果为ONE
echo ------两个单词无引号测试------------
tr a-z A-Z <<< one two  #报错 额外的操作数two
tr a-z A-Z <<< "one two"  #结果 ONE TWO

v. 标准错误和输出同时定向 &>命令
cat echo &>> error.log #将错误信息和日志都打印到error.log中
vi.单命令行输出输入一起做
eg:将一个文件小写转大写并输出到另一个文件中
tr a-z A-Z < ./test.log >result.log #先输入文件再输出到文件
(5)linux中清空文件内容的几种方法总结:
i. >access.log 重定向空到文件中来清空文件,如果没有该文件,则创建一个新的文件类似touch
ii.cat /dev/null >access.log 或者cp /dev/null >access.log 重定向null文件到文件来清空文件
3.文件描述符
(1)一般使用文件描述符的范围是0-9,标准输入、输出 、和错误是0 1 2 在shell启动时被建立
(2)使用exec命令,将文件赋给文件描述符
exec后如果没有指定命令,则重定向将改变当前shell的文件描述符
i.指定用于输入的文件描述符 :exec [n]<file 在文件描述符n上打开一个用于读取的文件file

eg: exec 3<.test_test.log #注意 文件描述符一定要和<贴一起,否则会把文件中的每一行当成命令执行,遇到exit时直接客户端退出

ii.直接exec <file 其实是将文件重定向到标准输入0
cat <& 3 #读取其中的内容 ,其实是将文件描述符内容复制到标准的输入
(3)<& 复制重定向 将一个打开的文件描述符复制输入到另一个文件描述符

eg: exec 3< ./test_test.log
exec 4<& 3  #将3复制到4
cat <&4  #这样cat的实际上是描述符3的内容

(4) read命令如果要从标准输入中读取,可以先在shell中打开标准输入文件操作符0,这样就不会等待用户输入了

eg: 
exec 3< ./error.log  # 重定向指文件描述符
while read -u 3 line  #read -u 可以读文件描述符,这样的话就可以只针对某个文件进行读取,不影响其他命令
do
        echo "-----line: $line-cat---------"       
         read -p "enter value :"   enter    #这里默认还是读取标准输入0
        echo "------enter :$enter--------------"
done
exec 3<&-  #用完文件描述符要关闭

(5)用于输出的文件描述符 exec [n] >file
i.
exec 4>error.log
ll >&4 #指定文件描述符,将命令结果写进去
ii.>& 复制输出文件描述符
exec 4>error.log;
exec 5>&4;
echo --test---- >&5 #这样会将内容写到error.log中
ps:以上的描述符4和5已经是输出描述符了,不能用于cat等需要输入描述符的命令
(6**)用于打开读和写的文件描述符**
i.exec [n] <>file n不指定的话则只表示标准输入
ii.对读写都可的文件描述符来说,读和写都会使得光标后移,有点类似于队列,后移后 文件描述符中的信息在光标之前的就会被pop
eg:

echo "one two" >./error.log
exec 4<>./error.log
read -n 5 var <&4 #光标位于w的位置了,显示one t
echo -------echo var---------
echo $var
echo -n  + >&4  #在光标处写入+,即替代w,然后光标后移
echo --------cat 4----------
cat <&4  #显示 光标处的o(w后面的)
cat ./error.log  #显示one t+o

(7)/proc是一个伪文件系统,linux中每一个进程在该文件夹中都有以进程号命名的文件夹,其中有该进程打开的文件描述符号

eg:vim /home/shtest/bak.sh 进程号为:10230
则在/proc/10230下有个fd文件夹,其中有进程正在操作的文件

第十一章 管道和过滤器

  1. |和>区别: 前者是连接命令和命令的,后者是把命令和文件联系起来,不可乱用,以下是乱用的 反例
eg: ll >more  #这样会将ll的结果输出到more文件中,如果目录中已经有more了 则会将more清除干净,如果是系统重要文件则会破坏系统本身

2.用法
(1)默认情况下命令是先从自己的参数中获得输入的,然后再从标准输入中获得

eg:ll |grep bin ./regex.sh   #这个样会先在文件regex.sh中搜索,结果:#!/bin/bash

(2)将文件内容放到一行,并用逗号隔开

eg: cat line1.txt  
12 12 33 23 4 545 65 67 676 87 7 79 89789 79 879 df 798 798797 erw rwer 32 rr er re re 
cat line1.txt | tr " " "," >line.txt

结果:12,12,33,23,4,545,65,67,676,87,7,79,89789,79,879,df,798,798797,erw,rwer,32,rr,er,re,re,er,e,
(3)管道符中使用重定向并不冲突

eg:cat <result.txt |grep "bin"  >>result.txt
who | sort >user.list  #将当前登录系统的用户排序输出到文件中
cat ./test_test.sh  |tr a-z A-Z |sort | uniq  #将文件内容大小写转化,排序 并去重复的行

3.过滤器
(1)linux命令从标准输入接收数据,并在标准输出上输出,则称该命令为过滤器;一般与管道一起使用
eg:grep ,head ,tail等等
(2)常用作为过滤器的命令
i.cut -b 以字节为单位分割;cut -c 以字符为单位;cut -d 自定义分割,默认为制表符;-f与-d一起使用,指示显示的区域

eg:grep "/bin/bash" /etc/passwd |cut -b 1-3   #-b 截取1到3个字节
roo 
zsx
echo "你好" |cut -c 1-   #-c表示按字符截取,1-表示从第一个开始截取到最后一个

结果: 你好
grep “/bin/bash” /etc/passwd |cut -d: -f1,4 #以:冒号为分隔符,展示第1和第4字段值
结果: zsx:/home/zsx
ii. head 显示文件的前几行

eg; ll | head -10  显示前10行文件
ps:head和tail结合起来能够输出文件的第几行
eg:head -10|tail -n 1 #输出第十行

iii paste 合并文件

eg:paste -d, bak.sh  case_bak.sh  两个文件的每一行都两两合并,以逗号分开,不加-d则默认是空格分开
paste -s bak.sh case_bak.sh 两个文件合并为一行 所有内容都写在一行

(3)split 文件分割 -l表示按行,-b表示按字节

eg: split -l 5 function.sh  -d  function # 意思是将文件function.sh每5行分割,然后将分割内容分别放进以function开头额文件, 如果不加输出文件名 则默认以x开头的文件; -d加上则文件后缀是数字,否则是abc..等字幕
split -b 10k/10m function.sh funciton.txt  #这个就是将文件按照字节大小分割,4
eg: ll | split -l 3 - outfile ; #此时outfile是需要加-的

(4)strings 可打印字节码文件中的字符串
eg: strings /usr/文件
(5)tee 将标准输出重定向到某个文件并继续保留标准输出,即既在屏幕上输出,又输出到文件中
i. -a 表示append 追加,,如果输出文件是-,则将标准输出再进行一次,相当于屏幕上输出两次
echo “—this is a test for tee------------” | tee -a SCORE.SH
ii.在管道的不同阶段存储命令不同结果
eg: ls | tee -a out.list | grep ^o | tee -a out.list #先将ls的结果存在文件中,再将grep的结果存入
iii

第十二章 捕获
1.信号
(1)信号被用于进程间的通信,信号是发送到某个进程或者同一个进程特定线程的用于通知发生了一个事件的异步通知;信号主要用于异常处理和中断方面
(2)内核模式下,代码具有对硬件所有的控制权限,可以执行所有的cpu指令以及访问所有的内存地址,是os中最底层最核心的代码,任何异常都是灾难性的;用户模式则是通过调用系统接口间接访问硬件,多数的应用程序和进程都是在用户模式下的
(3)只有用户模式下的进程会接收信号,若进程在内核模式中运行,则信号的执行只能等待进程返回至用户模式
(4)休眠的进程分可中断和不可中断;可中断的进程(如等待输入)收到信号,内核会唤醒该进程来处理信号;不可中断的进程内核会拖延该信号,指导进程处理完上一个任务
(5)进程收到信号时,会做3种处理
i.忽略,有些信号不可忽略,而没有默认行为的信号则会默认被忽略
ii.捕获,根据信号执行信号处理器的函数
iii.执行信号的默认行为
(6)信号的名称和值 信号以SIG开头,定义为正整数 , kill -l 显示所有的信号值和相应的信号名
(7)bash中的信号
i.默认情况下shell接收到SIGHUP信号后会退出,退出前会向当前shell终端下所有的作用重新发送SIGHUP信号,也就是说默认情况下当shell登录退出后,会向所有的作业发送SIGHUP信号,该终端下所有的进程都会停止运行
ii.disown 命令可以将进程从作业中移除,使得shell终端退出时无法向该进程发信号,进程还在

eg: sleep 300000 &
 jobs  #结果是会列出目前所有的作业,
disown %1 #选择作业号, 这样会将将该进程从作业中删除
jobs  #结果会发现刚才的作业消失了
shell终端退出,结果是换一个终端登录发现该进程还在运行

iii.disown -h 不会从作业中删除该进程,但是会在做标记,这样shell终端退出时也不会向该进程发送SIGHUP信号

(8)常见信号含义及信号值**在这里插入图片描述
在这里插入图片描述
2.进程
(1)概念
i.进程是运行在os中的程序的实例,该实例中包括了运行程序所需要的所有的特定环境
ii.OS通过PID的数字编码来追踪进程,每个进程PID都是唯一的
iii.同一个程序在不同shell终端运行时,每个终端都是不同实例,每个实例都有不同的进程号
iv
.ps -f 前面的进程号是pid,后面是PPID即父进程,每一个程序执行时,都把当前shell的进程当做父进程**

eg:UID         PID   PPID  C STIME TTY          TIME CMD
root      11265  11261  0 09:42 pts/1    00:00:00 -bash   #当前bash的进程是11265
root      11284  11265  0 09:43 pts/1    00:00:00 sleep 3000 #PPID 11265
root      11395  11265  0 10:07 pts/1    00:00:00 ps -f  #PPID 11265

例如执行ls命令时,linux内核将创建一个shell的子进程来执行命令,shell本身的进程就作为其父进程,子进程会指向与此shell相同的内存页
(2)前台进程和后台进程
i.当前台执行进程时,不能再命令提示符下运行其他进程操作
(3)进程的状态
D 不可中断休眠状态 ----进程正在休眠并不能恢复,知道某个事件发生
R 运行转态 --正在运行
S 休眠状态 ----进程没有运行,在等待信号唤醒
T 停止状态 -----进程被信号停止,如信号SIGINT活SIGSTOP
Z 僵死状态-----标记为defunct的进程是僵死的进程,,父进程会适时的销毁它,若父进程退出,则这些进程也会被销毁
(4)查看进程

i.ps aux 会比 ps -e多出来进程的运行状态
ii.pgrep 列出与选择相匹配的进程的ID  pgrep [选项]  匹配名称
eg: pgrep -l  sleep  #显示进程号和名称
11454   sleep
pgrep  sleep
11454  #直接列出进程号
pgrep -lu  root # -u 后面加用户名 查某个用户的进程号

(5)向进程发送信号
i.ctrl+c 终止信号,SIGINIT信号被进程的控制端发送给进程,中断进程运行
iictrl+z 挂起信号, 停止信号TSTP发送到运行的进程使得其停止
iii.信号值或者信号名都可作为kill命令的选项
eg : kill -9 123 #9是信号SIGKILL的信号值,kill信号会让进程立即终止,且该信号不能被忽略
kill -SIGKILL 123 / kill -KILL 123 #等价于信号值9
(6)关于子shell
i.子shell是由shell或者shell脚本运行的子进程,shell脚本可以自己启动子进程
在一个shell中用括号括起来就是子shell,启动一个子shell方式
: (command1;command2;command3)

(
while  [ 1 -eq 1 ]
do 
echo "--------执行---------"
done
)
ps
root      14213  14189  0 13:42 pts/0    00:00:00 /bin/bash ./whiletest.sh  shell进程
root      14214  14213 60 13:42 pts/0    00:00:06 /bin/bash ./whiletest.sh  子shell进程

ii.子shell中的变量是局部变量,不能传到父进程shell中;父进程shell中的变量即便是在子shell中进行操作,其作用范围也还是子shell内部,而不会作用在父shell

eg:var0=111
echo "------父shell开始中的var0:$var0-------------------"  #值为111
(
var=1234
echo "--------执行$var---------" #打印 1234
var0=112
echo "------var0:$var0---------" #打印112
)
echo "--------父shell的变量$var------------"  #打印空
echo "---------父shell中var0:$var0----------------"  #打印111

iii.利用ii中子shell变量的作用范围只限制在子shell中的这一特性,可以为某些命令设置专门的环境来执行

eg
(
IFS=,  #分隔符内部变量的改变作用范围仅限于子shell
for var in  $(cat ./kill.sh)
do
	echo $var
done
)
for var in  $(cat ./kill.sh)
do
        echo $var
done
子进程和父进程运行的结果:
子shell: 12
12
33....
父shell :12,12,33,23,4,545,65,67,676,87,7,79,89789,79,879,df,798,798797
erw,rwer,32,rr,er,re,re,er,e

3.捕获
(1)trap 当程序内捕获特定信号并对其进行处理
i. trap command signal [signal] #捕获信号并执行命令,信号可以写信号名也可以写信号值
ii trap后的signal可以被指定为ERR,当命令以非0状态退出时,command就会被执行;
eg; trap “echo there is an Error” ERR
cat r #结果:cat: r: 没有那个文件或目录
These is an Error #信号值为ERR,非0状态都会被捕获,之后会打印出这句话
iii ctrl+c对应的 SIGINT信号,如果不想让ctrl+c中断某个脚本时,可以将信号捕获,然后打印空格,这里会将sleep打断一次,但脚本还会运行

eg: trap " " SIGINT
COUNT=0
while [ $COUNT -lt 100 ]
COUNT=`expr $COUNT + 1`
do
        sleep 5
        echo "---------休眠5s---COUNT:$COUNT--------" 
done
trap - SIGINT

iv.对捕获的去除,trap - SIGINT 加横杠就可以,去掉了该信号;在脚本中捕获信号时,可以移除
第十三章 sed和awk 主要用于处理文本文件
1.sed编辑器基础
sed能做的操作如下:
自动化编辑一个或多个文件;简化在多个文件中执行相同编辑的任务;编写转化程序
2.sed模式空间,即一个临时缓冲区,编辑时在那里存储单个输入行;sed一次处理一行的优势在于当处理非常大的文件时不用将文件整个读入内存,防止内存溢出或者处理速度非常慢的情况出现
3.基本的sed编辑命令
(1).sed即可以在命令行中指定指令,也可以从文件中读取指令
语法:sed [options] command ; sed [options] -f file
(2)sed指令的语法形式 [address[,address]][!]command
i.指令由地址和命令组成,如果地址和模式空空间中的行匹配,则编辑命令被用于匹配的行,若指令没有地址,则应用于每个输入行;如果编辑命令改变了模式空间的内容,后续的编辑命令将被应用于当前行,而不是原始的输入行
ii.编辑命令还可以用大括号分组,以使用在同一个地址上
address{ #左大括号可以与第一个命令一行
command1
command2
command3
} #右大括号必须单独一行
(3)追加a、更改c、插入i
i. 追加

-------whiletest.sh-----------------
 /person_info:/a\     #用//标注当前行,a\表示追加
name:zhangshaoxuan\   #多行内容,每行后都要有反斜杠,最后一行除外
age:13\
E-mail:zhangshaoxuan@163.com
-------examper.txt----------------
jobs:
hobbies:
person_info:
命令: sed -f whiletest.sh examper.txt    #当插入的内容来自文本时,则需要加-f
结果:jobs:
hobbies:
person_info: #在这个之后加了追加的内容
name:zhangshaoxuan
age:13
E-mail:zhangshaoxuan@163.com

ii.插入i,将上例子中a\的位置换为i\, 结果会在选中的当前行的上方插入内容
ps:上面因为涉及到了跨行,所以选项a i等后面加上了
iii.内容直接来自命令时,则不需要-f

eg:sed '$aEnd of the file' examper.txt #在文件最后一行追加内容,$表示寻找最后一行

iv.追加和插入操作只能针对单个行地址,而更改命令c可以处理一个范围内的行;且c会清除模式空间,而a和i不影响模式空间内容
(4)删除编辑命令d
d会导致读取新输入行,而编辑脚本则从头开始新一轮,某行一旦匹配到这个地址,则删除整个行,而非只删除行中匹配的部分
eg:删除一个文件中的空行

sed /^$/d test_test.sh #正则表达式匹配空白的行,d表示删除,结果为文件不空行输出,但不影响原本文件内容
sed '1,7d' test_test.sh #删除第1-7行内容

(5)替换编辑命令 s
i.语法:[address]s/pattern/replacement/flags
flags 的修饰标识:n,1-512的数字;表示替换每行第n个匹配到的字符串;g对匹配到的内容全局替换,不加g则只是第一次; p打印模式空间内容,即匹配到的内容,w file 将匹配到的内容写入文件;以上标识可以组合使用;g最常用
ii.地址需要定界符斜杠’/’,地址也可以不写,那将替换所有匹配行,但是s一定要写,它是替换的标志;匹配模式如果不写,那将匹配的是地址内容本身,这在{}表示同个地址多个指令中应用s时特别有用
iii.正则可以用任意字符来分隔,除了换行符,定界符必须出现3次,如果出现在替代字符或者正则中,则反斜杠转义,即:
eg: s!\t!\n!g #s/\t/\n/g 效果是一样的,都是全局匹配制表符,替换为换行符;
iv.replacement中的元字符:&表示正则中匹配到的字符串内容; \n匹配第n个子表达式的值,\转义&或者\或者换行符等
eg:

echo "/home/person"|sed -r 's@(/.+/)([^/]+)/?@\1@g' #加上-r 使用扩展正则,不用上面那么多转义, \1表示取第一个子表达式
结果:/home/ 
eg:cat seds 
s/zhangsan/wanglin\&hanmeimei/g  #将zhangsan替换为wanglin&hanmeimei
sed -f seds whiletest.sh #结果为:wanglin&hanmeimei	lisi	wangwu	zhaoliu	maqi		
cat seds
s/zhangsan/wanglin&hanmeimei/g   #将转义符去掉

结果为:wanglinzhangsanhanmeimei	lisi	wangwu	zhaoliu	maqi	  &会变成zhangsan

ps:s替换和c的替换不一样,c在匹配后会将匹配行整行替换掉,而s只替换掉匹配行中匹配到的内容

cat person.txt 
person_info:
zhangshaoxuan
 sed '/person/cname:'  person.txt 
结果替换整行:name:
zhangshaoxuan
sed 's/person/name/' person.txt 
结果:替换匹配字段name_info:
zhangshaoxuan

(6)读取下一行的命令 n 匹配行之后将匹配行的下一行读到模式空间中

eg: /person_info/{  #seds文件中的内容,匹配person_info
n                           # 第一个命令将匹配行的下一行加载到模式空间中
/^$/d                     # 判断模式空间中的行是否匹配第二个命令内容,若是则做删除操做,删除匹配行的下一行空行                          
}                                #大括号表示多个指令应用于同一行

(6)读和写文件编辑命令
i.r和w用于直接处理文件 ,语法如下:
[address] r file; [address] w file
ii.r将文件内容读到模式空间的匹配行后,w将模式空间的内容写到参数file所指定的文件中;文件不存在时r不报错,w将新建 一个文件
iii 注意 r和w后面的参数file只能是文件,而不能是字符
eg: cat end
结果: #------this is the last line --------------------
sed -i 'KaTeX parse error: Expected 'EOF', got '#' at position 14: r end' *.sh #̲读命令将end文件中的内容读取…匹配最后一样,
结果:在所有.sh文件的最后增加end文件中的内容
iv.读文件后面的编辑命令不会影响读文件命令读到的行,也就是说该命令执行完后的如果还有命令执行,后面的命令完全不起作用
v.写文件命令功能是从一个文件中提取信息并将他放置在其他文件中
eg: 将内容分类

cat matchtype  #待匹配文件
huawei android
rongyao android
xiaomi android
iphone ios
imac   ios
iwatch  ios
cat seds  #指令文件
/ios/w ios.txt  #写后面跟的是目标文件
/android/ android.txt 
sed -f seds matchtype  #结果是生成两个文件ios和android.txt存放不同类型的内容

vi.如果要在写入之前删掉对应的类型,则可以

eg:/ios/{
s///g  #匹配模式不写时匹配的是地址,用空白替换
w  ios.txt
}
/android/{
s///g 
w android.txt
}

(7)sed 命令常用选项
-e 将下一个参数解释为sed指令,命令行上多个sed指令时才需要用-e选项
-f 指定由sed指令组成的脚本的名称
-i 直接修改读取的内容,而非输出到终端
-n 只显示匹配处理的行(否则会默认输出所有)(也就是关闭默认的输出)
(8)-p指令,打印模式空间内匹配的行
eg:

sed  -n '/San/p' datebook  #结果只显示含有San的行,p表示的是打印出匹配到的行,-n表示取消默认输出,
Jon DeLoach:408-253-3122:123 Park St., San Jose, CA 04086:7/25/53:85100
Jesse Neal:408-233-8971:45 Rose Terrace, San Francisco, CA 92303:2/3/36:25000
Jose Santiago:385-898-8357:38 Fife Way, Abilene, TX 39673:1/5/58:95600
sed  '/San/p' datebook  #如果不加-n则会打印全部文本,同时匹配到的行重复出现 

4.sed命令实例
(1)向文件中添加或者插入行
i.第5行后添加一行
eg;sed -i '5athis is the content for fifth line ’ whiletest.sh
ii.在匹配行之后添加内容
eg: sed -i ‘/person_info/aname is zhangsan’ whiletest.sh #在person_info后追加name is zhangsan
iii.文件最后一行添加多行内容
sed -i ‘KaTeX parse error: Expected 'EOF', got '#' at position 26: … #̲需要换行多行追加的时候才用\ …d’ shelltest.sh #从匹配到的删除到最后一行 ,^$匹配空行
eg: sed “/xi’an/,/zhouzhi/d” person.txt #从匹配到的xian行删除到zhouzhi行
vi.删除指定行范围内符合匹配模式的行
eg: sed ‘1,10{/function/d}’ shelltest.sh #删除1-10行内匹配到function的行
(4)替换文件中的内容 s,替换有个好处是,能够避免向删除那样每次一删除就是一行,替换可以将行中匹配值替换
i.替换匹配行中第一个符合匹配模式的字符串
eg: sed ‘s/function/functioning/’ shelltest.sh #将文件中的function替换为functioning
ii. 替换所有匹配的字符串
eg; sed ‘s/function/functioning/g’ shelltest.sh
iii. 替换每行第n个匹配到的字符串
eg:sed ‘s/function/functioning/1’ shelltest.sh #将每行匹配到的第1个匹配值进行替换
iv.替换掉匹配行中符合匹配模式的字符
eg: sed ‘/:/s/😕/g’ shelltest.sh #匹配文本中:的行,然后将行中:替换为空白,即删除
v.删除注释
eg:sed ‘s/#.*//g’ shelltest.sh #匹配所有行中#的内容全局替换为空白
(5)向文件中读取和写入,读和写都是针对的是r和w后的file,读是读出后追加到匹配行后,写是将匹配的内容直接写到file中
i匹配文件的每一行后面读一个文件的内容
eg:sed ‘r ios.txt’ android.txt #由于r前面没有写具体的行匹配模式,将会在android.txt的每一行后面都插入ios.txt的内容
ii.匹配具体的行后面读入文件的内容
eg:sed ‘/rongyao/r ios.txt’ android.txt #结果是会在rongyao后面插入ios.txt
iii.指定行后读入

eg:sed '2r ios.txt'  android.txt  #在第二行后读入  2可换成$表示最后一行
iv.将文件第一行到最后一行写进入
eg:sed '1,$w ios.txt'  android.txt
v.并集匹配行
eg:sed '/huawei\|rongyao/w ios.txt' android.txt  #匹配huawei或者rongyao,通过|连接 但是需要转义
eg: sed '/huawei\|xiaomi/s/$/& sb/g'  android.txt   #行中有huawei或者xiaomi的行,将行的最后替换为本身和sb,行的最后用$匹配
vi.将匹配行及其后n行的内容写到文件中
eg: sed '/rongyao/,+4w ios.txt'  android.txt  #在android.txt中匹配rongyao,然后将其及后面的4行写到ios.txt中
eg: sed '/rongyao/,+4d'  android.txt  #这种模式也能用于d删除

(6)sed支持的几种地址类型

i.first~step 起始匹配行~步长 
eg: 将文件第n和第n+1行合并为一行,
sed -n '1~2p' person.txt >temp1.txt  #-n表示取消默认输出,否则会重复输出匹配的行,p表示打印模式空间的内容
sed -n '2~2p' person.txt >temp2.txt 
paste temp1.txt  temp2.txt   #paste默认以制表位将两个文件行合并
ii.addr1, +N,从 addr1 这行到往下 N 行匹配,总共匹配 N+1 行
eg: sed -n '1,+2p' person.txt
iii. addr1,~N 从add开始往下匹配,知道行数是N的倍数
eg:sed -n '/addr/,~3p' person.txt
iv. sed '1,3d' person.txt  #从第一到第三行

4.sed与shell
(1)在sed中使用shell变量
i:在s替换指令中使用时,双引号引住指令, 无 法 再 表 示 为 最 后 一 行 , 即 不 可 能 在 同 一 个 指 令 中 既 用 无法再表示为最后一行,即不可能在同一个指令中既用 表示最后一行又表示变量的引用

eg: sed "s/^$/$var/g"  person.txt #对空白行替换为变量的值,s前如果再加$则会报错

ii.删除文件空白行

while ( test $# -ne 0 )
do
sed '/^$/d'  $1
shift
done

iii.替换文件内容

eg:[ $# -ne 3 ] && { echo "Usage: targetfile targetString replacement"; exit; }
oldString=$2
replacement=$3
file=$1
[ ! -f $file ] && { echo "file doesn't exist!!!" ; exit ; }
sed "s/$2/$3/g"  $1

iv.在shell中写多行sed指令执行,用来格式化文本
ps : fold -w 30 file 格式化文本,限制输出行最多30字符,即限制列的宽度

eg:var='/^$/d   #匹配文件中的空白行删除
s/\t//g' #匹配制表位,将制表位替换为空白,即删除制表位
sed "$var" $1

(2)从sed输出中设置shell变量
eg:重命名文件

for filename in $(ls *$1* )  #这里也可写成 in *$1*
do
	if [ -f $filename ]
	then
#将文件前缀去掉,只留文件名 
		basename=`basename $filename`
#echo 这种通道符的形式,是直接将echo的输出当成文件的输出,即原本是从文件中获取其输出的,现在改变的sed命令的输入来源,从echo的输出中获取
		newname=`echo $basename|sed "s/$1/$2/g"` #变量的值从sed的结果里设置,原本是标准输出,现在改为设置为变量的值
		mv "$basename" "$newname"
	fi
done
eg:从管道符中获取sed内容:
temp1=`echo $temp|sed 's/bd/./g'`   #将变量temp中的bd替换为.并将结果赋值给temp1

5.awk基础
(1)awk基础语法
i.awk指令由一个模式和一个动作组成, 也是对文件中的匹配行一行行做操作,如果不写pattern则对所有行执行
ps:awk的指令一定要放在’'单引号当中
语法:
pattern {action} #指令放在大括号中,与匹配模式分开
pattern {action}

ii.运行awk命令的方式跟sed命令类似,有两种方式
awk [options] [–] program–text file… #直接通过命令行执行
awk [options] -f program-file [–] file …#将指令写在文件中执行
iii.常见选项
-F fs-指定用于输入的数据列分隔符fs,指定多个分隔符可用 -F "[:;]",类似于正则中的或运算,指定[]为分隔符 则-F "[][]"awk默认会以空格和tab键作为文件内的默认分隔符
-v var=value ----awk执行前指定一个值给变量,用于awk的begin块
-f 指定一个指令文件 同sed
–表示命令行选项的结束,例如文件开头如果是-,不加–的话文件名也会被识别为选项
(2)awk打印指定的列,awk在处理已经被分隔为多个逻辑列文本方面有优势

eg:cat person.txt
person_info:  zhangshaoxuan
job: java engineer
experience: 3 years
address: shannxi
awk -F ':' '{print $1}' person.txt    #-F指定分隔符为:,打印第一列,默认分隔符为空格
结果:person_info
job
experience
address
ShannXi

eg:

awk '{print $0}' person.txt  #打印整行,$0表示整行,不加也可,结果是cat person.txt的内容

(3)从文件中读取awk指令

eg:cat awk 
BEGIN {FS=":"}  #BEGIN和FS必须都大写,否则设置无效,会被忽略
{ print $1}
{print $2}

(4)awk的BEGIN和END块
i.BEGIN块在awk开始处理输入文件之前被调用,适合做一些初始化的操作,如设置变量FS等,END是将输入的文件所有行处理完后执行的操作
(5)awk中使用正则表达式
i.正则表达式其实就是pattern部分,用来匹配行
eg: awk ‘/s/{print $1}’ person.txt #先匹配有s存在的行,然后打印这些行的第一列
awk ‘$1 ~ /s/{print $1}’ person.txt #对第一列进行正则模式匹配,如果第一列有s存在,则打印第一列
awk -F ‘:’ ‘$1 !~ /s/{print $2}’ person.txt #以:为分隔符,对每行第一列做不匹配,如果不存在s则打印该行第二列
ii.通过管道符给awk以输入

eg:cat /etc/passwd | awk -F ':' '$1 ~ /root/ {print $6 }'   
//打印所有redis的进程号
 ps -ef|grep redis |awk   '{print $2}'

iii.匹配[]字母或者数字

awk '$1 ~ /[A-Z]/{print $1}' person.txt  #在第一列中匹配A-Z中任意一个并打印第一列,
awk '$2 ~ /[0-9]/{print $1}' person.txt #匹配数字 
awk '$2 ~ /[123]/{print $2}' person.txt  #匹配123中任意一个

iv.awk中支持的正则元字符

^、$、.、*、+、?、[ABC][123][a-z][^a-z]、A|B匹配A或者B、(AB)+匹配AB一个或者多个组合,\*匹配星号,&代表匹配到的内容

ps !表示匹配和不匹配
(6)awk的表达式和块
i.awk可以通过布尔表达式来特定的块什么时候执行

 awk 'BEGIN{FS=":"} $1=="job" {print}'  person.txt   #所有的指令都写在单引号中,执行的动作用大括号,第一列等于job的行进行全行打印

ii.awk中的比较操纵符 包括 ==,<,> <=,>=.!=以及匹配和不匹配!
iii.awk还允许使用逻辑或||以及逻辑与&&创建表达式

eg:  awk 'BEGIN{FS="  " } ($10=="yuanyu" || $3 ~ "测试") {print} ' excel.txt #以两个空格为分隔符,第十行为"yuanyu"或者第三行内容包含“测试” 进行全行打印,注意条件要加括号

ps:如果表达式是正则则可以用//,否则必须用双引号
iv.awk中的表达式和变量,awk支持整数和浮点数运算以及完整的算术运算符

eg:#-------------计算文件空白行的个数---------------------------------
#在对行处理之前初始化变量
BEGIN {count=0}
/^$/{ count=count+1}  #正则表达式时候才能用//,此处对变量值的引用不用加$,与bash中的变量引用不同
#处理完所有行后,才打印
END {print "the number of blank line is" count} #不用加$

eg:-----普通的数学运算-----,awk执行语句中可以直接做运算

 awk ' $1 ~/[0-9]/{print $1 "  第一列的平方:"$1^2 }' excel.txt  
结果:164  第一列的平方:26896
513  第一列的平方:263169

awk -F ":" '{print $1 ": "$3+$4+$5}' datebook  #这里会直接显示3列的和

v.awk中的特殊变量
NF的值是当前行的总列数,而非第几列,第几列是用$1,$2代表的
eg: awk ‘(NF>1||$1 ~ /xi/) {print }’ person.txt #匹配列数大于1或者第一列中有xi的行并打印
NR的值是当前记录的行数,即表示第几行
eg:awk 'NR ==3 {print $2}' person.txt #打印第3行 第2列的数据

eg:awk '{print NR ".\t" $2}' excel.txt  #打印行号,NR直接写在print后面表示变量,如果加引号则只表示打印字母“NR”
结果: 1. 20190614
            2.  20190302 
.....

vi awk中的循环结构,在执行的时候加,即放在{}中
结构1:while (condition)
body

eg:cat awk
#----while循环测试---------------------------
{ i=0
 while (i<3)
 i++
 print $i  
}

awk -f awk excel.txt #结果展示每行的第三列,而没有展示每行的前三列,应该是print在输出的标准输出之前被刷新了
(7)awk和shell
i.awk中使用shell变量,通常使用选项-v或者BEGIN来将shell变量的值付给awk变量

eg:awk -v pat="$pattern" '$1 ~ pat {print}'  person.txt   

结果: 在shell中执行时,变量pattern的值会给到pat
eg:利用BEGIN块传递,双引号+单引号引用,否则不起作用

awk BEGIN { print "'$filename'"}

ii.从awk的结果中设置shell变量
eg:

cat person.txt 
person_info:  zhangshaoxuan

job: java engineer
experience: 3 years

address: shannxi
ShannXi
xi'an
zhouzhi

	this is the
x=`awk 'NR>2 {print}' person.txt`  #直接赋值  
echo $x
结果:job: java engineer experience: 3 years address: shannxi ShannXi xi'an zhouzhi this is the 变为一行了

iii.从awk的结果中设置多个变量的值
eg:

z=`awk '/add/{ print "x="$1; print "y="$2}' person.txt` #总体思路是通过多个print进行打印,然后再分割
echo $z 结果:x=address: y=shannxi
eval $z #将z的值作为shell命令来执行
echo $x # address:
echo $y #shannxi
或者通过source命令
eg:awk '/add/{ print "m="$1; print "n="$2}' person.txt>source.txt  
source source.txt
echo $m

iv.awk的数组如何判断键值是否存在
eg:

   if(  $1 in IPArr ){  #这样,通过in的方式,判断
                
                }else{
                        IPArr[$1]=0     
                }

(8) awk实例
i.awk统计字符出现的次数

eg :统计单个字符出现的次数 
[ $# -ne 2 ]&&{ echo "Usage:$0 filename  char"; exit ; }
filename=$1
char0=$2
#校验文件
[ ! -f $filename ] && { echo "$1 is not a file"; exit; }
#校验字符数量
length=`expr length "$char0"`
[ $length -gt 1 ] && { echo "the length of char should be 1 " ; exit ; }
#换行\后面不能有任何字符,否则失效
 awk -v char="$char0" \  
 'BEGIN {    
# 也可以将char变量写在BEGIN里, char="'$char0'"
 count=0;
 }
{       split($0,tab,"");
        for (i in tab) 
        { 
                if(tab[i]==char){ 
                        count++ 
                }
        }
}
END { print  char " ==> " count}'  person.txt

ii.统计多字符出现在文件中的次数

eg:#------------------------------------awk统计多个字符分别出现的次数---------------------------------------------
#校验
[ $# -lt 2 ]&&{ echo "Usage:$0 filename  char1,char2 ..."; exit ; }
[ ! -f $filename ] && { echo "$1 is not a file"; exit; }

#文件名
filename=$1 
#开始将字符转到第一个参数位置上
shift
initChars=""
#开始循环将每个字符连在一起
while ( test $# -gt 0 )
do
	initChars="$initChars""$1"
	shift
done
awk \
'BEGIN{ 
	chars="'$initChars'"  #shell变量的引用双引号+单引号
	split(chars,charArr,"") #将字符分隔到数组中
	for (x in  charArr){
		result[charArr[x]]=0 #以字符为索引初始化数量
	}
}
{
split($0,fileArr,"");  #将文件按单个字符存到数组中,这个只能放在执行块里,因为$0代表文件只在执行块中成立
for (i in fileArr){ #双层for循环,比对字符
	for (j in charArr){
		if (fileArr[i]==charArr[j]){
			result[charArr[j]]++;  #相等则结果自驾1
		}
	}
}
}
END{
	for (y in charArr){  #格式化打印结果
		print charArr[y] "==>"  result[charArr[y]]

	}
}'  $filename 

iii.统计总列数

awk 'BEGIN{ FS="'$char'" sum=0 filename="'$filename'" }
    { sum= sum+ NF }
    END{ print filename "==>"sum }' $filename
iv.统计最大列数
awk \
'BEGIN{	 FS="'$char'";  max=0 ; filename="'$filename'"} # BEGIN中的值放在一行时要用分号
{   if(NF>=max){
		max=NF
 	}
}
END {print filename "==>" max}' $filename

v.格式化打印ascii码值

START=33
END=125
printf "\tnumber\tHex\tCharacter\n"
printf "\t------\t---\t---------\n"
for ((i=$START;i<=$END;i++))  #这种for循环表示值从哪里开始到哪里结束 
do  #echo通过管道符给awk值
echo $i | awk '{printf ("\t%d\t%x\t%c\n",$1, $1, $1) }'  #printf的%c表示ascii值,其余参数参见printf函数,
done  

第十四章 shell sed和awk脚本练习
1.shell脚本练习
(1)统计ip的访问量

i.
file=/usr/local/nginx/logs/access.log
awk 'BEGIN{ 
          IPArr[""]=0 
}
{ 
                if(  $1 in IPArr ){         #这种方式判断某个key是否在数组中
                }else{
                        IPArr[$1]=0     
                }
        for ( y in IPArr ){
                if ( $1 == y ){
                        IPArr[$1]++;
                }
        }             
}
END{
        for ( i in IPArr ){
                if( i == "" ){
                }else{
                        print  i "====>"  IPArr[i]      
                }
        }
}'   $file

ii.第二种方式:
awk ‘{print $1}’ /usr/local/nginx/logs/access.log |sort | uniq -c #通过awk打印第一行,然后先排序,将相同结果放一块,最后排序去重,如果不放一起的话使用uniq -c 去重去不干净
结果:
13 127.0.0.1
265754 192.168.1.106
1512 192.168.1.111
1913 192.168.1.113
1 192.168.1.114
3906 192.168.43.125
(2)统计所有进程对内存总的使用情况

ps -aux | awk 'BEGIN{sum=0} { sum=sum+$6} END{print sum}'

(3)监控远端服务器是否正常连接

eg:while true 
do

        ping -c 1 192.168.1.102
        if [ $? -ne 0 ]
        then
                echo "============ping远端失败====================" `date "+%Y-%m-%d %H:%M:%S" `
                break 1
        fi
done  &>> ping.log

(4)变量的赋值等号两边一定不能有空格
eg: 对批量文件进行循环归档,

 for file in  $( ls )
        do
                if [[ $file =~ .*txt$ ]]
                then
                        echo "==============file:$file"
                        bak="$file".bak
                        echo "========================$bak"
                tar -cvf   "$bak".tar.gz  "$file"
                fi
        done

(5)打印shell 脚本的一种规范格式
eg:

#!/bin/bash
[ $# -ne 1 ]&&{ echo "Uage:$1 filename";exit; }
touch $1
chmod 777 $1
echo "
#------------Discription---------------------
# Filename:   $1
# Revision:   1.0
# Date:       $(date "+%Y-%m-%d %H:%M:%D")
# Author:     zhangshaoxuan
# Description:  
#--------------------------------------------


#---------------定义变量--------------------

--------------------------------------------">$1

(6)1-5行删除含有字母的行,剩下的行删除行中的字母
eg:

sed '1,5{/[a-zA-Z]/d}
1,5!{s/[a-zA-Z]//g} ' $1

ps:vim 中跳转到第一行和最后一行
底线模式:
第一行 :0
最后一行:$
命令模式
第一行:gg
最后一行:shift+g
(6)二次求差值找规律,注意expr不能求乘方运算

eg:
x=10;
m=21;
i=0
echo $x
 x=`expr $x + $m`
echo $x
for i in $(seq 0 14)
do
        let m=2**$i+$m
        x=`expr $x + $m `
        echo $x
done

(7)命令seq first step last 按照步长生成序列数

eg:
seq 0 14 #生成0-14的序列数,默认步长为1
seq 0 2 14 # 结果:0 2 4 6 8 10 12 14
seq 14 -1 0 #结果:逆序14-1
seq -s "," 0 14 #结果:s=string 即给定分隔符进行分隔,默认是\n 

(8)统计自定义用户个数
eg:

who|awk '$1!="root"{print $1}'|sort|uniq -c 
count=`who|awk '$1!="root"{print $1}'|sort|uniq -c|wc -l`  #多个管道符,每次左边一个的执行结果是右边一个的输入
echo "自定义用户个数是:"$count

(9)定时检查文件有没有新生成
eg:

newnumber=0
oldnumber=0
temp=/home/temp/temp.txt
while true
do
        filename="/home/temp/"`date -d "5 min ago" "+%Y-%m-%d_%H:%M:%S"`".txt"
        find ./ -type f >$temp
        newnumber=`cat < $temp | wc -l `
        if [ $newnumber -gt $oldnumber ]
        then
                oldnumber=$newnumber

                mv $temp  $filename
        fi
        sleep 20
done

(10)查找最近使用频率最高的命令
eg:

HISTFILE=/root/.bash_history  # 没法直接使用history命令,history是bash的内部命令在shell中被设置为默认无法使用,history的结果其实都来自.bash_history文件
sort $HISTFILE | awk '{print $1}'|uniq -c|sort -nr  

(11)shell中遍历展示文件的绝对路径 文件夹下面必须加*
eg:

for file in $(ls /home/temp/*)  #如果不加*,则只遍历出文件名,对文件操作时会报错
do
   echo $file  # 结果是打印文件的全路径
done

(12)cat循环一行一行输出文本内容时,要将IFS设置为换行符即 IFS=$’\n’
(13)cut命令的用法,求/etc/passwd中第10和第20行用户的id之和

sum=0
id1=`head -$1 $file | tail -n 1 |cut -d : -f 3`  #head求前10位,加tail求第10位,cut -d表示用:进行分割列,-f3表示取第三个,-d和-f必须连着用
id2=`head -$2 $file | tail -n 1 |cut -d : -f 3`
let sum=$id1+$id2
echo $sum

(14)grep和wc计算空白行,统计行总数要善用wc -l

eg:blank1=`grep "^$" $1|wc -l`  或者 awkawk '/^$/{print}' practice.sh |wc -l 

(15)匹配ip ,[[]]中可以直接多逻辑判断,{}在if的正则判断中不需要\转义
eg:

pattern="[0-5]\{3\}"
if [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]  #这里{}不用转义,但要加^$否则仍不能正确匹配
then
        filed1=`echo $1|cut -d. -f 1`  #在第一轮有限正则后,保证都是数字,然后cut 跟225比较
        filed2=`echo $1|cut -d. -f 2`
        filed3=`echo $1|cut -d. -f 3`
        filed4=`echo $1|cut -d. -f 4`
        if [[ $filed1 -gt 225 || $filed2 -gt 225 || $filed3 -gt 225 || $filed4 -gt 225 ]]
        then
                echo "$1 is not a ip"
        else
                ping -c 1 $1
                if [ $? = 0 ]
                then
                        echo "$1 is up";
                        exit;
                else
                        echo "$1 is unreachable"
                        exit;
                fi
        fi
else
echo "$1 is not suitable"
fi

(16)判断正整数,正则表达式匹配默认是存在匹配,只要存在就算匹配,要改成必须,则加上^$
eg:

[[ $1 =~ ^[1-9][0-9]+$]]  #匹配正整数

(17)匹配空格,+加号前面要转义,删除/etc/fstab⽂件中所有以#开头,后⾯⾄少跟⼀个空⽩字符的⾏的⾏⾸的# 和空⽩字符
eg:sed ‘s/^#[[:blank:]]+//g’ person.txt
(18)开头非#的行首增加#,&表示正则匹配到的位置
eg : sed ‘s/[#]/#&/g’ person.txt
(19)处理/home/person 分别展示路径和文件名,可以通过括号括起来,然后取不同位置的子表达式
eg:echo “/home/person”|sed ‘s@(/.+/)([^/]+)@\2@g’ \2表示取第二个子表达式的值
结果:person
echo “/home/person”|sed -r ‘s@(/.+/)([^/]+)/?@\1@g’ #加上-r 使用扩展正则,不用上面那么多转义
结果:/home/
(20)取ifconfig中本机ip
eg: ifconfig|awk ‘NR == 2 {print $2}’
(21)统计文件中每个单词出现的次数

eg: grep -o "\<[[:alpha:]]\+\>" /etc/init.d/functions |sort |uniq -c  # -o表示 只展示匹配到的内容, \<\>表示单词边界
eg: sed -r 's@[^[:alpha:]]@\n@g' /etc/init.d/functions | sort | uniq -c    #暂时没看懂\n是什么意思

(22)sed -n +p指令 相当于grep命令,展示的都是匹配的行,如果想只展示匹配到的字符串,或者字符串的一部分,则使用s进行替换

 sed -n '/^J/p' datebook
grep '^J' datebook 两个结果相同
sed -rn '/^[CE]/s/(.+):(.+:){3}.+/\1/gp' datebook #、\1表示取第一个子表达式

(23)不出现843的行,sed命令如果不加-i的话,本质上都可以当成是查询的命令如d删除来查询不包括某个字符串的行

eg: grep -v '834' datebook
eg:  sed '/834/d' datebook

(24)\n表示对子表达式的应用

eg:grep  '1[1-2]\(\/[0-9]\{1,2\}\)\1' datebook #\1表示对第一个子表达式的应用
eg:sed -nr  '/1[1-2](\/[0-9]{1,2})\1/p' datebook   #\1表示对子表达式的应用
#------------------------替换英文的名和姓的位置-------
eg: sed -r 's/^(\<[[:alpha:]]+\>)([^[:alpha:]]+)(\<[[:alpha:]]+\>)/\3\2\1/g' #单词边界不管是不是扩展的regexp都加\
#对于非字母的匹配使用 [^[:alpha:]],\3\2\1分别表示子表达式
sed  '/500$/d'  datebook 
sed  '$aTHE END'  datebook  # 不是正则的话,a指令直接加后面,不用// 

(25)sed中gp和-n的使用,纯打印匹配的内容

eg:------找出以Dan开头的人的电话
cat datebook
Jody Savage:(206) 548-1278:15:188:150 
Guy Quigley:(916) 343-6410:250:100:175 
Dan Savage:(406) 298-7744:450:300:275  
sed -n -r '/Dan/.+:(.+)(:.+){3}/\1/gp'  -p表示打印一次模式空间中替换后的内容,即replacement,-n表示取消输出所有文本
结果:(406) 298-7744:450:300:275  
如果不加 -n和p结果为:
Jody Savage:(206) 548-1278:15:188:150 
Guy Quigley:(916) 343-6410:250:100:175 
(406) 298-7744  替换后的整个文本会输出

(26)awk指定多个分隔符-F [: ]

eg:awk -F '[: ]' '{print $2 "," $1}' datebook

(27)awk列相加

eg:awk -F ":" '($3+$4+$5)<=800{print $1}' datebook
eg:awk -F ":" '{if(($3+$4+$5)<800){print $1,$2}}' datebook

(28) 逻辑非的使用,区号不在916的人

eg:awk -F ":" '$2 !~ /(916)/ {print $1 ":" $2}' datebook
awk -F "[: ]" '$3 !="(916)" {print $1  $2  $3}' datebook

(29) grep+cut也能实现awk分列的操作,如上题可以用grep

eg:grep -v '\(916\)'  datebook | cut -d: -f 1,2

(30)输出每行的行号的几种方式
i. awk的NR,sed的=,nl,cat -n

eg:awk '{print NR" :" $0}' datebook 
eg:  sed -n '/Jody/{=;p}' datebook  #sed多指令打印匹配行的行号和内容
eg: cat -n datebook  #展示包括空行在内的所有行的行号

ii.nl的主要参数说明
eg:

nl datebook #默认输出不包括空格的行号
...  
	89	[ $# -ne 2 ]&&{ echo "Usage:$0 file1 file2";exit;}
       
    90	blank1=`grep "^$" $1|wc -l`
    91	blank2=`grep "^$" $2|wc -l`
...
nl -b a datebook  #跟cat -n是一样的,空格也算行数
nl -n t datebook #空行不算,默认值
-n ln:行号在萤幕的最左方显示;
nl -b a -n ln datebook 
结果:
1     	Mike Harrington:(510) 548-1278:250:100:175
2     	Christian Dobbins:(408) 538-2358:155:90:201
-n rn:行号在自己栏位的最右方显示,且不加 0nl -b a -n rn datebook 
     1	Mike Harrington:(510) 548-1278:250:100:175
     2	Christian Dobbins:(408) 538-2358:155:90:201

-n rz:行号在自己栏位的最右方显示,且加 0 
nl -b a -n rz datebook 
000001	Mike Harrington:(510) 548-1278:250:100:175
000002	Christian Dobbins:(408) 538-2358:155:90:201

(31)打印捐款总数,awk执行语句中可以直接做运算

eg:awk -F ":" '{print $1 ": "$3+$4+$5}' datebook 

(32)改列的内容,重新给列赋值即可

eg:awk -F '[: ]' '$1=="Nancy" && $2=="McNeil"{$1="Louise";$2="McInnes";print $1,$2 }' lab4.data

(33)格式化打印,awk执行语句中可以使用三目表达式

eg:
echo "-----------------------------------------------------------------------"
echo "NAME              PAHONE          Jan|    Feb|    Mar|    Total Donated"
echo "-----------------------------------------------------------------------"
awk -F ":" '{print $1"  "$2"    "$3"    "$4"    "$5"    "$3+$4+$5}' $f
echo "------------------------------------------------------------------------"
echo "  SUMMARY"
echo "------------------------------------------------------------------------"
awk -F ":" 'BEGIN{sum=0;avg=0;min=100;max=1;count=0}
{
sum=sum+$3+$4+$5;
count++;
min=min>$3?$3:min  #这里的if(){}可以直接三目表达式
min=min>$4?$4:min
min=min>$5?$5:min

max=max>$3?max:$3
max=max>$4?max:$4
max=max>$5?max:$5
}
END{
avg=sum/count
print "The campaign received a total of " sum  "for this quarter."
print "The average donation for the 12 contribuors was $" avg
print "The highest contribution was $" max
print "The lowest contribution was $" min
}' $f

第十五章 其他

  1. vim下查看文件的字符编码集
:set fileencoding
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值