daily/Shell.md at master · johQin/daily · GitHub

您所在的位置:网站首页 linux查看关键字前后 daily/Shell.md at master · johQin/daily · GitHub

daily/Shell.md at master · johQin/daily · GitHub

2023-03-08 12:55| 来源: 网络整理| 查看: 265

Shell 0 shell 初识 0.1 shell功能

0.2 脚本初识 0.2.1 hello bash

注意:

命令的执行从上而下,从左而右。 命令、参数间的多个空白,空白行会被忽略,tab会视为空白 读到enter就会执行,"\[enter]"让一条命令扩展至下一行 #用来做批注 1 第一个script #!/bin/bash # Program: # This program shows ... # History: # 2005/08/23 PATH=/bin:/sbin:/usr/sbin/:/usr/local/sbin export PATH echo -e "Hello World! \a \n" exit 0 #!/bin/bash:第一行是声明这个script使用的shell名称(shebang机制) 通过这一句,当这个程序被执行时,它就能够加载bash的相关配置环境配置文件 PATH: 主要环境变量的声明 这里相当于一般程序里的import的功能 建议务必要将一些重要的环境变量设置好(设置PATH与LANG等),如此一来,则可让我们这个程序在进行时直接执行一些外部命令,而不必写绝对路径。 echo 主要程序部分 exit 告知执行结果。 讨论一个命令执行成功与否,可以使用**$?**这个变量来看看。 exit n相当于一个return flag,我们可以通过查看$?这个flag来查看程序的执行情况如何 系统自带的命令返回0代表执行成功,1代表失败 #!/usr/bin/bash ping -c1 www.baidu.com &> /dev/null && echo "baidu connected" || echo "baidu disconnected" echo $? # #!/usr/bin/bash, #与!之间没有空格,这个叫shebang机制,程序的第一行,也仅第一行。声明这个脚本程序默认用哪个解释器执行, # 如果通过 bash ./ping01.sh 执行,则就显式通过 bash 这个解释器执行,如果通过 ./ping01.sh 执行,则shebang机制就会生效,默认通过 #! 声明的解释器执行 # 显式指定的级别大于shebang声明,如果没有显式指定,那么就会按shebang声明的解释器执行 # &>,数据流重定向,将本该出现在屏幕上得输出内容,重定向到其他文件得内容中去,在这里将屏幕内容重定向到垃圾黑洞设备/dev/null,而不会在屏幕打印 # && 逻辑与,前一条命令执行成功,则与的命令紧接着开始执行,如失败则不执行 # || 逻辑或,前面的命令执行成功,则不执行后面语句,如失败,则执行后面的语句 # $? 的值表示最近一条命令是否成功,0:success,非零:fail 0.2.2 bash中临时执行其他语言脚本

通过重定向,标准输入,然后执行其他语言脚本

echo "hello bash" /usr/bin/python /~/.bash_login > /~/.profile,前面的文件如存在,就不会再读后面的文件 /~/.bash_profile会再调取/~/.bashrc。所以我们可以在此将自己的偏好设置写入该文件(/~/.bashrc)

login_shell的配置文件读取流程

手动更新配置:source

source 配置文件

其他相关配置文件

/etc/man.config:执行man时man page的路径到哪里去找 ~/.bash_history:历史命令 ~/.bash_logout:当我注销bash后系统再帮我做完什么操作才离开 1.2.4 终端机快捷键

快捷键设置

查看终端机**"特殊功能"**按键列表:stty -a,stty(setting tty)

输出的**^**代表【Ctrl】的意思,其他的eof,erase,intr等自行查阅便知

按键修改,eg:stty erase ^h

修改终端机设置值,set [ -uvCHhmBx ]

常用快捷键

组合键 意义 Ctrl + C 终止当前输入的命令,终止运行的程序 Ctrl + D 退出bash子进程,or 输入结束(eof),从光标删右方一个字符 Ctrl + Z j将运行的程序送到后台 Ctrl + L q清屏 Ctrl + R s搜索历史命令,搜索到回车即可执行 Ctrl + U or K c从光标位删至行首or行尾 Ctrl + S or Q 暂停or 恢复屏幕输出 1.2.5 通配符与特殊符号 通配符 意义 ***** 0到多个任意字符 ? 至少一个任意字符 [ ] 字符集,eg:[abceft] [ - ] 字符段,eg:[a-z] [ ^ ] 取非,eg:[^a-z],非小写字母 特殊符号 意义 # 批注 \ 转义 | 管道符 ; 命令分割符 ~ 用户主文件 $ 变量前导符 & 作业控制 ! 非 / 目录符号,路径分割符 >,>> 数据流重定向,输出导向 (追加内容) ####标准输出#### #屏幕不输出任何信息,~下有一个mine文件创建。 ls -al / > ~/mine #若~下mine文件不存在,则创建 #若存在,则清空文件,在写新数据 # >> 追加数据到原文件 #将stdout写入list_right文件,将stderr写入list_error find /home -name .bashrc > list_right 2> list_error #stdout与stderr写入同一个文件 find /home -name .bashrc > list 2>&1 # 混合重定向 &> find /home -name .bashrc &> list #垃圾黑洞设备/dev/null find /home -name .bashrc 2> /dev/null ####标准输入#### cat > introduce catfile 追加内容),2——错误输出(2> 覆盖内容,2>>追加内容) &>——将正确输出和错误输出都重定向到文件 tee 双向重定向

管道命令

command1 | command2

命令排序

单行执行多个命令 ;——不具备逻辑判断能力,每个命令都会执行,不论是否报错 && 逻辑与 ||逻辑或——具备判断能力, 逻辑与&&,当逻辑符号左侧的命令执行成功才会执行逻辑符号右侧的命令 逻辑或||,当逻辑符号左侧的命令执行失败才会执行逻辑符号右侧的命令

通配符

# * 匹配任意多个字符 ls ha* ls *tx* rm -rf *.pdf rm -rf * ll /dev/sd[a-z]* # ? 匹配任意一个字符 touch love loove live l7ve; ll l?ve -rw-r--r-- 1 root root 0 Oct 3 15:24 l7ve -rw-r--r-- 1 root root 0 Oct 3 15:24 live -rw-r--r-- 1 root root 0 Oct 3 15:24 love # rm -rf l*ve # [] 字符集,匹配中括号中任意一个字符 [a-zA-Z0-9] [^a-z],尖角开头取非 ll l[io]ve -rw-r--r-- 1 root root 0 Oct 3 15:28 live -rw-r--r-- 1 root root 0 Oct 3 15:28 love ll l[^a-Z]ve -rw-r--r-- 1 root root 0 Oct 3 15:28 l7ve # (cmd) 在子shell中执行cmd # {} 枚举值 mkdir -pv ./{111/{aaa,bbb},222} || ls mkdir: created directory './111' mkdir: created directory './111/aaa' mkdir: created directory './111/bbb' 111 222 touch {a..c}{1..3} || ls a1 a2 a3 b1 b2 b3 c1 c2 c3 cp -rv /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.old # 可以写成下面的形式 cp -rv /etc/sysconfig/network-scripts/{ifcfg-eth0,ifcfg-eth0.old} cp -rv /etc/sysconfig/network-scripts/ifcfg-eth0{,old} # \ 转义字符 touch qin\ fei # 就创建了一个带有空格的文件 # 而并没有创建两个文件 # 转义回车符,使命令可以多行书写 ls /etc/sysconfig/network \ >/etc/hosts \ >/etc/paaswd echo \\ \ echo "atb" # -e可以输出转义后的字符 echo -e "a\tb" echo -e "a\nb"

echo 打印颜色文本。printf指令也可以格式化输出

# 31m是指代文本的颜色,31m是红色,32m是绿色,33m是黄色。4开头的是修改文本的背景色。 # 末尾一定要\e[0m,用于重置文本颜色,否则以后所有打印都是这个颜色 echo -e "\e[1;31mthis is a red text.\e[0m" 2 shell变量

变量的类别:自定义变量和环境变量

2.1 变量 2.1.1 显示与设置

set——查看所有变量(包含环境变量与自定义变量)

变量的引用:$var_name

echo $变量名 echo ${变量名},常用于数字参数的变量,以及对变量进行删除替换等操作时(相当于计算变量) echo 功能有很多,这里只是用到了查看变量的功能 列出当前环境变量)——env 列出所有变量(包含环境变量与自定义变量)——set

变量的赋值

显式赋值,格式:var_name=var_val

#1.常量赋值 ip1=192.168.4.25 address="beijing tiananmen" #2.变量间赋值 var1=${ip1} var2="ip is ${var1}" var3="ip is $var2" #3.命令替换,先执行命令,再赋值,反引号``等价于$() # 反引号的命令替换 today=`date +%F` echo $today # 打印2022-01-14 touch `date +%F`_file.txt # $()的命令替换 today1=$(date +%F) echo $today1 # 打印2022-01-14

键盘读入:read

read [ -ptn ] var_name p,后面接输入提示文本 t,输入等待时间,记住输入后一定要按回车,否则时间结束后依旧不能声明变量 n,只获取输入的n个字符 read -p "请输入你需要备份的文件名: " back_file read ip1 ip2 ip3 #输入 1.1.1.1 2.2.2.2 3.3.3.3,空格相隔,可以一次赋多个值 #!/usr/bin/bash read -p "请输入姓名,性别,年龄【eg:qqq m 25】:" name sex age echo "你输入的姓名:$name,性别:$sex,年龄:$age"

规则:

等号两边不能直接接空格,变量命名只能是英文和数字(数字不能开头)

引用变量:$var_name 或${var_name},${var_name}也用在拼接字符串(解决歧义),变量的右侧有其他字符时。

变量的值若有空格,可用双引号或单引号将值罩起来。

双引号可以解析值中的变量(占位符),而单引号不行

qi=kh mine="my name is $qi" echo $mine # my name is kh mine='my name is $qi' #重新赋值 echo $mine # my name is $qi

特殊符号用反斜杠\转义

追加变量内容

PATH="$PATH":/home/bin

环境变量(全局变量):export var_name

取消变量unset

unset var_name 2.1.2 变量运算 1 整数运算 # 1 expr, # 加减乘除取余,+ - \* / % ,其中乘号需要转义,因为会误认为*为通配符 expr 1 + 3 # 输出 4 num1=10 num2=20 expr $num1 + $num2 # 输出 30 sum=`expr $num1 + $num2` echo $sum #30 # 乘 expr $num1 \* $num2 # 输出 200 # 2 $(()) echo $(($num1 * $num2)) echo $((num1 * num2)) # 括号里面可以不用变量引用符 echo $((5-3*2)) echo $(((3+2)*5)) echo $((2**10)) sum=$((num1 + num2)) # 3 $[] echo $[2+2] echo $[2**10] num1=$[2**10] echo $num1 # 4 let # 运算间不能加空格,必须写一起 let num3=1+2 echo $num3 no=0 let no++ # 自加1操作 let no-- # 自减1操作 let no+=10 # no=$no+10 let no-=20 # no=$no-20 #!/usr/bin/bash ip=127.0.0.1 i=1 while [$i -le 5] do ping -c1 $ip &>dev/null if [$? -eq 0];then echo "$ip is up..." fi let i++ done 2 小数运算

bash 不支持浮点运算,如果需要进行浮点运算,需要借助bc,awk 处理。

有内建的bash计算器,称作bc。在shell提示符下通过bc命令访问bash计算器。

echo "scale=2;6/4" | bc awk 'BEGIN{print 1/2}' # bc命令能识别输入重定向, # EOF文件字符串标识了内联重定向数据的开始和结尾。记住仍然需要反引号来将bc命令的输出赋给变量。 #!/bin/bash var1=10.46 var2=43.67 var3=33.2 var4=71 var5=`bc /dev/null; then # 上面一行等价于下面两行 # id $user &>/dev/null # if [ $? -eq 0]; then echo "用户已存在" else useradd $user if [ $? -eq 0]; then echo "$user 已创建成功" fi fi '>read -p "请输入用户名: " user if id $user &>/dev/null; then # 上面一行等价于下面两行 # id $user &>/dev/null # if [ $? -eq 0]; then echo "用户已存在" else useradd $user if [ $? -eq 0]; then echo "$user 已创建成功" fi fi 3.2.2 case语句

case语句只能做字符串比较,不能做大小比较

case $var in "state1") 程序段1 ;; "state2") 程序段2 ;; "state3" | "state4") 程序段2 ;; *)#用*表示其他状态 程序段n ;; esac #!/usr/bin/bash read -p 'please input username: ' $user if [ ! -z "$user" ]; then echo "input error" exit fi id $user &>/dev/null if [ "$?" -ne 0 ];then echo "$user is unexited" exit fi read -p "are you sure? [y/n]" action # if语句的写法 # if [ "$action" = "y" -o "$action" = "Y" -o "$action" = "yes" -o "$action" = "YES" ]; then # userdel -r $user # echo "$user is deleted successfully" # else # echo "action is canceled" # fi # case语句的写法 case $action in "y" | "Y" | "yes" | "YES") userdel -r $user echo "$user is deleted successfully" ;; *) echo "action is canceled" ;; esac 3.3.3 练习 拼指定主机测试, 判断用户是否存在 判断当前内核版本是否为3,且次版本是否大于10 uname -r 判断vsftpd软件包是否安装,如果没有安装则自动安装 rpm -q vsftpd 判断httpd是否运行 报警脚本 根分区剩余空间小于20% 内存已使用空间大于80% 想用户alice发送邮件 配合例行性任务crond每5分钟执行一次 3.3 循环结构 3.3.1 for循环 #foreach循环 # for 循环默认按照空格来分割序列,除非修改了环境变量IFS, for var in constant1 constant2 constant3 do 程序段$var可以获取constant列表的内容 done #for循环 s=0 for ((i=0; i# 1.序列循环 #!/usr/bin/bash # 这里有两种方式创建一个序列,一个是{start..end},一个是seq start end for i in {2..254} # 不支持动态变量的改变序列长度 # for i in `seq 2 254` # seq -w 100 w参数可以等位补齐,例如:001,002...100 do { ip="10.80.5.$i" ping -c1 -W1 $ip &>/dev/null if [ $? -eq 0 ]; then echo "$ip" | tee -a ip_up.txt echo "is up" else echo "$ip is down" fi }& # 将花括号里面的命令丢到后台执行 done wait # 等待所有后台进程结束再执行后面的的命令 echo "finished" # 2.读文件,然后循环 # 不推荐用for来处理文件,因为for默认按照空格来分割序列,需要修改IFS for ip in `cat ip.txt` do # echo $ip ping -c1 $ip &> /dev/null if [ $? -eq 0 ];then echo "${ip} is up." else echo "${ip} is down." fi done # 3. 通过文件,批量创建用户,用户名和密码都在文件中 # 需要改分割符IFS # 判断参数个数 if [ $# -eq 0 ];then echo "must input file parameters" fi # 判断文件是否存在 if [ ! -f $1 ]; then echo "file:$1 is unexisted" fi # 我们希望文件内容按回车分割,而不是按空格或tab分割 # 这里需要重新设置IFS,使用完毕后需要还原 IFSTemp=$IFS IFS=$'\n' # user.txt 内容 # abc 145566 # ttt 123456 # dfd 145987 for line in `cat user.txt` do # 如果是空行,跳过 if [ ${#line} -eq 0 ]; then continue fi # 分割每行内容 user=`echo "$line" | awk '{print $1}'` pass=`echo "$line" | awk '{print $2}'` id $user &>/dev/null if [ $? -eq 0 ]; then echo "$user already exists" else useradd $user echo "$pass" | passwd --stdin $user &>/dev/null if [ $? -eq 0 ]; then echo "$user created successfully" fi fi done IFS=$IFSTemp # 如果这里不要in以及后面的序列,那么它默认会获取整个脚本(或函数)的参数列表 for i do let sum+=i done 3.3.2 while循环

条件为真,执行循环。

for循环需要读文件时需要修改分割符(默认空格和tab),而while没有这个问题,while默认换行符。

逐行处理文件,请优先考虑while循环,而不是for。

#while循环 while [ condition ] do 程序段 done i=1 sum=0 while [ $i -le 100] do let sum+=$i # 等价于 # let sum=$sum + $i let i++ done printf "sum=${sum}" # 通过文件批量创建用户 while read line do # 不需要做空行判断,因为read 空行会得到1的返回值 # 分割每行内容 user=`echo "$line" | awk '{print $1}'` pass=`echo "$line" | awk '{print $2}'` id $user &>/dev/null if [ $? -eq 0 ]; then echo "$user already exists" else useradd $user echo "$pass" | passwd --stdin $user &>/dev/null if [ $? -eq 0 ]; then echo "$user created successfully" fi fi done # 如果网络环境正常,就一直send,如果网络环境异常,就跳出循环、 ip=10.80.5.25 while ping -c1 -w1 $ip &>/dev/null do # send message sleep 1 done echo "$ip is down" 3.3.3 util循环

条件为假,执行循环。条件为真跳出循环,和while正好相反

#do循环 until [ condition ] do 程序段 done # 如果网络环境异常,就一直send,如果网络环境正常,就跳出循环、 ip=10.80.5.25 util ping -c1 -w1 $ip &>/dev/null do # send message sleep 1 done echo "$ip is up" 3.3.4 循环汇总 for i in {2..254} do { ip="10.80.5.$i" ping -c1 -w1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip is up." fi }& done wait echo "all finished" j=2 while [ $j -le 254 ] do { ip="10.80.5.$j" ping -c1 -w1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip is up." fi }& let i++ # 这里不能扔到异步(后台)去自加 done wait echo "all finished" k=2 util [ $k -gt 254 ] do { ip="10.80.5.$k" ping -c1 -w1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip is up." fi }& let k++ done wait echo "all finished" 3.3.5 并发控制(控制并发数量)

在上面的循环程序中,每执行一次循环体(后台执行),就会开启一个新的子进程,当循环体被执行了1000次,如果循环体内的执行过程较长,极有可能就会累积1000个子进程。

就像下面这个批量后台创建1000个用户

for i in `seq -w 1000` do { user="u$i" useradd user echo 123456 | passwd --stdin $user &>/dev/null if [ $? -eq 0 ]; then echo "$user is created successfully" fi }& done

并发控制(控制并发数量),需要一下两个基础知识点:

文件描述符(文件句柄) 查看当前进程中,打开的文件描述符列表:ll /proc/$$/fd,fd——file description,$$——表示当前进程 打开file1文件并且指定文件描述符序号:exec 6 /file1 关闭file1文件并且释放文件描述符:exec 6>&- 其实我们修改文件就是在修改文件描述符 当一个文件FD未被释放,删除源文件也不会影响FD,并且还可以通过fd恢复源文件 匿名管道和命名管道 管道对应的就是一个管道文件 匿名管道,就像一般的管道命令,这个文件没有名字,暗送,eg:rpm -qa | grep bash 匿名管道是由pipe函数创建 并打开的 在同一个终端上 命名管道,通过创建具体/path/file1文件来实现,eg:mkfifo /tmp/tmpfifo, 命名管道是由mkfifo函数创建 的 可以实现不同终端之间的通信,一个端子向fifo文件里写数据,一个端子从fifo文件读数据。 管道里的数据,一旦看了或用了就没了, 并且是fifo(first input first output) 并发控制实例 >(追加)",因为管道文件是无法覆盖的,写一个就是一个,是无法覆盖的 done for i in `seq 100` do read -u 8 # -u 指定文件描述符 # read必须读到内容,否则将会停滞在这里,而管道文件的内容读一个字符,少一个字符,所以每次最多只能有5个进程 { ip="10.80.5.${i}" ping -c1 -w1 $ip if [ $? -eq 0 ];then echo "$user is up" else echo "$user is down" fi echo >&8 # 每次进程结束,需要往管道文件里,加一个0a字符,相当于释放一个进程 }& done wait # 释放文件描述符 exec 8>&- echo "all finished"">thread_num=5 tmpfifo='/tmp/$$.fifo' mkfifo $tempfifo # 创建一个fifo管道文件 exec 8 $tempfifo # 指定描述符序号为8 rm $tempfifo # 删除文件不会影响文件描述符 for j in `seq $thread_num` do echo >&8 # echo 每次写入一个0a字符(换行符,不是没有内容哈),给文件描述符8,总共写了5次,文件里面有5个字符 # 还有这里的重定向符“>(覆盖)”,为什么不是">>(追加)",因为管道文件是无法覆盖的,写一个就是一个,是无法覆盖的 done for i in `seq 100` do read -u 8 # -u 指定文件描述符 # read必须读到内容,否则将会停滞在这里,而管道文件的内容读一个字符,少一个字符,所以每次最多只能有5个进程 { ip="10.80.5.${i}" ping -c1 -w1 $ip if [ $? -eq 0 ];then echo "$user is up" else echo "$user is down" fi echo >&8 # 每次进程结束,需要往管道文件里,加一个0a字符,相当于释放一个进程 }& done wait # 释放文件描述符 exec 8>&- echo "all finished" 3.3.6 shell内置命令

冒号**:**:空语句,永真,占位符

break:结束当前循环,跳出本层循环

continue:忽略本次循环剩余代码,直接进行下一次循环

shift:使位置参数(函数和脚本通用)向左移动,默认移动一位,shift 2就移两位

while [ $# -ne 0 ] do useradd $1 # 添加用户,./script.sh alice qqq kkk hhh # let sum+=$1 # 所有参数求和,./script.sh 1 2 3 4 shift 1 # 执行后,命令后面的第一参数会被踢出参数队列,而执行前的第二个参数被推向了参数队列的首位 done 4 数组

普通数组:只能用整数作为数组索引

关联数组:可以使用字符串作为数组索引,类似于java的类

4.1 数组定义 # 1.数组声明 # shell默认能识别普通数组,而不能识别关联数组 declare -aA [数组名] # a,声明普通数组 # A,声明关联数组,在定义关联数组之前,一定要声明,否则引用数组元素将会出错 # 如果没有数组名,将会打印出当前环境下的所有当前类型的数组 declare -a # 查看当前环境,所有的普通数组 declare -A # 查看当前环境,所有的关联数组 # 2.普通数组定义 # 一次赋一个值 fruit[0]=apple fruit[1]=pear # 一次赋多个值 books=(linux shell awk openstack docker) arr1=(`cat /etc/passwd`) # 期望该文件的每一行作为一个数组元素,但记住默认是以空格为分割对象,需要修改IFS arr2=(`ls /var/ftp/shell/for*`) arr3=(qqq kkk "hhh ni") # 数组只有三个元素 arr4=($red $blue $orange) arr5=(1 2 3 "linux shell" [20]=puppet) # 可以跳过中间索引值 # 3.关联数组定义 # 先声明 declare -A info1 # 一次赋一个值 sex=([m]=1) sex+=([f]=1) # echo ${!sex[@]} m f # 一次赋多个值 info1=([name]=qqq [sex]=male [age]=30 [height]=170) # 4.访问数组 echo ${fruit[0]} # 访问数组第一个元素 echo ${fruit[@]} # 访问数组所有元素,等价于echo ${fruit[*]} echo ${#fruit[@]} # 统计数组元素个数 echo ${!fruit[@]} # 获取所有数组元素的索引(包括关联数组的索引) echo ${fruit[@]:1} # 从数组索引1开始切片,关联数组不能切片 echo ${fruit[@]:1:2} # 从数组索引1开始切2个数组元素 4.2 数组赋值与遍历 #!/usr/bin/bash while read line do hosts[i++]=$line done //格式 affixPattern(? \( \) \{ \}

在shell中元字符需要加**\**才能起作用

词首()定位符,使用时\< \> 子表达式( expression ),使用时\( expression \) 重复限定符{ m,n },使用时\{ m,n \} + |,在使用时\+ \|

shell扩展元字符:+ ? | () {}

6.3 grep族

对行做操作,在文件或指定输入(管道,标准输入)中查找指定的正则表达式,并打印所有包含该表达式的行。

grep, egrep,扩展的grep,支持更多的元字符,推荐使用egrep fgrep,fixed grep,按字面意思解释所有字符

grep [选项] "pattern" file1 file2...

pattern,记得要加引号,因为如果pattern中包含空格,那么空格后的内容,容易被当做file名 返回值$? 0,可以找到包含匹配项的行 1,不可以找到包含匹配项的行 2,找不到指定的文件 grep 能使用基本元字符,如果要使用扩展元字符:grep -E,推荐使用egrep grep选项 -i,--ignore-case,忽略大小写 -q,--quit,--silent,打印到屏幕 -v,--invert-match,反向匹配,只显示不匹配的行 -R,-r,--recursive,针对目录,递归查找 --color,centos7开始默认支持对匹配项加颜色标注 -o,只返回匹配的内容,而不返回整行 -B,--before-context,eg:grep -B2,会连带显示匹配行的前两行 -A,--after-context,-A2,会连带显示匹配行的前2行 -C,--context,-C2,会连带显示匹配行的前后两行 -l,只列出含有匹配内容的文件名 -n,--line-number,连带显示匹配行的行号 # 如果想要查找某个命令帮助内容中某个选项的相关内容, #grep 后面的-u不能直接写,而需要一个\去转义-,否则它会认为-u是grep的选项,而不是pattern useradd --help | grep '\-u' # 简单的ip地址匹配 ([0-9]{1,3}\.){3}[0-9]{1,3} ([0-9]{1,3}).\1.\1.\1 # 涉及捕获组命名规则 6.4 流编辑器sed

vim是一种交互式的编辑器,而sed是一种在线的,非交互式的编辑器。

它一次处理一行内容。

处理时,把当前处理的行存储在临时缓存区中,称为“模式空间”

接着,用sed命令处理缓存区中的内容,处理完成后,把缓存区的内容送往stdout。接着处理下一行,直到文件末尾。

该操作不会改变源文件的实际内容,除非将输出内容重定向到其他文件中进行存储。

sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,编写转化程序等。

sed 命令格式

命令的方式:sed [option] 'command' file(s) ,这里的命令是sed特有的命令,后面会说到 脚本的方式:sed [option] -f scipts_file file(s)

sed命令只有在有语法错误的时候返回非0,其余状态都返回0,所以在sed里,通常不把$?作为命令是否执行成功的依据。

sed同grep'一样支持正则表达式,默认情况下,可以使用基本元字符,pattern需要写在双斜线(可以替换为其他字符eg:#@$,如果在查找模式下,需要将上述的第一个进行转义,如果是在替换模式可以不需要转义)之间。

6.3.1 sed选项 -r,支持使用扩展元字符 -n,--quit,--silent -f i,in place,就地编辑,会修改源文件 e,多重编辑,顺承编辑操作,将文件做了一次sed之后(所有行都操作过了),在此基础上,再做一次sed # s命令,substitute替换,将第一个正则/root/匹配到的内容,替换成alice sed -r 's/root/alice/' /etc/passwd sed -r 's@root@alice@' /etc/passwd #正则表达式双斜杠符号可以直接替换为@ # d命令,delete删除,删除含有匹配内容的行 sed -r '/root/d' /etc/passwd sed -r '\@root@d' /etc/passwd #在查找模式中,正则表达式双斜杠符号必须在第一个斜杠时转义,然后可替换为@ sed -e '1,3d' -e 's/home/admin/' /etc/passwd # 第一个-e,将第一行到第三行删除 # 得到没有1,2,3行的内容后 # 再进行第二个-e的替换操作 # 等价于 sed -e '1,3d;s/home/admin/' /etc/passwd 6.4.2 定址

地址用于决定对那些行进行编辑处理

地址的形式可以是数字,正则表达式或二者的结合

如果没有指定地址,sed将会处理文件中的所有行

格式:[address]command

# 数字定址 sed -r '3d' /etc/passwd # 删除第三行 sed -r '3,5d' /etc/passwd # 删除第三行到第五行 sed -r '3,$d' /etc/passwd # 第三行到最后一行 # 正则定址,adress——[/root/] sed -r '/root/d' /etc/passwd sed -r '/^#/d' /file.conf # 删除配置文件中的#注释行 # 混合定址,adress——[/root/,5],command——d sed -r '/root/,5d' /etc/passwd # 删除从root行开始,到第五行 # 特殊符号定址 sed -r '/^bin/,+5d' /etc/passwd # 删除从/^bin/开始,再删后面5行 sed -r '/root/!d' /etc/passwd # 反向删除,除了匹配行,其余都删 sed -r '1~2d' /etc/passwd # 删除所有奇数行 sed -r '0~2d' /etc/passwd # 删除所有偶数行 sed -r 's/(.*)/#\1/' /etc/passwd # 在所有行前加#,首先匹配所有行(.*),然后替换成#\1,\1是匹配的内容 #等价于 sed -r 's/.*/#&/' /etc/passwd # &在替换的内容里面表示前面匹配的内容,和\1是一致的 6.4.3 sed命令 命令组合

当多个命令都作用于同一地址行,就可使用:

address{command1;command2;...commandn}

sed -r '3{d;h}' /etc/passwd

和多重编辑不同,这里的两个命令都针对于第三行,而不是多重编辑的两次sed操作。

暂存空间

在模式空间之外还有一个暂存空间(保持空间),用于腾挪内容之用

模式空间的定址行可以存到暂存空间,在需要的时候再将暂存空间的内容放到模式空间,两个空间的内容可以交互。

暂存空间初始时有一个空行

命令解析 # d,delete,删除定址的行 # s,substitute,替换 sed -r 's/root/alice/g' /etc/passwd #g一行中所有匹配到的内容都需要替换 sed -r 's/root/alice/gi' /etc/passwd #i忽略大小写 sed -r 's/(.)(.)(.*)/\1\2yyy\3/' /etc/passwd # 每行的第二个字母后加yyy sed -r 's@root@alice@' /etc/passwd # 格式分割符/可以直接替换为@ # r,读入指定的文件内容到定址行后面,指定的文件与r间可以没有空格,这里有空格只是为了阅读更友好 sed -r '/lp/r /etc/hosts' /etc/passwd # 将/etc/hosts文件的内容,读入到定址行的后面 # w, 将定址行的内容写入到其他文件中 sed -r '/lp/w /tmp/1.txt' /etc/passwd # a,在定址行后面插入指定内容 sed -r '2a\1111' /etc/passwd sed -r '2a\1111 2222\ 3333' /etc/passwd # 插入多行内容 # i,在定址行前面插入指定内容 # c,将定址行(整行)替换为指定内容 # n,操作定址行的下一行,通常配合组合命令使用 sed -r '/adm/{n;s/sbin/uuu}' /etc/passwd # 定址行为包含/adm/内容的行,n——它的下一行,做替换操作 sed -r '/adm/{n;n;n;s/sbin/uuu}' /etc/passwd # n可以用多次,下下下一行 # h,H和g,G 暂存和取用命令 # h,把模式空间里的内容复制后,覆盖暂存空间当前的内容(覆盖的方式) # H,把模式空间里的内容复制后,追加到暂存空间当前内容的后面(追加的方式) # g,把暂存空间的内容复制后,覆盖模式空间当前的内容 # G,把暂存空间的内容复制后,追加到模式空间当前的内容的后面 sed -r '1h;$G' /etc/passwd # 复制第一行内容,覆盖暂存空间(初始的空行被覆盖掉); # 再匹配到最后一行($)内容的时候,再将暂存空间的内容(原第一行的内容)追加的最后一行的后面 sed -r '1{h;d};$G' /etc/passwd # 剪切第一行内容,并追加到最后一行之后 sed -r '1h;2,$g' /etc/passwd # 将第二行到最后一行,都覆盖为第一行的内容 sed -r '1h;2,3H;$G' /etc/passwd # 将1,2,3行的内容追加到最后一行之后 # x,交换暂存空间与模式空间的内容 sed -r '4h;5x;6G' /etc/passwd #...4465... # !,反向选择 sed -r '3!d' /etc/passwd # 除了第三行以外的内容都删除 sed中使用变量 # 在最后一行后,追加$var1的内容 # 法一:有变量的地方用双引号 sed -ri '$a'"$var1" /file # 法二:不用引号 sed -ri $a$var1 /file # 法三:双引号下转义:最后一行$ sed -ri "\$a$var1" /file 6.4.4 操作实例 # 删除文件中的#注释行 sed -ri '/^[ \t]*#/d' /file # 删除文件中的//注释行 sed -ri '\@^[ \t]*//@d' /file # 删除文件中的空行 sed -ri '/^[ \t]*$/d' /file # 删除空行和#注释行 sed -ri '/^[ \t]*$ | ^[ \t]*#/d' /file sed -ri '/^[ \t]*[#|$]/d' /file # 给文件行添加注释 sed -r 's/(.*)/#\1/' /file sed -r 's/.*/#&/' /file # 将行首的0个或多个空格、tab、#,换成一个# sed -r 's/^[ \t#]**/#/' /file 6.5 awk

awk是一种编程语言,它能提供一个类编程环境来修改额重新组织数据。它也是逐行扫描数据

数据可以来自标准注入,文件,管道。

特点:

定义变量来保存数据 使用算术和字符串操作符来处理数据 使用结构化编程概念(选择结构,循环结构)为数据增加处理逻辑 提取数据文件中的数据元素,将其重新排列或格式化,生成格式化报告

gawk程序是Unix中原始awk程序的GNU版本。所有linux发行版都没有默认安装gawk,需要自行安装。它提供了一种编程语言而不只是编辑器命令。

awk命令格式

命令方式:awk [option] 'command' files 脚本方式:awk [option] -f awk_scirpt_file files 6.5.1 awk工作流程

awk 'BEGIN{FS=":"}{print $1,$2}'

awk使用一行作为输入,并将这一行赋给内部变量$0,每一行可称为一个记录,以换行符结束 行被内部变量FS(字段分隔符,默认空格 or tab)分隔成字段,每个字段被依序存储在内部变量$1,$2,...$n,最多可以到100。 在处理时,原命令的逗号被映射为OFS(output FS),打印出$1,$2 一行处理后,换另一行,重复上述动作,直到所有行处理完毕 6.5.2 awk选项 F,定义每行输入数据的字段分割符,默认是空格和tab f,指定awk命令脚本文件 6.5.3 awk内部变量 $0:当前处理行的内容 NR:当前行被处理的次序号(多个文件输入时,会依次递增) FNR:当前行被处理的次序号(多个文件输入时,一旦开始处理下一个文件,次序号会重新归1) NF:当前行一共有多少个字段,$NF是最后一个字段的内容 FS:输入内容的分隔符,默认空格和tab OFS:输出内容的分隔符。默认空格 RS:记录(行)分隔符。默认是换行符 ORS:输出记录(行)的分隔符。默认是换行符 # 一行按照多种分隔符分隔字段,并且多个空格,多个:,多个tab都只算一个分隔符(符合正则语法) awk -F'[ :\t]+' '{print $1,$2,$3}' #这里按照空格,冒号,tab同时分 6.5.4 awk命令

任何awk命令语句都是由模式和动作组成,模式里包括两个特殊模式(BEGIN,END),动作则由花括号括起来的语句

模式用于匹配数据行,而动作则是对匹配行进行操作

BEGIN{} pattern{} END{}

BEGIN模式

发生在读文件之前(读第一行数据之前)

可以不要files参数,而另外两个过程需要files参数

通常用于定义一些变量,例如FS(字段分隔符),OFS(输出字符分隔符)

可以省略该模式

其他pattern模式:处理行数据,其他模式可以是任何条件语句或复合语句或正则,省略则表示匹配所有行

END模式:所有行处理结束之后,可以省略该模式

三个过程之间可以没有分割(空格),一般分割是为了阅读友好

模式之间在书写上也没有次序之分,谁先谁后都一样,按BEGIN,其他,END是为了阅读友好

awk 'BEGIN{print 1/2} {print "ok"} END{print "------"}' /etc/hosts 0.5 # 在读文件之前打印了一个1/2 ok # 在处理每行的时候,输出ok ok ok ok ------ # 在处理完所有行之后,输出------- # 字符串需要用双引号括起来,否则无法打印 # BEGIN定义变量 awk 'BEGIN{FS=":";OFS="-----"} {print $1,$2}' /etc/passwd root-----x bin-----x daemon-----x adm-----x 函数

一般输出和格式化输出

# 将年月换行输出 date | awk '{print "month: " $2 "\nYear: " $NF}' # 只要想原样输出的都应放置在双引号之中 # 定宽输出, awk -F: '{printf "%-15s %-10s %-15s\n",$1,$2,$3}' /etc/passwd # 第一个字段15个字符,第二个字段10个字符 awk -F: '{printf "|%-15s |%-10s |%-15s|\n",$1,$2,$3}' # %s——字符类型,%d——数值类型,%f——浮点类型,-表示左对齐(默认右对齐) # printf默认不会在行尾自动换行,需要自行添加换行符\n或其他 6.5.5 命令模式 # 正则表达式 # 匹配整行(记录) awk '/^root/' /etc/passwd # 把以root开头的行打印出 awk '!/root/' /etc/passwd # 把不是以root开头的行打印出 # 匹配字段,等于不等于(~ !~) awk -F: '$1 ~ /root/' /etc/passwd # 第一个字段内容等于root,就打印 awk -F: '$1 !~ /root/' /etc/passwd # 第一个字段内容不等于root,就打印 # 比较表达式 # 利用关系运算符来比较数字和字符串(字符串需要用双引号括起来) # 关系运算符,< > = == != awk -F: '$3 == 0' /etc/passwd awk -F: '$7 == "/bin/bash"' /etc/passwd awk -F: '$3>300{print $0}' /etc/passwd # 等价于 awk -F: '{if($3>300) print $0}' /etc/passwd # 把比较运算放在行处理里面 awk -F: '{if($3>300) {print $0} }' /etc/passwd awk -F: '{if($3>300) {print $0} else {print $1} }' /etc/passwd # 算术运算,+ - * / % ^指数 awk -F: '$3*10 > 500' /etc/passwd awk -F: '{if($3*10 > 500){print $0}}' /etc/passwd # 逻辑与复合模式 # && 与,|| 或,!非 awk -F: '!($1~/root/ || $3 4 ? "high":"low"$7)}' datafile # 赋值运算 awk '$3=="chris"{$3="chris你好";print $0}' datafile # 短加运算 awk '{$7%=3;print $7}' datafile 6.5.5 命令动作——awk脚本编程

注意:这里只写出动作部分,没有写全命令

程序结构 # 1.条件结构 { if(expression){sentence1; sentence2;} } { if(expression){sentence1; sentence2;} else {sentence1; sentence2;} } { if(expression){sentence1; sentence2;} else if(expression) {sentence1; sentence2;} else {sentence1; sentence2;} } # 统计符合$3字段大于0小于100的行的行数, { if($3>0 && $3var=bash # 法一:command 用双引号,变量用双引号,再用反斜杠转义 echo "unix scripts" | awk "gsub(/unix/,\"$var\")" # 法二:command 用单引号,变量用双引号套单引号"'"$var"'", echo "unix scripts" | awk 'gsub(/unix/,"'"$var"'")' # 法三:command 用单引号,变量用三个单引号,不能在函数中使用 df -h | awk '{if(int($5) > '''$var'''){print $6":"$5}}' # 法四:awk -v echo "unix scripts" | awk -v var="bash" 'gsub(/unix/,var)' 6.5.6 函数 # 一般输出print # 将年月换行输出 date | awk '{print "month: " $2 "\nYear: " $NF}' # 只要想原样输出的都应放置在双引号之中 # 格式化输出printf # 定宽输出, awk -F: '{printf "%-15s %-10s %-15s\n",$1,$2,$3}' /etc/passwd # 第一个字段15个字符,第二个字段10个字符 awk -F: '{printf "|%-15s |%-10s |%-15s|\n",$1,$2,$3}' # %s——字符类型,%d——数值类型,%f——浮点类型,-表示左对齐(默认右对齐) # printf默认不会在行尾自动换行,需要自行添加换行符\n或其他 # 计算变量的长度length awk -F: 'length($1)==4{count++;print $1} END{print "count is "count}' /etc/passwd # 统计用户名长度为4的个数 # 查找替换sub,全量查找替换gsub echo "unix script" | awk 'gsub(/unix/,bash)' # 转整数int,int(20%)=>20 常用

basename:用于打印目录或者文件的基本名称,

basename ./scripts/预定义变量.sh # 打印出 预定义变量.sh

类似的还有dirname,打印出目录或路径的名字

command:调用指定的指令并执行,命令执行时不查询shell函数。command命令只能够执行shell内部的命令。

command [-pVv] command1 [参数 ...]

p,类似于type命令,用来查询,command1是不是可执行的内部命令 #!/usr/bin/bash # 动态执行命令 read -p "input command: " command1 command $command1 # 根据命令的有无,去安装某些软件 read -p "input command: " command2 if command -v $command2 &>/dev/null; then echo "$command2 is already existed” else echo "yum install" # yum -y install fi

冒号(:)的作用

空命令

参数扩展

重定向

当注释使用

whoami

用于显示自身用户名称。 相当于执行"id -un"指令

wait

它等待后台运行的进程完成并返回退出状态。与等待指定时间的sleep 命令不同,该wait命令等待所有(不带任何参数)或特定后台任务完成。

time

time command1 测量一个命令或一个脚本的运行时间

printf



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3